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(std::string(Filename), std::string(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(std::string(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(std::string(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(std::string(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(std::string(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(std::string(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),
345 changeTo(member(std::string(E)), cat("good"))),
346 Input, Expected);
347 }
348
TEST_F(TransformerTest,NodePartMemberMultiToken)349 TEST_F(TransformerTest, NodePartMemberMultiToken) {
350 std::string Input = R"cc(
351 struct Y {
352 int operator*();
353 int good();
354 template <typename T> void foo(T t);
355 };
356 int neutral(int x) {
357 Y y;
358 y.template foo<int>(3);
359 return y.operator *();
360 }
361 )cc";
362 std::string Expected = R"cc(
363 struct Y {
364 int operator*();
365 int good();
366 template <typename T> void foo(T t);
367 };
368 int neutral(int x) {
369 Y y;
370 y.template good<int>(3);
371 return y.good();
372 }
373 )cc";
374
375 StringRef MemExpr = "member";
376 testRule(makeRule(memberExpr().bind(MemExpr),
377 changeTo(member(std::string(MemExpr)), cat("good"))),
378 Input, Expected);
379 }
380
TEST_F(TransformerTest,InsertBeforeEdit)381 TEST_F(TransformerTest, InsertBeforeEdit) {
382 std::string Input = R"cc(
383 int f() {
384 return 7;
385 }
386 )cc";
387 std::string Expected = R"cc(
388 int f() {
389 int y = 3;
390 return 7;
391 }
392 )cc";
393
394 StringRef Ret = "return";
395 testRule(
396 makeRule(returnStmt().bind(Ret),
397 insertBefore(statement(std::string(Ret)), cat("int y = 3;"))),
398 Input, Expected);
399 }
400
TEST_F(TransformerTest,InsertAfterEdit)401 TEST_F(TransformerTest, InsertAfterEdit) {
402 std::string Input = R"cc(
403 int f() {
404 int x = 5;
405 return 7;
406 }
407 )cc";
408 std::string Expected = R"cc(
409 int f() {
410 int x = 5;
411 int y = 3;
412 return 7;
413 }
414 )cc";
415
416 StringRef Decl = "decl";
417 testRule(
418 makeRule(declStmt().bind(Decl),
419 insertAfter(statement(std::string(Decl)), cat("int y = 3;"))),
420 Input, Expected);
421 }
422
TEST_F(TransformerTest,RemoveEdit)423 TEST_F(TransformerTest, RemoveEdit) {
424 std::string Input = R"cc(
425 int f() {
426 int x = 5;
427 return 7;
428 }
429 )cc";
430 std::string Expected = R"cc(
431 int f() {
432 return 7;
433 }
434 )cc";
435
436 StringRef Decl = "decl";
437 testRule(
438 makeRule(declStmt().bind(Decl), remove(statement(std::string(Decl)))),
439 Input, Expected);
440 }
441
TEST_F(TransformerTest,WithMetadata)442 TEST_F(TransformerTest, WithMetadata) {
443 std::string Input = R"cc(
444 int f() {
445 int x = 5;
446 return 7;
447 }
448 )cc";
449
450 Transformer T(
451 makeRule(declStmt().bind("decl"),
452 withMetadata(remove(statement(std::string("decl"))), 17)),
453 consumer());
454 T.registerMatchers(&MatchFinder);
455 auto Factory = newFrontendActionFactory(&MatchFinder);
456 EXPECT_TRUE(runToolOnCodeWithArgs(
457 Factory->create(), Input, std::vector<std::string>(), "input.cc",
458 "clang-tool", std::make_shared<PCHContainerOperations>(), {}));
459 ASSERT_EQ(Changes.size(), 1u);
460 const llvm::Any &Metadata = Changes[0].getMetadata();
461 ASSERT_TRUE(llvm::any_isa<int>(Metadata));
462 EXPECT_THAT(llvm::any_cast<int>(Metadata), 17);
463 }
464
TEST_F(TransformerTest,MultiChange)465 TEST_F(TransformerTest, MultiChange) {
466 std::string Input = R"cc(
467 void foo() {
468 if (10 > 1.0)
469 log(1) << "oh no!";
470 else
471 log(0) << "ok";
472 }
473 )cc";
474 std::string Expected = R"(
475 void foo() {
476 if (true) { /* then */ }
477 else { /* else */ }
478 }
479 )";
480
481 StringRef C = "C", T = "T", E = "E";
482 testRule(
483 makeRule(ifStmt(hasCondition(expr().bind(C)), hasThen(stmt().bind(T)),
484 hasElse(stmt().bind(E))),
485 {changeTo(node(std::string(C)), cat("true")),
486 changeTo(statement(std::string(T)), cat("{ /* then */ }")),
487 changeTo(statement(std::string(E)), cat("{ /* else */ }"))}),
488 Input, Expected);
489 }
490
TEST_F(TransformerTest,OrderedRuleUnrelated)491 TEST_F(TransformerTest, OrderedRuleUnrelated) {
492 StringRef Flag = "flag";
493 RewriteRule FlagRule = makeRule(
494 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
495 hasName("proto::ProtoCommandLineFlag"))))
496 .bind(Flag)),
497 unless(callee(cxxMethodDecl(hasName("GetProto"))))),
498 changeTo(node(std::string(Flag)), cat("PROTO")));
499
500 std::string Input = R"cc(
501 proto::ProtoCommandLineFlag flag;
502 int x = flag.foo();
503 int y = flag.GetProto().foo();
504 int f(string s) { return strlen(s.c_str()); }
505 )cc";
506 std::string Expected = R"cc(
507 proto::ProtoCommandLineFlag flag;
508 int x = PROTO.foo();
509 int y = flag.GetProto().foo();
510 int f(string s) { return REPLACED; }
511 )cc";
512
513 testRule(applyFirst({ruleStrlenSize(), FlagRule}), Input, Expected);
514 }
515
TEST_F(TransformerTest,OrderedRuleRelated)516 TEST_F(TransformerTest, OrderedRuleRelated) {
517 std::string Input = R"cc(
518 void f1();
519 void f2();
520 void call_f1() { f1(); }
521 void call_f2() { f2(); }
522 )cc";
523 std::string Expected = R"cc(
524 void f1();
525 void f2();
526 void call_f1() { REPLACE_F1; }
527 void call_f2() { REPLACE_F1_OR_F2; }
528 )cc";
529
530 RewriteRule ReplaceF1 =
531 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
532 changeTo(cat("REPLACE_F1")));
533 RewriteRule ReplaceF1OrF2 =
534 makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
535 changeTo(cat("REPLACE_F1_OR_F2")));
536 testRule(applyFirst({ReplaceF1, ReplaceF1OrF2}), Input, Expected);
537 }
538
539 // Change the order of the rules to get a different result. When `ReplaceF1OrF2`
540 // comes first, it applies for both uses, so `ReplaceF1` never applies.
TEST_F(TransformerTest,OrderedRuleRelatedSwapped)541 TEST_F(TransformerTest, OrderedRuleRelatedSwapped) {
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 f2();
551 void call_f1() { REPLACE_F1_OR_F2; }
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 testRule(applyFirst({ReplaceF1OrF2, ReplaceF1}), Input, Expected);
562 }
563
564 // Verify that a set of rules whose matchers have different base kinds works
565 // properly, including that `applyFirst` produces multiple matchers. We test
566 // two different kinds of rules: Expr and Decl. We place the Decl rule in the
567 // middle to test that `buildMatchers` works even when the kinds aren't grouped
568 // together.
TEST_F(TransformerTest,OrderedRuleMultipleKinds)569 TEST_F(TransformerTest, OrderedRuleMultipleKinds) {
570 std::string Input = R"cc(
571 void f1();
572 void f2();
573 void call_f1() { f1(); }
574 void call_f2() { f2(); }
575 )cc";
576 std::string Expected = R"cc(
577 void f1();
578 void DECL_RULE();
579 void call_f1() { REPLACE_F1; }
580 void call_f2() { REPLACE_F1_OR_F2; }
581 )cc";
582
583 RewriteRule ReplaceF1 =
584 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
585 changeTo(cat("REPLACE_F1")));
586 RewriteRule ReplaceF1OrF2 =
587 makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
588 changeTo(cat("REPLACE_F1_OR_F2")));
589 RewriteRule DeclRule = makeRule(functionDecl(hasName("f2")).bind("fun"),
590 changeTo(name("fun"), cat("DECL_RULE")));
591
592 RewriteRule Rule = applyFirst({ReplaceF1, DeclRule, ReplaceF1OrF2});
593 EXPECT_EQ(transformer::detail::buildMatchers(Rule).size(), 2UL);
594 testRule(Rule, Input, Expected);
595 }
596
597 // Verifies that a rule with a top-level matcher for an implicit node (like
598 // `implicitCastExpr`) does not change the code, when the AST traversal skips
599 // implicit nodes. In this test, only the rule with the explicit-node matcher
600 // will fire.
TEST_F(TransformerTest,OrderedRuleImplicitIgnored)601 TEST_F(TransformerTest, OrderedRuleImplicitIgnored) {
602 std::string Input = R"cc(
603 void f1();
604 int f2();
605 void call_f1() { f1(); }
606 float call_f2() { return f2(); }
607 )cc";
608 std::string Expected = R"cc(
609 void f1();
610 int f2();
611 void call_f1() { REPLACE_F1; }
612 float call_f2() { return f2(); }
613 )cc";
614
615 RewriteRule ReplaceF1 =
616 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
617 changeTo(cat("REPLACE_F1")));
618 RewriteRule ReplaceF2 =
619 makeRule(implicitCastExpr(hasSourceExpression(callExpr())),
620 changeTo(cat("REPLACE_F2")));
621 testRule(applyFirst({ReplaceF1, ReplaceF2}), Input, Expected);
622 }
623
624 // Verifies that explicitly setting the traversal kind fixes the problem in the
625 // previous test.
TEST_F(TransformerTest,OrderedRuleImplicitMatched)626 TEST_F(TransformerTest, OrderedRuleImplicitMatched) {
627 std::string Input = R"cc(
628 void f1();
629 int f2();
630 void call_f1() { f1(); }
631 float call_f2() { return f2(); }
632 )cc";
633 std::string Expected = R"cc(
634 void f1();
635 int f2();
636 void call_f1() { REPLACE_F1; }
637 float call_f2() { return REPLACE_F2; }
638 )cc";
639
640 RewriteRule ReplaceF1 = makeRule(
641 traverse(clang::TK_AsIs, callExpr(callee(functionDecl(hasName("f1"))))),
642 changeTo(cat("REPLACE_F1")));
643 RewriteRule ReplaceF2 =
644 makeRule(traverse(clang::TK_AsIs,
645 implicitCastExpr(hasSourceExpression(callExpr()))),
646 changeTo(cat("REPLACE_F2")));
647 testRule(applyFirst({ReplaceF1, ReplaceF2}), Input, Expected);
648 }
649
650 //
651 // Negative tests (where we expect no transformation to occur).
652 //
653
654 // Tests for a conflict in edits from a single match for a rule.
TEST_F(TransformerTest,TextGeneratorFailure)655 TEST_F(TransformerTest, TextGeneratorFailure) {
656 std::string Input = "int conflictOneRule() { return 3 + 7; }";
657 // Try to change the whole binary-operator expression AND one its operands:
658 StringRef O = "O";
659 class AlwaysFail : public transformer::MatchComputation<std::string> {
660 llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &,
661 std::string *) const override {
662 return llvm::createStringError(llvm::errc::invalid_argument, "ERROR");
663 }
664 std::string toString() const override { return "AlwaysFail"; }
665 };
666 Transformer T(
667 makeRule(binaryOperator().bind(O),
668 changeTo(node(std::string(O)), std::make_shared<AlwaysFail>())),
669 consumer());
670 T.registerMatchers(&MatchFinder);
671 EXPECT_FALSE(rewrite(Input));
672 EXPECT_THAT(Changes, IsEmpty());
673 EXPECT_EQ(ErrorCount, 1);
674 }
675
676 // Tests for a conflict in edits from a single match for a rule.
TEST_F(TransformerTest,OverlappingEditsInRule)677 TEST_F(TransformerTest, OverlappingEditsInRule) {
678 std::string Input = "int conflictOneRule() { return 3 + 7; }";
679 // Try to change the whole binary-operator expression AND one its operands:
680 StringRef O = "O", L = "L";
681 Transformer T(makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O),
682 {changeTo(node(std::string(O)), cat("DELETE_OP")),
683 changeTo(node(std::string(L)), cat("DELETE_LHS"))}),
684 consumer());
685 T.registerMatchers(&MatchFinder);
686 EXPECT_FALSE(rewrite(Input));
687 EXPECT_THAT(Changes, IsEmpty());
688 EXPECT_EQ(ErrorCount, 1);
689 }
690
691 // Tests for a conflict in edits across multiple matches (of the same rule).
TEST_F(TransformerTest,OverlappingEditsMultipleMatches)692 TEST_F(TransformerTest, OverlappingEditsMultipleMatches) {
693 std::string Input = "int conflictOneRule() { return -7; }";
694 // Try to change the whole binary-operator expression AND one its operands:
695 StringRef E = "E";
696 Transformer T(makeRule(expr().bind(E),
697 changeTo(node(std::string(E)), cat("DELETE_EXPR"))),
698 consumer());
699 T.registerMatchers(&MatchFinder);
700 // The rewrite process fails because the changes conflict with each other...
701 EXPECT_FALSE(rewrite(Input));
702 // ... but two changes were produced.
703 EXPECT_EQ(Changes.size(), 2u);
704 EXPECT_EQ(ErrorCount, 0);
705 }
706
TEST_F(TransformerTest,ErrorOccurredMatchSkipped)707 TEST_F(TransformerTest, ErrorOccurredMatchSkipped) {
708 // Syntax error in the function body:
709 std::string Input = "void errorOccurred() { 3 }";
710 Transformer T(makeRule(functionDecl(hasName("errorOccurred")),
711 changeTo(cat("DELETED;"))),
712 consumer());
713 T.registerMatchers(&MatchFinder);
714 // The rewrite process itself fails...
715 EXPECT_FALSE(rewrite(Input));
716 // ... and no changes or errors are produced in the process.
717 EXPECT_THAT(Changes, IsEmpty());
718 EXPECT_EQ(ErrorCount, 0);
719 }
720
721 // Transformation of macro source text when the change encompasses the entirety
722 // of the expanded text.
TEST_F(TransformerTest,SimpleMacro)723 TEST_F(TransformerTest, SimpleMacro) {
724 std::string Input = R"cc(
725 #define ZERO 0
726 int f(string s) { return ZERO; }
727 )cc";
728 std::string Expected = R"cc(
729 #define ZERO 0
730 int f(string s) { return 999; }
731 )cc";
732
733 StringRef zero = "zero";
734 RewriteRule R = makeRule(integerLiteral(equals(0)).bind(zero),
735 changeTo(node(std::string(zero)), cat("999")));
736 testRule(R, Input, Expected);
737 }
738
739 // Transformation of macro source text when the change encompasses the entirety
740 // of the expanded text, for the case of function-style macros.
TEST_F(TransformerTest,FunctionMacro)741 TEST_F(TransformerTest, FunctionMacro) {
742 std::string Input = R"cc(
743 #define MACRO(str) strlen((str).c_str())
744 int f(string s) { return MACRO(s); }
745 )cc";
746 std::string Expected = R"cc(
747 #define MACRO(str) strlen((str).c_str())
748 int f(string s) { return REPLACED; }
749 )cc";
750
751 testRule(ruleStrlenSize(), Input, Expected);
752 }
753
754 // Tests that expressions in macro arguments can be rewritten.
TEST_F(TransformerTest,MacroArg)755 TEST_F(TransformerTest, MacroArg) {
756 std::string Input = R"cc(
757 #define PLUS(e) e + 1
758 int f(string s) { return PLUS(strlen(s.c_str())); }
759 )cc";
760 std::string Expected = R"cc(
761 #define PLUS(e) e + 1
762 int f(string s) { return PLUS(REPLACED); }
763 )cc";
764
765 testRule(ruleStrlenSize(), Input, Expected);
766 }
767
768 // Tests that expressions in macro arguments can be rewritten, even when the
769 // macro call occurs inside another macro's definition.
TEST_F(TransformerTest,MacroArgInMacroDef)770 TEST_F(TransformerTest, MacroArgInMacroDef) {
771 std::string Input = R"cc(
772 #define NESTED(e) e
773 #define MACRO(str) NESTED(strlen((str).c_str()))
774 int f(string s) { return MACRO(s); }
775 )cc";
776 std::string Expected = R"cc(
777 #define NESTED(e) e
778 #define MACRO(str) NESTED(strlen((str).c_str()))
779 int f(string s) { return REPLACED; }
780 )cc";
781
782 testRule(ruleStrlenSize(), Input, Expected);
783 }
784
785 // Tests the corner case of the identity macro, specifically that it is
786 // discarded in the rewrite rather than preserved (like PLUS is preserved in the
787 // previous test). This behavior is of dubious value (and marked with a FIXME
788 // in the code), but we test it to verify (and demonstrate) how this case is
789 // handled.
TEST_F(TransformerTest,IdentityMacro)790 TEST_F(TransformerTest, IdentityMacro) {
791 std::string Input = R"cc(
792 #define ID(e) e
793 int f(string s) { return ID(strlen(s.c_str())); }
794 )cc";
795 std::string Expected = R"cc(
796 #define ID(e) e
797 int f(string s) { return REPLACED; }
798 )cc";
799
800 testRule(ruleStrlenSize(), Input, Expected);
801 }
802
803 // Tests that two changes in a single macro expansion do not lead to conflicts
804 // in applying the changes.
TEST_F(TransformerTest,TwoChangesInOneMacroExpansion)805 TEST_F(TransformerTest, TwoChangesInOneMacroExpansion) {
806 std::string Input = R"cc(
807 #define PLUS(a,b) (a) + (b)
808 int f() { return PLUS(3, 4); }
809 )cc";
810 std::string Expected = R"cc(
811 #define PLUS(a,b) (a) + (b)
812 int f() { return PLUS(LIT, LIT); }
813 )cc";
814
815 testRule(makeRule(integerLiteral(), changeTo(cat("LIT"))), Input, Expected);
816 }
817
818 // Tests case where the rule's match spans both source from the macro and its
819 // arg, with the begin location (the "anchor") being the arg.
TEST_F(TransformerTest,MatchSpansMacroTextButChangeDoesNot)820 TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNot) {
821 std::string Input = R"cc(
822 #define PLUS_ONE(a) a + 1
823 int f() { return PLUS_ONE(3); }
824 )cc";
825 std::string Expected = R"cc(
826 #define PLUS_ONE(a) a + 1
827 int f() { return PLUS_ONE(LIT); }
828 )cc";
829
830 StringRef E = "expr";
831 testRule(makeRule(binaryOperator(hasLHS(expr().bind(E))),
832 changeTo(node(std::string(E)), cat("LIT"))),
833 Input, Expected);
834 }
835
836 // Tests case where the rule's match spans both source from the macro and its
837 // arg, with the begin location (the "anchor") being inside the macro.
TEST_F(TransformerTest,MatchSpansMacroTextButChangeDoesNotAnchoredInMacro)838 TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNotAnchoredInMacro) {
839 std::string Input = R"cc(
840 #define PLUS_ONE(a) 1 + a
841 int f() { return PLUS_ONE(3); }
842 )cc";
843 std::string Expected = R"cc(
844 #define PLUS_ONE(a) 1 + a
845 int f() { return PLUS_ONE(LIT); }
846 )cc";
847
848 StringRef E = "expr";
849 testRule(makeRule(binaryOperator(hasRHS(expr().bind(E))),
850 changeTo(node(std::string(E)), cat("LIT"))),
851 Input, Expected);
852 }
853
854 // No rewrite is applied when the changed text does not encompass the entirety
855 // of the expanded text. That is, the edit would have to be applied to the
856 // macro's definition to succeed and editing the expansion point would not
857 // suffice.
TEST_F(TransformerTest,NoPartialRewriteOMacroExpansion)858 TEST_F(TransformerTest, NoPartialRewriteOMacroExpansion) {
859 std::string Input = R"cc(
860 #define ZERO_PLUS 0 + 3
861 int f(string s) { return ZERO_PLUS; })cc";
862
863 StringRef zero = "zero";
864 RewriteRule R = makeRule(integerLiteral(equals(0)).bind(zero),
865 changeTo(node(std::string(zero)), cat("0")));
866 testRule(R, Input, Input);
867 }
868
869 // This test handles the corner case where a macro expands within another macro
870 // to matching code, but that code is an argument to the nested macro call. A
871 // simple check of isMacroArgExpansion() vs. isMacroBodyExpansion() will get
872 // this wrong, and transform the code.
TEST_F(TransformerTest,NoPartialRewriteOfMacroExpansionForMacroArgs)873 TEST_F(TransformerTest, NoPartialRewriteOfMacroExpansionForMacroArgs) {
874 std::string Input = R"cc(
875 #define NESTED(e) e
876 #define MACRO(str) 1 + NESTED(strlen((str).c_str()))
877 int f(string s) { return MACRO(s); }
878 )cc";
879
880 testRule(ruleStrlenSize(), Input, Input);
881 }
882
883 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
884 // Verifies that `Type` and `QualType` are not allowed as top-level matchers in
885 // rules.
TEST(TransformerDeathTest,OrderedRuleTypes)886 TEST(TransformerDeathTest, OrderedRuleTypes) {
887 RewriteRule QualTypeRule = makeRule(qualType(), changeTo(cat("Q")));
888 EXPECT_DEATH(transformer::detail::buildMatchers(QualTypeRule),
889 "Matcher must be.*node matcher");
890
891 RewriteRule TypeRule = makeRule(arrayType(), changeTo(cat("T")));
892 EXPECT_DEATH(transformer::detail::buildMatchers(TypeRule),
893 "Matcher must be.*node matcher");
894 }
895 #endif
896
897 // Edits are able to span multiple files; in this case, a header and an
898 // implementation file.
TEST_F(TransformerTest,MultipleFiles)899 TEST_F(TransformerTest, MultipleFiles) {
900 std::string Header = R"cc(void RemoveThisFunction();)cc";
901 std::string Source = R"cc(#include "input.h"
902 void RemoveThisFunction();)cc";
903 Transformer T(
904 makeRule(functionDecl(hasName("RemoveThisFunction")), changeTo(cat(""))),
905 consumer());
906 T.registerMatchers(&MatchFinder);
907 auto Factory = newFrontendActionFactory(&MatchFinder);
908 EXPECT_TRUE(runToolOnCodeWithArgs(
909 Factory->create(), Source, std::vector<std::string>(), "input.cc",
910 "clang-tool", std::make_shared<PCHContainerOperations>(),
911 {{"input.h", Header}}));
912
913 std::sort(Changes.begin(), Changes.end(),
914 [](const AtomicChange &L, const AtomicChange &R) {
915 return L.getFilePath() < R.getFilePath();
916 });
917
918 ASSERT_EQ(Changes[0].getFilePath(), "./input.h");
919 EXPECT_THAT(Changes[0].getInsertedHeaders(), IsEmpty());
920 EXPECT_THAT(Changes[0].getRemovedHeaders(), IsEmpty());
921 llvm::Expected<std::string> UpdatedCode =
922 clang::tooling::applyAllReplacements(Header,
923 Changes[0].getReplacements());
924 ASSERT_TRUE(static_cast<bool>(UpdatedCode))
925 << "Could not update code: " << llvm::toString(UpdatedCode.takeError());
926 EXPECT_EQ(format(*UpdatedCode), format(R"cc(;)cc"));
927
928 ASSERT_EQ(Changes[1].getFilePath(), "input.cc");
929 EXPECT_THAT(Changes[1].getInsertedHeaders(), IsEmpty());
930 EXPECT_THAT(Changes[1].getRemovedHeaders(), IsEmpty());
931 UpdatedCode = clang::tooling::applyAllReplacements(
932 Source, Changes[1].getReplacements());
933 ASSERT_TRUE(static_cast<bool>(UpdatedCode))
934 << "Could not update code: " << llvm::toString(UpdatedCode.takeError());
935 EXPECT_EQ(format(*UpdatedCode), format(R"cc(#include "input.h"
936 ;)cc"));
937 }
938 } // namespace
939