1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | #include "TestVisitor.h" |
10 | #include "clang/Basic/Diagnostic.h" |
11 | #include "clang/Tooling/FixIt.h" |
12 | |
13 | using namespace clang; |
14 | |
15 | using tooling::fixit::getText; |
16 | using tooling::fixit::getExtendedText; |
17 | using tooling::fixit::createRemoval; |
18 | using tooling::fixit::createReplacement; |
19 | |
20 | namespace { |
21 | |
22 | struct CallsVisitor : TestVisitor<CallsVisitor> { |
23 | bool VisitCallExpr(CallExpr *Expr) { |
24 | OnCall(Expr, Context); |
25 | return true; |
26 | } |
27 | |
28 | std::function<void(CallExpr *, ASTContext *Context)> OnCall; |
29 | }; |
30 | |
31 | std::string LocationToString(SourceLocation Loc, ASTContext *Context) { |
32 | return Loc.printToString(Context->getSourceManager()); |
33 | } |
34 | |
35 | TEST(FixItTest, getText) { |
36 | CallsVisitor Visitor; |
37 | |
38 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
39 | EXPECT_EQ("foo(x, y)", getText(*CE, *Context)); |
40 | EXPECT_EQ("foo(x, y)", getText(CE->getSourceRange(), *Context)); |
41 | |
42 | Expr *P0 = CE->getArg(0); |
43 | Expr *P1 = CE->getArg(1); |
44 | EXPECT_EQ("x", getText(*P0, *Context)); |
45 | EXPECT_EQ("y", getText(*P1, *Context)); |
46 | }; |
47 | Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); |
48 | |
49 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
50 | EXPECT_EQ("APPLY(foo, x, y)", getText(*CE, *Context)); |
51 | }; |
52 | Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n" |
53 | "void foo(int x, int y) { APPLY(foo, x, y); }"); |
54 | } |
55 | |
56 | TEST(FixItTest, getTextWithMacro) { |
57 | CallsVisitor Visitor; |
58 | |
59 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
60 | EXPECT_EQ("F OO", getText(*CE, *Context)); |
61 | Expr *P0 = CE->getArg(0); |
62 | Expr *P1 = CE->getArg(1); |
63 | EXPECT_EQ("", getText(*P0, *Context)); |
64 | EXPECT_EQ("", getText(*P1, *Context)); |
65 | }; |
66 | Visitor.runOver("#define F foo(\n" |
67 | "#define OO x, y)\n" |
68 | "void foo(int x, int y) { F OO ; }"); |
69 | |
70 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
71 | EXPECT_EQ("", getText(*CE, *Context)); |
72 | Expr *P0 = CE->getArg(0); |
73 | Expr *P1 = CE->getArg(1); |
74 | EXPECT_EQ("x", getText(*P0, *Context)); |
75 | EXPECT_EQ("y", getText(*P1, *Context)); |
76 | }; |
77 | Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n" |
78 | "void foo(int x, int y) { FOO(x,y) }"); |
79 | } |
80 | |
81 | TEST(FixItTest, getExtendedText) { |
82 | CallsVisitor Visitor; |
83 | |
84 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
85 | EXPECT_EQ("foo(x, y);", |
86 | getExtendedText(*CE, tok::TokenKind::semi, *Context)); |
87 | |
88 | Expr *P0 = CE->getArg(0); |
89 | Expr *P1 = CE->getArg(1); |
90 | EXPECT_EQ("x", getExtendedText(*P0, tok::TokenKind::semi, *Context)); |
91 | EXPECT_EQ("x,", getExtendedText(*P0, tok::TokenKind::comma, *Context)); |
92 | EXPECT_EQ("y", getExtendedText(*P1, tok::TokenKind::semi, *Context)); |
93 | }; |
94 | Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); |
95 | Visitor.runOver("void foo(int x, int y) { if (true) foo(x, y); }"); |
96 | Visitor.runOver("int foo(int x, int y) { if (true) return 3 + foo(x, y); }"); |
97 | Visitor.runOver("void foo(int x, int y) { for (foo(x, y);;) ++x; }"); |
98 | Visitor.runOver( |
99 | "bool foo(int x, int y) { for (;foo(x, y);) x = 1; return true; }"); |
100 | |
101 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
102 | EXPECT_EQ("foo()", getExtendedText(*CE, tok::TokenKind::semi, *Context)); |
103 | }; |
104 | Visitor.runOver("bool foo() { if (foo()) return true; return false; }"); |
105 | Visitor.runOver("void foo() { int x; for (;; foo()) ++x; }"); |
106 | Visitor.runOver("int foo() { return foo() + 3; }"); |
107 | } |
108 | |
109 | TEST(FixItTest, createRemoval) { |
110 | CallsVisitor Visitor; |
111 | |
112 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
113 | FixItHint Hint = createRemoval(*CE); |
114 | EXPECT_EQ("foo(x, y)", getText(Hint.RemoveRange.getAsRange(), *Context)); |
115 | EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); |
116 | EXPECT_TRUE(Hint.CodeToInsert.empty()); |
117 | |
118 | Expr *P0 = CE->getArg(0); |
119 | FixItHint Hint0 = createRemoval(*P0); |
120 | EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), *Context)); |
121 | EXPECT_TRUE(Hint0.InsertFromRange.isInvalid()); |
122 | EXPECT_TRUE(Hint0.CodeToInsert.empty()); |
123 | |
124 | Expr *P1 = CE->getArg(1); |
125 | FixItHint Hint1 = createRemoval(*P1); |
126 | EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), *Context)); |
127 | EXPECT_TRUE(Hint1.InsertFromRange.isInvalid()); |
128 | EXPECT_TRUE(Hint1.CodeToInsert.empty()); |
129 | }; |
130 | Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); |
131 | |
132 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
133 | Expr *P0 = CE->getArg(0); |
134 | FixItHint Hint0 = createRemoval(*P0); |
135 | EXPECT_EQ("x + y", getText(Hint0.RemoveRange.getAsRange(), *Context)); |
136 | |
137 | Expr *P1 = CE->getArg(1); |
138 | FixItHint Hint1 = createRemoval(*P1); |
139 | EXPECT_EQ("y + x", getText(Hint1.RemoveRange.getAsRange(), *Context)); |
140 | }; |
141 | Visitor.runOver("void foo(int x, int y) { foo(x + y, y + x); }"); |
142 | } |
143 | |
144 | TEST(FixItTest, createRemovalWithMacro) { |
145 | CallsVisitor Visitor; |
146 | |
147 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
148 | FixItHint Hint = createRemoval(*CE); |
149 | EXPECT_EQ("FOO", getText(Hint.RemoveRange.getAsRange(), *Context)); |
150 | EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); |
151 | EXPECT_TRUE(Hint.CodeToInsert.empty()); |
152 | |
153 | Expr *P0 = CE->getArg(0); |
154 | FixItHint Hint0 = createRemoval(*P0); |
155 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>", |
156 | LocationToString(Hint0.RemoveRange.getBegin(), Context)); |
157 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>", |
158 | LocationToString(Hint0.RemoveRange.getEnd(), Context)); |
159 | EXPECT_TRUE(Hint0.InsertFromRange.isInvalid()); |
160 | EXPECT_TRUE(Hint0.CodeToInsert.empty()); |
161 | |
162 | Expr *P1 = CE->getArg(1); |
163 | FixItHint Hint1 = createRemoval(*P1); |
164 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:20>", |
165 | LocationToString(Hint1.RemoveRange.getBegin(), Context)); |
166 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:20>", |
167 | LocationToString(Hint1.RemoveRange.getEnd(), Context)); |
168 | EXPECT_TRUE(Hint1.InsertFromRange.isInvalid()); |
169 | EXPECT_TRUE(Hint1.CodeToInsert.empty()); |
170 | }; |
171 | Visitor.runOver("#define FOO foo(1, 1)\n" |
172 | "void foo(int x, int y) { FOO; }"); |
173 | |
174 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
175 | FixItHint Hint = createRemoval(*CE); |
176 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:37>", |
177 | LocationToString(Hint.RemoveRange.getBegin(), Context)); |
178 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:45>", |
179 | LocationToString(Hint.RemoveRange.getEnd(), Context)); |
180 | EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); |
181 | EXPECT_TRUE(Hint.CodeToInsert.empty()); |
182 | }; |
183 | Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n" |
184 | "void foo(int x, int y) { FOO(x,y) }"); |
185 | } |
186 | |
187 | TEST(FixItTest, createReplacement) { |
188 | CallsVisitor Visitor; |
189 | |
190 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
191 | Expr *P0 = CE->getArg(0); |
192 | Expr *P1 = CE->getArg(1); |
193 | FixItHint Hint0 = createReplacement(*P0, *P1, *Context); |
194 | FixItHint Hint1 = createReplacement(*P1, *P0, *Context); |
195 | |
196 | |
197 | EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), *Context)); |
198 | EXPECT_TRUE(Hint0.InsertFromRange.isInvalid()); |
199 | EXPECT_EQ(Hint0.CodeToInsert, "y"); |
200 | |
201 | |
202 | EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), *Context)); |
203 | EXPECT_TRUE(Hint1.InsertFromRange.isInvalid()); |
204 | EXPECT_EQ(Hint1.CodeToInsert, "x"); |
205 | }; |
206 | |
207 | Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); |
208 | |
209 | Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n" |
210 | "void foo(int x, int y) { APPLY(foo, x, y); }"); |
211 | |
212 | Visitor.runOver("#define APPLY(f, P) f(P)\n" |
213 | "#define PAIR(x, y) x, y\n" |
214 | "void foo(int x, int y) { APPLY(foo, PAIR(x, y)); }\n"); |
215 | } |
216 | |
217 | TEST(FixItTest, createReplacementWithMacro) { |
218 | CallsVisitor Visitor; |
219 | |
220 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
221 | Expr *P0 = CE->getArg(0); |
222 | Expr *P1 = CE->getArg(1); |
223 | FixItHint Hint = createReplacement(*P0, *P1, *Context); |
224 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>", |
225 | LocationToString(Hint.RemoveRange.getBegin(), Context)); |
226 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:1:17>", |
227 | LocationToString(Hint.RemoveRange.getEnd(), Context)); |
228 | EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); |
229 | EXPECT_TRUE(Hint.CodeToInsert.empty()); |
230 | }; |
231 | |
232 | Visitor.runOver("#define FOO foo(1, 1)\n" |
233 | "void foo(int x, int y) { FOO; }"); |
234 | |
235 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
236 | Expr *P0 = CE->getArg(0); |
237 | Expr *P1 = CE->getArg(1); |
238 | FixItHint Hint = createReplacement(*P0, *P1, *Context); |
239 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:2:30>", |
240 | LocationToString(Hint.RemoveRange.getBegin(), Context)); |
241 | EXPECT_EQ("input.cc:2:26 <Spelling=input.cc:2:30>", |
242 | LocationToString(Hint.RemoveRange.getEnd(), Context)); |
243 | EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); |
244 | EXPECT_EQ("y", Hint.CodeToInsert); |
245 | }; |
246 | Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n" |
247 | "void foo(int x, int y) { FOO(x,y) }"); |
248 | |
249 | Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { |
250 | Expr *P0 = CE->getArg(0); |
251 | Expr *P1 = CE->getArg(1); |
252 | FixItHint Hint = createReplacement(*P0, *P1, *Context); |
253 | EXPECT_EQ("x + y", getText(Hint.RemoveRange.getAsRange(), *Context)); |
254 | EXPECT_TRUE(Hint.InsertFromRange.isInvalid()); |
255 | EXPECT_EQ("y + x", Hint.CodeToInsert); |
256 | }; |
257 | Visitor.runOver("void foo(int x, int y) { foo(x + y, y + x); }"); |
258 | } |
259 | |
260 | } |
261 | |