1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | #include "RewriterTestContext.h" |
10 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
11 | #include "clang/ASTMatchers/ASTMatchers.h" |
12 | #include "clang/Tooling/RefactoringCallbacks.h" |
13 | #include "gtest/gtest.h" |
14 | |
15 | namespace clang { |
16 | namespace tooling { |
17 | |
18 | using namespace ast_matchers; |
19 | |
20 | template <typename T> |
21 | void expectRewritten(const std::string &Code, const std::string &Expected, |
22 | const T &AMatcher, RefactoringCallback &Callback) { |
23 | std::map<std::string, Replacements> FileToReplace; |
24 | ASTMatchRefactorer Finder(FileToReplace); |
25 | Finder.addMatcher(AMatcher, &Callback); |
26 | std::unique_ptr<tooling::FrontendActionFactory> Factory( |
27 | tooling::newFrontendActionFactory(&Finder)); |
28 | ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), Code)) |
29 | << "Parsing error in \"" << Code << "\""; |
30 | RewriterTestContext Context; |
31 | FileID ID = Context.createInMemoryFile("input.cc", Code); |
32 | EXPECT_TRUE(tooling::applyAllReplacements(FileToReplace["input.cc"], |
33 | Context.Rewrite)); |
34 | EXPECT_EQ(Expected, Context.getRewrittenText(ID)); |
35 | } |
36 | |
37 | TEST(RefactoringCallbacksTest, ReplacesStmtsWithString) { |
38 | std::string Code = "void f() { int i = 1; }"; |
39 | std::string Expected = "void f() { ; }"; |
40 | ReplaceStmtWithText Callback("id", ";"); |
41 | expectRewritten(Code, Expected, id("id", declStmt()), Callback); |
42 | } |
43 | |
44 | TEST(RefactoringCallbacksTest, ReplacesStmtsInCalledMacros) { |
45 | std::string Code = "#define A void f() { int i = 1; }\nA"; |
46 | std::string Expected = "#define A void f() { ; }\nA"; |
47 | ReplaceStmtWithText Callback("id", ";"); |
48 | expectRewritten(Code, Expected, id("id", declStmt()), Callback); |
49 | } |
50 | |
51 | TEST(RefactoringCallbacksTest, IgnoresStmtsInUncalledMacros) { |
52 | std::string Code = "#define A void f() { int i = 1; }"; |
53 | std::string Expected = "#define A void f() { int i = 1; }"; |
54 | ReplaceStmtWithText Callback("id", ";"); |
55 | expectRewritten(Code, Expected, id("id", declStmt()), Callback); |
56 | } |
57 | |
58 | TEST(RefactoringCallbacksTest, ReplacesInteger) { |
59 | std::string Code = "void f() { int i = 1; }"; |
60 | std::string Expected = "void f() { int i = 2; }"; |
61 | ReplaceStmtWithText Callback("id", "2"); |
62 | expectRewritten(Code, Expected, id("id", expr(integerLiteral())), Callback); |
63 | } |
64 | |
65 | TEST(RefactoringCallbacksTest, ReplacesStmtWithStmt) { |
66 | std::string Code = "void f() { int i = false ? 1 : i * 2; }"; |
67 | std::string Expected = "void f() { int i = i * 2; }"; |
68 | ReplaceStmtWithStmt Callback("always-false", "should-be"); |
69 | expectRewritten( |
70 | Code, Expected, |
71 | id("always-false", |
72 | conditionalOperator(hasCondition(cxxBoolLiteral(equals(false))), |
73 | hasFalseExpression(id("should-be", expr())))), |
74 | Callback); |
75 | } |
76 | |
77 | TEST(RefactoringCallbacksTest, ReplacesIfStmt) { |
78 | std::string Code = "bool a; void f() { if (a) f(); else a = true; }"; |
79 | std::string Expected = "bool a; void f() { f(); }"; |
80 | ReplaceIfStmtWithItsBody Callback("id", true); |
81 | expectRewritten( |
82 | Code, Expected, |
83 | id("id", ifStmt(hasCondition(implicitCastExpr(hasSourceExpression( |
84 | declRefExpr(to(varDecl(hasName("a"))))))))), |
85 | Callback); |
86 | } |
87 | |
88 | TEST(RefactoringCallbacksTest, RemovesEntireIfOnEmptyElse) { |
89 | std::string Code = "void f() { if (false) int i = 0; }"; |
90 | std::string Expected = "void f() { }"; |
91 | ReplaceIfStmtWithItsBody Callback("id", false); |
92 | expectRewritten(Code, Expected, |
93 | id("id", ifStmt(hasCondition(cxxBoolLiteral(equals(false))))), |
94 | Callback); |
95 | } |
96 | |
97 | TEST(RefactoringCallbacksTest, TemplateJustText) { |
98 | std::string Code = "void f() { int i = 1; }"; |
99 | std::string Expected = "void f() { FOO }"; |
100 | auto Callback = ReplaceNodeWithTemplate::create("id", "FOO"); |
101 | EXPECT_FALSE(Callback.takeError()); |
102 | expectRewritten(Code, Expected, id("id", declStmt()), **Callback); |
103 | } |
104 | |
105 | TEST(RefactoringCallbacksTest, TemplateSimpleSubst) { |
106 | std::string Code = "void f() { int i = 1; }"; |
107 | std::string Expected = "void f() { long x = 1; }"; |
108 | auto Callback = ReplaceNodeWithTemplate::create("decl", "long x = ${init}"); |
109 | EXPECT_FALSE(Callback.takeError()); |
110 | expectRewritten(Code, Expected, |
111 | id("decl", varDecl(hasInitializer(id("init", expr())))), |
112 | **Callback); |
113 | } |
114 | |
115 | TEST(RefactoringCallbacksTest, TemplateLiteral) { |
116 | std::string Code = "void f() { int i = 1; }"; |
117 | std::string Expected = "void f() { string x = \"$-1\"; }"; |
118 | auto Callback = ReplaceNodeWithTemplate::create("decl", |
119 | "string x = \"$$-${init}\""); |
120 | EXPECT_FALSE(Callback.takeError()); |
121 | expectRewritten(Code, Expected, |
122 | id("decl", varDecl(hasInitializer(id("init", expr())))), |
123 | **Callback); |
124 | } |
125 | |
126 | static void ExpectStringError(const std::string &Expected, |
127 | llvm::Error E) { |
128 | std::string Found; |
129 | handleAllErrors(std::move(E), [&](const llvm::StringError &SE) { |
130 | llvm::raw_string_ostream Stream(Found); |
131 | SE.log(Stream); |
132 | }); |
133 | EXPECT_EQ(Expected, Found); |
134 | } |
135 | |
136 | TEST(RefactoringCallbacksTest, TemplateUnterminated) { |
137 | auto Callback = ReplaceNodeWithTemplate::create("decl", |
138 | "string x = \"$$-${init\""); |
139 | ExpectStringError("Unterminated ${...} in replacement template near ${init\"", |
140 | Callback.takeError()); |
141 | } |
142 | |
143 | TEST(RefactoringCallbacksTest, TemplateUnknownDollar) { |
144 | auto Callback = ReplaceNodeWithTemplate::create("decl", |
145 | "string x = \"$<"); |
146 | ExpectStringError("Invalid $ in replacement template near $<", |
147 | Callback.takeError()); |
148 | } |
149 | |
150 | |
151 | } |
152 | } |
153 | |