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