blob: ee05b74c1a20eca62f2f15b0a7e199a9b34c5fcb [file] [log] [blame]
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +00001//===- unittest/Tooling/TransformerTest.cpp -------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
Yitzhak Mandelbaumfbdf8352019-10-10 02:34:47 +00009#include "clang/Tooling/Transformer/Transformer.h"
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000010#include "clang/ASTMatchers/ASTMatchers.h"
Yitzhak Mandelbaumfbdf8352019-10-10 02:34:47 +000011#include "clang/Tooling/Transformer/RangeSelector.h"
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000012#include "clang/Tooling/Tooling.h"
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +000013#include "llvm/Support/Errc.h"
14#include "llvm/Support/Error.h"
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000015#include "gmock/gmock.h"
16#include "gtest/gtest.h"
17
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +000018using namespace clang;
19using namespace tooling;
20using namespace ast_matchers;
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000021namespace {
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +000022using ::testing::IsEmpty;
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +000023using transformer::RewriteRule;
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +000024
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000025constexpr char KHeaderContents[] = R"cc(
26 struct string {
27 string(const char*);
28 char* c_str();
29 int size();
30 };
31 int strlen(const char*);
32
33 namespace proto {
34 struct PCFProto {
35 int foo();
36 };
37 struct ProtoCommandLineFlag : PCFProto {
38 PCFProto& GetProto();
39 };
40 } // namespace proto
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +000041 class Logger {};
42 void operator<<(Logger& l, string msg);
43 Logger& log(int level);
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000044)cc";
45
46static ast_matchers::internal::Matcher<clang::QualType>
47isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
48 return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
49}
50
51static std::string format(StringRef Code) {
52 const std::vector<Range> Ranges(1, Range(0, Code.size()));
53 auto Style = format::getLLVMStyle();
54 const auto Replacements = format::reformat(Style, Code, Ranges);
55 auto Formatted = applyAllReplacements(Code, Replacements);
56 if (!Formatted) {
57 ADD_FAILURE() << "Could not format code: "
58 << llvm::toString(Formatted.takeError());
59 return std::string();
60 }
61 return *Formatted;
62}
63
64static void compareSnippets(StringRef Expected,
65 const llvm::Optional<std::string> &MaybeActual) {
66 ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
67 auto Actual = *MaybeActual;
68 std::string HL = "#include \"header.h\"\n";
69 auto I = Actual.find(HL);
70 if (I != std::string::npos)
71 Actual.erase(I, HL.size());
72 EXPECT_EQ(format(Expected), format(Actual));
73}
74
75// FIXME: consider separating this class into its own file(s).
76class ClangRefactoringTestBase : public testing::Test {
77protected:
78 void appendToHeader(StringRef S) { FileContents[0].second += S; }
79
80 void addFile(StringRef Filename, StringRef Content) {
81 FileContents.emplace_back(Filename, Content);
82 }
83
84 llvm::Optional<std::string> rewrite(StringRef Input) {
85 std::string Code = ("#include \"header.h\"\n" + Input).str();
86 auto Factory = newFrontendActionFactory(&MatchFinder);
87 if (!runToolOnCodeWithArgs(
88 Factory->create(), Code, std::vector<std::string>(), "input.cc",
89 "clang-tool", std::make_shared<PCHContainerOperations>(),
90 FileContents)) {
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +000091 llvm::errs() << "Running tool failed.\n";
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000092 return None;
93 }
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +000094 if (ErrorCount != 0) {
95 llvm::errs() << "Generating changes failed.\n";
96 return None;
97 }
98 auto ChangedCode =
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +000099 applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000100 if (!ChangedCode) {
101 llvm::errs() << "Applying changes failed: "
102 << llvm::toString(ChangedCode.takeError()) << "\n";
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000103 return None;
104 }
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000105 return *ChangedCode;
106 }
107
108 Transformer::ChangeConsumer consumer() {
109 return [this](Expected<AtomicChange> C) {
110 if (C) {
111 Changes.push_back(std::move(*C));
112 } else {
113 consumeError(C.takeError());
114 ++ErrorCount;
115 }
116 };
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000117 }
118
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000119 template <typename R>
120 void testRule(R Rule, StringRef Input, StringRef Expected) {
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000121 Transformer T(std::move(Rule), consumer());
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000122 T.registerMatchers(&MatchFinder);
123 compareSnippets(Expected, rewrite(Input));
124 }
125
126 clang::ast_matchers::MatchFinder MatchFinder;
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000127 // Records whether any errors occurred in individual changes.
128 int ErrorCount = 0;
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000129 AtomicChanges Changes;
130
131private:
132 FileContentMappings FileContents = {{"header.h", ""}};
133};
134
135class TransformerTest : public ClangRefactoringTestBase {
136protected:
137 TransformerTest() { appendToHeader(KHeaderContents); }
138};
139
Yitzhak Mandelbaum3f1ab732019-07-18 17:44:54 +0000140// Given string s, change strlen($s.c_str()) to REPLACED.
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000141static RewriteRule ruleStrlenSize() {
142 StringRef StringExpr = "strexpr";
143 auto StringType = namedDecl(hasAnyName("::basic_string", "::string"));
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000144 auto R = makeRule(
145 callExpr(callee(functionDecl(hasName("strlen"))),
146 hasArgument(0, cxxMemberCallExpr(
147 on(expr(hasType(isOrPointsTo(StringType)))
148 .bind(StringExpr)),
149 callee(cxxMethodDecl(hasName("c_str")))))),
Yitzhak Mandelbaumfab72052019-05-24 15:11:45 +0000150 change(text("REPLACED")), text("Use size() method directly on string."));
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000151 return R;
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000152}
153
154TEST_F(TransformerTest, StrlenSize) {
155 std::string Input = "int f(string s) { return strlen(s.c_str()); }";
156 std::string Expected = "int f(string s) { return REPLACED; }";
157 testRule(ruleStrlenSize(), Input, Expected);
158}
159
160// Tests that no change is applied when a match is not expected.
161TEST_F(TransformerTest, NoMatch) {
162 std::string Input = "int f(string s) { return s.size(); }";
163 testRule(ruleStrlenSize(), Input, Input);
164}
165
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000166// Tests replacing an expression.
167TEST_F(TransformerTest, Flag) {
168 StringRef Flag = "flag";
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000169 RewriteRule Rule = makeRule(
170 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
171 hasName("proto::ProtoCommandLineFlag"))))
172 .bind(Flag)),
173 unless(callee(cxxMethodDecl(hasName("GetProto"))))),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000174 change(node(Flag), text("EXPR")));
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000175
176 std::string Input = R"cc(
177 proto::ProtoCommandLineFlag flag;
178 int x = flag.foo();
179 int y = flag.GetProto().foo();
180 )cc";
181 std::string Expected = R"cc(
182 proto::ProtoCommandLineFlag flag;
183 int x = EXPR.foo();
184 int y = flag.GetProto().foo();
185 )cc";
186
187 testRule(std::move(Rule), Input, Expected);
188}
189
Yitzhak Mandelbaum727bdcb2019-07-02 13:11:04 +0000190TEST_F(TransformerTest, AddIncludeQuoted) {
191 RewriteRule Rule = makeRule(callExpr(callee(functionDecl(hasName("f")))),
192 change(text("other()")));
193 addInclude(Rule, "clang/OtherLib.h");
194
195 std::string Input = R"cc(
196 int f(int x);
197 int h(int x) { return f(x); }
198 )cc";
199 std::string Expected = R"cc(#include "clang/OtherLib.h"
200
201 int f(int x);
202 int h(int x) { return other(); }
203 )cc";
204
205 testRule(Rule, Input, Expected);
206}
207
208TEST_F(TransformerTest, AddIncludeAngled) {
209 RewriteRule Rule = makeRule(callExpr(callee(functionDecl(hasName("f")))),
210 change(text("other()")));
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000211 addInclude(Rule, "clang/OtherLib.h", transformer::IncludeFormat::Angled);
Yitzhak Mandelbaum727bdcb2019-07-02 13:11:04 +0000212
213 std::string Input = R"cc(
214 int f(int x);
215 int h(int x) { return f(x); }
216 )cc";
217 std::string Expected = R"cc(#include <clang/OtherLib.h>
218
219 int f(int x);
220 int h(int x) { return other(); }
221 )cc";
222
223 testRule(Rule, Input, Expected);
224}
225
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000226TEST_F(TransformerTest, NodePartNameNamedDecl) {
227 StringRef Fun = "fun";
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000228 RewriteRule Rule = makeRule(functionDecl(hasName("bad")).bind(Fun),
229 change(name(Fun), text("good")));
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000230
231 std::string Input = R"cc(
232 int bad(int x);
233 int bad(int x) { return x * x; }
234 )cc";
235 std::string Expected = R"cc(
236 int good(int x);
237 int good(int x) { return x * x; }
238 )cc";
239
240 testRule(Rule, Input, Expected);
241}
242
243TEST_F(TransformerTest, NodePartNameDeclRef) {
244 std::string Input = R"cc(
245 template <typename T>
246 T bad(T x) {
247 return x;
248 }
249 int neutral(int x) { return bad<int>(x) * x; }
250 )cc";
251 std::string Expected = R"cc(
252 template <typename T>
253 T bad(T x) {
254 return x;
255 }
256 int neutral(int x) { return good<int>(x) * x; }
257 )cc";
258
259 StringRef Ref = "ref";
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000260 testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000261 change(name(Ref), text("good"))),
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000262 Input, Expected);
263}
264
265TEST_F(TransformerTest, NodePartNameDeclRefFailure) {
266 std::string Input = R"cc(
267 struct Y {
268 int operator*();
269 };
270 int neutral(int x) {
271 Y y;
272 int (Y::*ptr)() = &Y::operator*;
273 return *y + x;
274 }
275 )cc";
276
277 StringRef Ref = "ref";
Yitzhak Mandelbauma5dadbe2019-04-30 17:24:36 +0000278 Transformer T(makeRule(declRefExpr(to(functionDecl())).bind(Ref),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000279 change(name(Ref), text("good"))),
Yitzhak Mandelbauma5dadbe2019-04-30 17:24:36 +0000280 consumer());
281 T.registerMatchers(&MatchFinder);
282 EXPECT_FALSE(rewrite(Input));
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000283}
284
285TEST_F(TransformerTest, NodePartMember) {
286 StringRef E = "expr";
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000287 RewriteRule Rule = makeRule(memberExpr(member(hasName("bad"))).bind(E),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000288 change(member(E), text("good")));
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000289
290 std::string Input = R"cc(
291 struct S {
292 int bad;
293 };
294 int g() {
295 S s;
296 return s.bad;
297 }
298 )cc";
299 std::string Expected = R"cc(
300 struct S {
301 int bad;
302 };
303 int g() {
304 S s;
305 return s.good;
306 }
307 )cc";
308
309 testRule(Rule, Input, Expected);
310}
311
312TEST_F(TransformerTest, NodePartMemberQualified) {
313 std::string Input = R"cc(
314 struct S {
315 int bad;
316 int good;
317 };
318 struct T : public S {
319 int bad;
320 };
321 int g() {
322 T t;
323 return t.S::bad;
324 }
325 )cc";
326 std::string Expected = R"cc(
327 struct S {
328 int bad;
329 int good;
330 };
331 struct T : public S {
332 int bad;
333 };
334 int g() {
335 T t;
336 return t.S::good;
337 }
338 )cc";
339
340 StringRef E = "expr";
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000341 testRule(makeRule(memberExpr().bind(E), change(member(E), text("good"))),
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000342 Input, Expected);
343}
344
345TEST_F(TransformerTest, NodePartMemberMultiToken) {
346 std::string Input = R"cc(
347 struct Y {
348 int operator*();
349 int good();
350 template <typename T> void foo(T t);
351 };
352 int neutral(int x) {
353 Y y;
354 y.template foo<int>(3);
355 return y.operator *();
356 }
357 )cc";
358 std::string Expected = R"cc(
359 struct Y {
360 int operator*();
361 int good();
362 template <typename T> void foo(T t);
363 };
364 int neutral(int x) {
365 Y y;
366 y.template good<int>(3);
367 return y.good();
368 }
369 )cc";
370
371 StringRef MemExpr = "member";
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000372 testRule(makeRule(memberExpr().bind(MemExpr),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000373 change(member(MemExpr), text("good"))),
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000374 Input, Expected);
375}
376
Yitzhak Mandelbaum2e4a6282019-06-06 14:20:29 +0000377TEST_F(TransformerTest, InsertBeforeEdit) {
378 std::string Input = R"cc(
379 int f() {
380 return 7;
381 }
382 )cc";
383 std::string Expected = R"cc(
384 int f() {
385 int y = 3;
386 return 7;
387 }
388 )cc";
389
390 StringRef Ret = "return";
391 testRule(makeRule(returnStmt().bind(Ret),
392 insertBefore(statement(Ret), text("int y = 3;"))),
393 Input, Expected);
394}
395
396TEST_F(TransformerTest, InsertAfterEdit) {
397 std::string Input = R"cc(
398 int f() {
399 int x = 5;
400 return 7;
401 }
402 )cc";
403 std::string Expected = R"cc(
404 int f() {
405 int x = 5;
406 int y = 3;
407 return 7;
408 }
409 )cc";
410
411 StringRef Decl = "decl";
412 testRule(makeRule(declStmt().bind(Decl),
413 insertAfter(statement(Decl), text("int y = 3;"))),
414 Input, Expected);
415}
416
417TEST_F(TransformerTest, RemoveEdit) {
418 std::string Input = R"cc(
419 int f() {
420 int x = 5;
421 return 7;
422 }
423 )cc";
424 std::string Expected = R"cc(
425 int f() {
426 return 7;
427 }
428 )cc";
429
430 StringRef Decl = "decl";
431 testRule(makeRule(declStmt().bind(Decl), remove(statement(Decl))), Input,
432 Expected);
433}
434
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000435TEST_F(TransformerTest, MultiChange) {
436 std::string Input = R"cc(
437 void foo() {
438 if (10 > 1.0)
439 log(1) << "oh no!";
440 else
441 log(0) << "ok";
442 }
443 )cc";
444 std::string Expected = R"(
445 void foo() {
446 if (true) { /* then */ }
447 else { /* else */ }
448 }
449 )";
450
451 StringRef C = "C", T = "T", E = "E";
452 testRule(makeRule(ifStmt(hasCondition(expr().bind(C)),
453 hasThen(stmt().bind(T)), hasElse(stmt().bind(E))),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000454 {change(node(C), text("true")),
455 change(statement(T), text("{ /* then */ }")),
456 change(statement(E), text("{ /* else */ }"))}),
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000457 Input, Expected);
458}
459
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000460TEST_F(TransformerTest, OrderedRuleUnrelated) {
461 StringRef Flag = "flag";
462 RewriteRule FlagRule = makeRule(
463 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
464 hasName("proto::ProtoCommandLineFlag"))))
465 .bind(Flag)),
466 unless(callee(cxxMethodDecl(hasName("GetProto"))))),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000467 change(node(Flag), text("PROTO")));
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000468
469 std::string Input = R"cc(
470 proto::ProtoCommandLineFlag flag;
471 int x = flag.foo();
472 int y = flag.GetProto().foo();
473 int f(string s) { return strlen(s.c_str()); }
474 )cc";
475 std::string Expected = R"cc(
476 proto::ProtoCommandLineFlag flag;
477 int x = PROTO.foo();
478 int y = flag.GetProto().foo();
479 int f(string s) { return REPLACED; }
480 )cc";
481
482 testRule(applyFirst({ruleStrlenSize(), FlagRule}), Input, Expected);
483}
484
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000485TEST_F(TransformerTest, OrderedRuleRelated) {
486 std::string Input = R"cc(
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000487 void f1();
488 void f2();
489 void call_f1() { f1(); }
490 void call_f2() { f2(); }
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000491 )cc";
492 std::string Expected = R"cc(
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000493 void f1();
494 void f2();
495 void call_f1() { REPLACE_F1; }
496 void call_f2() { REPLACE_F1_OR_F2; }
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000497 )cc";
498
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000499 RewriteRule ReplaceF1 =
500 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
501 change(text("REPLACE_F1")));
502 RewriteRule ReplaceF1OrF2 =
503 makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
504 change(text("REPLACE_F1_OR_F2")));
505 testRule(applyFirst({ReplaceF1, ReplaceF1OrF2}), Input, Expected);
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000506}
507
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000508// Change the order of the rules to get a different result. When `ReplaceF1OrF2`
509// comes first, it applies for both uses, so `ReplaceF1` never applies.
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000510TEST_F(TransformerTest, OrderedRuleRelatedSwapped) {
511 std::string Input = R"cc(
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000512 void f1();
513 void f2();
514 void call_f1() { f1(); }
515 void call_f2() { f2(); }
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000516 )cc";
517 std::string Expected = R"cc(
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000518 void f1();
519 void f2();
520 void call_f1() { REPLACE_F1_OR_F2; }
521 void call_f2() { REPLACE_F1_OR_F2; }
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000522 )cc";
523
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000524 RewriteRule ReplaceF1 =
525 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
526 change(text("REPLACE_F1")));
527 RewriteRule ReplaceF1OrF2 =
528 makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
529 change(text("REPLACE_F1_OR_F2")));
530 testRule(applyFirst({ReplaceF1OrF2, ReplaceF1}), Input, Expected);
531}
532
533// Verify that a set of rules whose matchers have different base kinds works
534// properly, including that `applyFirst` produces multiple matchers. We test
535// two different kinds of rules: Expr and Decl. We place the Decl rule in the
536// middle to test that `buildMatchers` works even when the kinds aren't grouped
537// together.
538TEST_F(TransformerTest, OrderedRuleMultipleKinds) {
539 std::string Input = R"cc(
540 void f1();
541 void f2();
542 void call_f1() { f1(); }
543 void call_f2() { f2(); }
544 )cc";
545 std::string Expected = R"cc(
546 void f1();
547 void DECL_RULE();
548 void call_f1() { REPLACE_F1; }
549 void call_f2() { REPLACE_F1_OR_F2; }
550 )cc";
551
552 RewriteRule ReplaceF1 =
553 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
554 change(text("REPLACE_F1")));
555 RewriteRule ReplaceF1OrF2 =
556 makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
557 change(text("REPLACE_F1_OR_F2")));
558 RewriteRule DeclRule = makeRule(functionDecl(hasName("f2")).bind("fun"),
559 change(name("fun"), text("DECL_RULE")));
560
561 RewriteRule Rule = applyFirst({ReplaceF1, DeclRule, ReplaceF1OrF2});
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000562 EXPECT_EQ(transformer::detail::buildMatchers(Rule).size(), 2UL);
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000563 testRule(Rule, Input, Expected);
Yitzhak Mandelbaum8369a9b2019-05-17 14:23:33 +0000564}
565
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000566//
567// Negative tests (where we expect no transformation to occur).
568//
569
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000570// Tests for a conflict in edits from a single match for a rule.
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000571TEST_F(TransformerTest, TextGeneratorFailure) {
572 std::string Input = "int conflictOneRule() { return 3 + 7; }";
573 // Try to change the whole binary-operator expression AND one its operands:
574 StringRef O = "O";
575 auto AlwaysFail = [](const ast_matchers::MatchFinder::MatchResult &)
576 -> llvm::Expected<std::string> {
577 return llvm::createStringError(llvm::errc::invalid_argument, "ERROR");
578 };
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000579 Transformer T(makeRule(binaryOperator().bind(O), change(node(O), AlwaysFail)),
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000580 consumer());
581 T.registerMatchers(&MatchFinder);
582 EXPECT_FALSE(rewrite(Input));
583 EXPECT_THAT(Changes, IsEmpty());
584 EXPECT_EQ(ErrorCount, 1);
585}
586
587// Tests for a conflict in edits from a single match for a rule.
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000588TEST_F(TransformerTest, OverlappingEditsInRule) {
589 std::string Input = "int conflictOneRule() { return 3 + 7; }";
590 // Try to change the whole binary-operator expression AND one its operands:
591 StringRef O = "O", L = "L";
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000592 Transformer T(makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O),
593 {change(node(O), text("DELETE_OP")),
594 change(node(L), text("DELETE_LHS"))}),
595 consumer());
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000596 T.registerMatchers(&MatchFinder);
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000597 EXPECT_FALSE(rewrite(Input));
598 EXPECT_THAT(Changes, IsEmpty());
599 EXPECT_EQ(ErrorCount, 1);
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000600}
601
602// Tests for a conflict in edits across multiple matches (of the same rule).
603TEST_F(TransformerTest, OverlappingEditsMultipleMatches) {
604 std::string Input = "int conflictOneRule() { return -7; }";
605 // Try to change the whole binary-operator expression AND one its operands:
606 StringRef E = "E";
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000607 Transformer T(makeRule(expr().bind(E), change(node(E), text("DELETE_EXPR"))),
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000608 consumer());
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000609 T.registerMatchers(&MatchFinder);
610 // The rewrite process fails because the changes conflict with each other...
611 EXPECT_FALSE(rewrite(Input));
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000612 // ... but two changes were produced.
613 EXPECT_EQ(Changes.size(), 2u);
614 EXPECT_EQ(ErrorCount, 0);
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000615}
616
617TEST_F(TransformerTest, ErrorOccurredMatchSkipped) {
618 // Syntax error in the function body:
619 std::string Input = "void errorOccurred() { 3 }";
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000620 Transformer T(makeRule(functionDecl(hasName("errorOccurred")),
Yitzhak Mandelbaum3ec50e22019-05-22 14:48:19 +0000621 change(text("DELETED;"))),
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000622 consumer());
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000623 T.registerMatchers(&MatchFinder);
624 // The rewrite process itself fails...
625 EXPECT_FALSE(rewrite(Input));
Yitzhak Mandelbaumaecc59c2019-04-30 16:48:33 +0000626 // ... and no changes or errors are produced in the process.
627 EXPECT_THAT(Changes, IsEmpty());
628 EXPECT_EQ(ErrorCount, 0);
Yitzhak Mandelbaumfa1552e2019-04-18 17:52:24 +0000629}
630
Yitzhak Mandelbaum3f1ab732019-07-18 17:44:54 +0000631// Transformation of macro source text when the change encompasses the entirety
632// of the expanded text.
633TEST_F(TransformerTest, SimpleMacro) {
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000634 std::string Input = R"cc(
Yitzhak Mandelbaum3f1ab732019-07-18 17:44:54 +0000635#define ZERO 0
636 int f(string s) { return ZERO; }
637 )cc";
638 std::string Expected = R"cc(
639#define ZERO 0
640 int f(string s) { return 999; }
641 )cc";
642
643 StringRef zero = "zero";
644 RewriteRule R = makeRule(integerLiteral(equals(0)).bind(zero),
645 change(node(zero), text("999")));
646 testRule(R, Input, Expected);
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000647}
648
Yitzhak Mandelbaum3f1ab732019-07-18 17:44:54 +0000649// Transformation of macro source text when the change encompasses the entirety
650// of the expanded text, for the case of function-style macros.
651TEST_F(TransformerTest, FunctionMacro) {
652 std::string Input = R"cc(
653#define MACRO(str) strlen((str).c_str())
654 int f(string s) { return MACRO(s); }
655 )cc";
656 std::string Expected = R"cc(
657#define MACRO(str) strlen((str).c_str())
658 int f(string s) { return REPLACED; }
659 )cc";
660
661 testRule(ruleStrlenSize(), Input, Expected);
662}
663
664// Tests that expressions in macro arguments can be rewritten.
665TEST_F(TransformerTest, MacroArg) {
666 std::string Input = R"cc(
667#define PLUS(e) e + 1
668 int f(string s) { return PLUS(strlen(s.c_str())); }
669 )cc";
670 std::string Expected = R"cc(
671#define PLUS(e) e + 1
672 int f(string s) { return PLUS(REPLACED); }
673 )cc";
674
675 testRule(ruleStrlenSize(), Input, Expected);
676}
677
678// Tests that expressions in macro arguments can be rewritten, even when the
679// macro call occurs inside another macro's definition.
680TEST_F(TransformerTest, MacroArgInMacroDef) {
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000681 std::string Input = R"cc(
682#define NESTED(e) e
683#define MACRO(str) NESTED(strlen((str).c_str()))
Yitzhak Mandelbaum3f1ab732019-07-18 17:44:54 +0000684 int f(string s) { return MACRO(s); }
685 )cc";
686 std::string Expected = R"cc(
687#define NESTED(e) e
688#define MACRO(str) NESTED(strlen((str).c_str()))
689 int f(string s) { return REPLACED; }
690 )cc";
691
692 testRule(ruleStrlenSize(), Input, Expected);
693}
694
695// Tests the corner case of the identity macro, specifically that it is
696// discarded in the rewrite rather than preserved (like PLUS is preserved in the
697// previous test). This behavior is of dubious value (and marked with a FIXME
698// in the code), but we test it to verify (and demonstrate) how this case is
699// handled.
700TEST_F(TransformerTest, IdentityMacro) {
701 std::string Input = R"cc(
702#define ID(e) e
703 int f(string s) { return ID(strlen(s.c_str())); }
704 )cc";
705 std::string Expected = R"cc(
706#define ID(e) e
707 int f(string s) { return REPLACED; }
708 )cc";
709
710 testRule(ruleStrlenSize(), Input, Expected);
711}
712
Yitzhak Mandelbaumdb24ef52019-09-27 15:26:04 +0000713// Tests that two changes in a single macro expansion do not lead to conflicts
714// in applying the changes.
715TEST_F(TransformerTest, TwoChangesInOneMacroExpansion) {
716 std::string Input = R"cc(
717#define PLUS(a,b) (a) + (b)
718 int f() { return PLUS(3, 4); }
719 )cc";
720 std::string Expected = R"cc(
721#define PLUS(a,b) (a) + (b)
722 int f() { return PLUS(LIT, LIT); }
723 )cc";
724
725 testRule(makeRule(integerLiteral(), change(text("LIT"))), Input, Expected);
726}
727
728// Tests case where the rule's match spans both source from the macro and its
729// arg, with the begin location (the "anchor") being the arg.
730TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNot) {
731 std::string Input = R"cc(
732#define PLUS_ONE(a) a + 1
733 int f() { return PLUS_ONE(3); }
734 )cc";
735 std::string Expected = R"cc(
736#define PLUS_ONE(a) a + 1
737 int f() { return PLUS_ONE(LIT); }
738 )cc";
739
740 StringRef E = "expr";
741 testRule(makeRule(binaryOperator(hasLHS(expr().bind(E))),
742 change(node(E), text("LIT"))),
743 Input, Expected);
744}
745
746// Tests case where the rule's match spans both source from the macro and its
747// arg, with the begin location (the "anchor") being inside the macro.
748TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNotAnchoredInMacro) {
749 std::string Input = R"cc(
750#define PLUS_ONE(a) 1 + a
751 int f() { return PLUS_ONE(3); }
752 )cc";
753 std::string Expected = R"cc(
754#define PLUS_ONE(a) 1 + a
755 int f() { return PLUS_ONE(LIT); }
756 )cc";
757
758 StringRef E = "expr";
759 testRule(makeRule(binaryOperator(hasRHS(expr().bind(E))),
760 change(node(E), text("LIT"))),
761 Input, Expected);
762}
763
Yitzhak Mandelbaum3f1ab732019-07-18 17:44:54 +0000764// No rewrite is applied when the changed text does not encompass the entirety
765// of the expanded text. That is, the edit would have to be applied to the
766// macro's definition to succeed and editing the expansion point would not
767// suffice.
768TEST_F(TransformerTest, NoPartialRewriteOMacroExpansion) {
769 std::string Input = R"cc(
770#define ZERO_PLUS 0 + 3
771 int f(string s) { return ZERO_PLUS; })cc";
772
773 StringRef zero = "zero";
774 RewriteRule R = makeRule(integerLiteral(equals(0)).bind(zero),
775 change(node(zero), text("0")));
776 testRule(R, Input, Input);
777}
778
779// This test handles the corner case where a macro expands within another macro
780// to matching code, but that code is an argument to the nested macro call. A
781// simple check of isMacroArgExpansion() vs. isMacroBodyExpansion() will get
782// this wrong, and transform the code.
783TEST_F(TransformerTest, NoPartialRewriteOfMacroExpansionForMacroArgs) {
784 std::string Input = R"cc(
785#define NESTED(e) e
786#define MACRO(str) 1 + NESTED(strlen((str).c_str()))
787 int f(string s) { return MACRO(s); }
788 )cc";
789
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000790 testRule(ruleStrlenSize(), Input, Input);
791}
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000792
793#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
794// Verifies that `Type` and `QualType` are not allowed as top-level matchers in
795// rules.
796TEST(TransformerDeathTest, OrderedRuleTypes) {
797 RewriteRule QualTypeRule = makeRule(qualType(), change(text("Q")));
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000798 EXPECT_DEATH(transformer::detail::buildMatchers(QualTypeRule),
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000799 "Matcher must be.*node matcher");
800
801 RewriteRule TypeRule = makeRule(arrayType(), change(text("T")));
Yitzhak Mandelbaum8bb47cd2019-10-16 01:06:46 +0000802 EXPECT_DEATH(transformer::detail::buildMatchers(TypeRule),
Yitzhak Mandelbaum42b957a2019-08-13 12:31:29 +0000803 "Matcher must be.*node matcher");
804}
805#endif
Yitzhak Mandelbaumfdd98782019-04-05 15:14:05 +0000806} // namespace