diff options
author | Yitzhak Mandelbaum <yitzhakm@google.com> | 2020-07-24 13:27:51 +0000 |
---|---|---|
committer | Yitzhak Mandelbaum <yitzhakm@google.com> | 2020-07-24 14:38:17 +0000 |
commit | c332a984aefc6f8a6b44fd3687a5bbce3f8f035c (patch) | |
tree | cd8d5d8e4847fb7944e87a9c4dc1a4eb30c19d22 /clang/unittests/Tooling/TransformerTest.cpp | |
parent | 4ef2e594d5be2e0e6d4446c8082b15466bc7ffcb (diff) | |
download | llvm-c332a984aefc6f8a6b44fd3687a5bbce3f8f035c.zip llvm-c332a984aefc6f8a6b44fd3687a5bbce3f8f035c.tar.gz llvm-c332a984aefc6f8a6b44fd3687a5bbce3f8f035c.tar.bz2 |
[libTooling] Add an `EditGenerator` that applies a rule throughout a bound node.
The new combinator, `rewriteDescendants`, applies a rewrite rule to all
descendants of a specified bound node. That rewrite rule can refer to nodes
bound by the parent, both in the matcher and in the edits.
Reviewed By: gribozavr2
Differential Revision: https://reviews.llvm.org/D84409
Diffstat (limited to 'clang/unittests/Tooling/TransformerTest.cpp')
-rw-r--r-- | clang/unittests/Tooling/TransformerTest.cpp | 118 |
1 files changed, 117 insertions, 1 deletions
diff --git a/clang/unittests/Tooling/TransformerTest.cpp b/clang/unittests/Tooling/TransformerTest.cpp index 1a68eb1..77fd380 100644 --- a/clang/unittests/Tooling/TransformerTest.cpp +++ b/clang/unittests/Tooling/TransformerTest.cpp @@ -114,7 +114,9 @@ protected: if (C) { Changes.push_back(std::move(*C)); } else { - consumeError(C.takeError()); + // FIXME: stash this error rather then printing. + llvm::errs() << "Error generating changes: " + << llvm::toString(C.takeError()) << "\n"; ++ErrorCount; } }; @@ -414,6 +416,120 @@ TEST_F(TransformerTest, ShrinkTo) { Input, Expected); } +// Rewrite various Stmts inside a Decl. +TEST_F(TransformerTest, RewriteDescendantsDeclChangeStmt) { + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + std::string Expected = + "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3"))); + testRule(makeRule(functionDecl(hasName("f")).bind("fun"), + rewriteDescendants("fun", InlineX)), + Input, Expected); +} + +// Rewrite various TypeLocs inside a Decl. +TEST_F(TransformerTest, RewriteDescendantsDeclChangeTypeLoc) { + std::string Input = "int f(int *x) { return *x; }"; + std::string Expected = "char f(char *x) { return *x; }"; + auto IntToChar = makeRule(typeLoc(loc(qualType(isInteger(), builtinType()))), + changeTo(cat("char"))); + testRule(makeRule(functionDecl(hasName("f")).bind("fun"), + rewriteDescendants("fun", IntToChar)), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsStmt) { + // Add an unrelated definition to the header that also has a variable named + // "x", to test that the rewrite is limited to the scope we intend. + appendToHeader(R"cc(int g(int x) { return x; })cc"); + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + std::string Expected = + "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3"))); + testRule(makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))), + rewriteDescendants("body", InlineX)), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsStmtWithAdditionalChange) { + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + std::string Expected = + "int newName(int x) { int y = 3; { int z = 3 * 3; } return 3; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3"))); + testRule( + makeRule( + functionDecl(hasName("f"), hasBody(stmt().bind("body"))).bind("f"), + flatten(changeTo(name("f"), cat("newName")), + rewriteDescendants("body", InlineX))), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsTypeLoc) { + std::string Input = "int f(int *x) { return *x; }"; + std::string Expected = "int f(char *x) { return *x; }"; + auto IntToChar = + makeRule(typeLoc(loc(qualType(isInteger(), builtinType()))).bind("loc"), + changeTo(cat("char"))); + testRule( + makeRule(functionDecl(hasName("f"), + hasParameter(0, varDecl(hasTypeLoc( + typeLoc().bind("parmType"))))), + rewriteDescendants("parmType", IntToChar)), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsReferToParentBinding) { + std::string Input = + "int f(int p) { int y = p; { int z = p * p; } return p; }"; + std::string Expected = + "int f(int p) { int y = 3; { int z = 3 * 3; } return 3; }"; + std::string VarId = "var"; + auto InlineVar = makeRule(declRefExpr(to(varDecl(equalsBoundNode(VarId)))), + changeTo(cat("3"))); + testRule(makeRule(functionDecl(hasName("f"), + hasParameter(0, varDecl().bind(VarId))) + .bind("fun"), + rewriteDescendants("fun", InlineVar)), + Input, Expected); +} + +TEST_F(TransformerTest, RewriteDescendantsUnboundNode) { + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + auto InlineX = + makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3"))); + Transformer T(makeRule(functionDecl(hasName("f")), + rewriteDescendants("UNBOUND", InlineX)), + consumer()); + T.registerMatchers(&MatchFinder); + EXPECT_FALSE(rewrite(Input)); + EXPECT_THAT(Changes, IsEmpty()); + EXPECT_EQ(ErrorCount, 1); +} + +TEST_F(TransformerTest, RewriteDescendantsInvalidNodeType) { + std::string Input = + "int f(int x) { int y = x; { int z = x * x; } return x; }"; + auto IntToChar = + makeRule(qualType(isInteger(), builtinType()), changeTo(cat("char"))); + Transformer T( + makeRule(functionDecl( + hasName("f"), + hasParameter(0, varDecl(hasType(qualType().bind("type"))))), + rewriteDescendants("type", IntToChar)), + consumer()); + T.registerMatchers(&MatchFinder); + EXPECT_FALSE(rewrite(Input)); + EXPECT_THAT(Changes, IsEmpty()); + EXPECT_EQ(ErrorCount, 1); +} + TEST_F(TransformerTest, InsertBeforeEdit) { std::string Input = R"cc( int f() { |