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/Refactoring/Transformer.h"
10 #include "clang/ASTMatchers/ASTMatchers.h"
11 #include "clang/Tooling/Refactoring/RangeSelector.h"
12 #include "clang/Tooling/Tooling.h"
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/Error.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17 
18 using namespace clang;
19 using namespace tooling;
20 using namespace ast_matchers;
21 
22 namespace {
23 using ::testing::IsEmpty;
24 
25 constexpr 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
41   class Logger {};
42   void operator<<(Logger& l, string msg);
43   Logger& log(int level);
44 )cc";
45 
46 static ast_matchers::internal::Matcher<clang::QualType>
47 isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
48   return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
49 }
50 
51 static 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 
64 static 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).
76 class ClangRefactoringTestBase : public testing::Test {
77 protected:
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)) {
91       llvm::errs() << "Running tool failed.\n";
92       return None;
93     }
94     if (ErrorCount != 0) {
95       llvm::errs() << "Generating changes failed.\n";
96       return None;
97     }
98     auto ChangedCode =
99         applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
100     if (!ChangedCode) {
101       llvm::errs() << "Applying changes failed: "
102                    << llvm::toString(ChangedCode.takeError()) << "\n";
103       return None;
104     }
105     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     };
117   }
118 
119   template <typename R>
120   void testRule(R Rule, StringRef Input, StringRef Expected) {
121     Transformer T(std::move(Rule), consumer());
122     T.registerMatchers(&MatchFinder);
123     compareSnippets(Expected, rewrite(Input));
124   }
125 
126   clang::ast_matchers::MatchFinder MatchFinder;
127   // Records whether any errors occurred in individual changes.
128   int ErrorCount = 0;
129   AtomicChanges Changes;
130 
131 private:
132   FileContentMappings FileContents = {{"header.h", ""}};
133 };
134 
135 class TransformerTest : public ClangRefactoringTestBase {
136 protected:
137   TransformerTest() { appendToHeader(KHeaderContents); }
138 };
139 
140 // Given string s, change strlen($s.c_str()) to $s.size().
141 static RewriteRule ruleStrlenSize() {
142   StringRef StringExpr = "strexpr";
143   auto StringType = namedDecl(hasAnyName("::basic_string", "::string"));
144   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")))))),
150       change(text("REPLACED")), text("Use size() method directly on string."));
151   return R;
152 }
153 
154 TEST_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.
161 TEST_F(TransformerTest, NoMatch) {
162   std::string Input = "int f(string s) { return s.size(); }";
163   testRule(ruleStrlenSize(), Input, Input);
164 }
165 
166 // Tests that expressions in macro arguments are rewritten (when applicable).
167 TEST_F(TransformerTest, StrlenSizeMacro) {
168   std::string Input = R"cc(
169 #define ID(e) e
170     int f(string s) { return ID(strlen(s.c_str())); })cc";
171   std::string Expected = R"cc(
172 #define ID(e) e
173     int f(string s) { return ID(REPLACED); })cc";
174   testRule(ruleStrlenSize(), Input, Expected);
175 }
176 
177 // Tests replacing an expression.
178 TEST_F(TransformerTest, Flag) {
179   StringRef Flag = "flag";
180   RewriteRule Rule = makeRule(
181       cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
182                                     hasName("proto::ProtoCommandLineFlag"))))
183                                .bind(Flag)),
184                         unless(callee(cxxMethodDecl(hasName("GetProto"))))),
185       change(node(Flag), text("EXPR")));
186 
187   std::string Input = R"cc(
188     proto::ProtoCommandLineFlag flag;
189     int x = flag.foo();
190     int y = flag.GetProto().foo();
191   )cc";
192   std::string Expected = R"cc(
193     proto::ProtoCommandLineFlag flag;
194     int x = EXPR.foo();
195     int y = flag.GetProto().foo();
196   )cc";
197 
198   testRule(std::move(Rule), Input, Expected);
199 }
200 
201 TEST_F(TransformerTest, NodePartNameNamedDecl) {
202   StringRef Fun = "fun";
203   RewriteRule Rule = makeRule(functionDecl(hasName("bad")).bind(Fun),
204                               change(name(Fun), text("good")));
205 
206   std::string Input = R"cc(
207     int bad(int x);
208     int bad(int x) { return x * x; }
209   )cc";
210   std::string Expected = R"cc(
211     int good(int x);
212     int good(int x) { return x * x; }
213   )cc";
214 
215   testRule(Rule, Input, Expected);
216 }
217 
218 TEST_F(TransformerTest, NodePartNameDeclRef) {
219   std::string Input = R"cc(
220     template <typename T>
221     T bad(T x) {
222       return x;
223     }
224     int neutral(int x) { return bad<int>(x) * x; }
225   )cc";
226   std::string Expected = R"cc(
227     template <typename T>
228     T bad(T x) {
229       return x;
230     }
231     int neutral(int x) { return good<int>(x) * x; }
232   )cc";
233 
234   StringRef Ref = "ref";
235   testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref),
236                     change(name(Ref), text("good"))),
237            Input, Expected);
238 }
239 
240 TEST_F(TransformerTest, NodePartNameDeclRefFailure) {
241   std::string Input = R"cc(
242     struct Y {
243       int operator*();
244     };
245     int neutral(int x) {
246       Y y;
247       int (Y::*ptr)() = &Y::operator*;
248       return *y + x;
249     }
250   )cc";
251 
252   StringRef Ref = "ref";
253   Transformer T(makeRule(declRefExpr(to(functionDecl())).bind(Ref),
254                          change(name(Ref), text("good"))),
255                 consumer());
256   T.registerMatchers(&MatchFinder);
257   EXPECT_FALSE(rewrite(Input));
258 }
259 
260 TEST_F(TransformerTest, NodePartMember) {
261   StringRef E = "expr";
262   RewriteRule Rule = makeRule(memberExpr(member(hasName("bad"))).bind(E),
263                               change(member(E), text("good")));
264 
265   std::string Input = R"cc(
266     struct S {
267       int bad;
268     };
269     int g() {
270       S s;
271       return s.bad;
272     }
273   )cc";
274   std::string Expected = R"cc(
275     struct S {
276       int bad;
277     };
278     int g() {
279       S s;
280       return s.good;
281     }
282   )cc";
283 
284   testRule(Rule, Input, Expected);
285 }
286 
287 TEST_F(TransformerTest, NodePartMemberQualified) {
288   std::string Input = R"cc(
289     struct S {
290       int bad;
291       int good;
292     };
293     struct T : public S {
294       int bad;
295     };
296     int g() {
297       T t;
298       return t.S::bad;
299     }
300   )cc";
301   std::string Expected = R"cc(
302     struct S {
303       int bad;
304       int good;
305     };
306     struct T : public S {
307       int bad;
308     };
309     int g() {
310       T t;
311       return t.S::good;
312     }
313   )cc";
314 
315   StringRef E = "expr";
316   testRule(makeRule(memberExpr().bind(E), change(member(E), text("good"))),
317            Input, Expected);
318 }
319 
320 TEST_F(TransformerTest, NodePartMemberMultiToken) {
321   std::string Input = R"cc(
322     struct Y {
323       int operator*();
324       int good();
325       template <typename T> void foo(T t);
326     };
327     int neutral(int x) {
328       Y y;
329       y.template foo<int>(3);
330       return y.operator *();
331     }
332   )cc";
333   std::string Expected = R"cc(
334     struct Y {
335       int operator*();
336       int good();
337       template <typename T> void foo(T t);
338     };
339     int neutral(int x) {
340       Y y;
341       y.template good<int>(3);
342       return y.good();
343     }
344   )cc";
345 
346   StringRef MemExpr = "member";
347   testRule(makeRule(memberExpr().bind(MemExpr),
348                     change(member(MemExpr), text("good"))),
349            Input, Expected);
350 }
351 
352 TEST_F(TransformerTest, InsertBeforeEdit) {
353   std::string Input = R"cc(
354     int f() {
355       return 7;
356     }
357   )cc";
358   std::string Expected = R"cc(
359     int f() {
360       int y = 3;
361       return 7;
362     }
363   )cc";
364 
365   StringRef Ret = "return";
366   testRule(makeRule(returnStmt().bind(Ret),
367                     insertBefore(statement(Ret), text("int y = 3;"))),
368            Input, Expected);
369 }
370 
371 TEST_F(TransformerTest, InsertAfterEdit) {
372   std::string Input = R"cc(
373     int f() {
374       int x = 5;
375       return 7;
376     }
377   )cc";
378   std::string Expected = R"cc(
379     int f() {
380       int x = 5;
381       int y = 3;
382       return 7;
383     }
384   )cc";
385 
386   StringRef Decl = "decl";
387   testRule(makeRule(declStmt().bind(Decl),
388                     insertAfter(statement(Decl), text("int y = 3;"))),
389            Input, Expected);
390 }
391 
392 TEST_F(TransformerTest, RemoveEdit) {
393   std::string Input = R"cc(
394     int f() {
395       int x = 5;
396       return 7;
397     }
398   )cc";
399   std::string Expected = R"cc(
400     int f() {
401       return 7;
402     }
403   )cc";
404 
405   StringRef Decl = "decl";
406   testRule(makeRule(declStmt().bind(Decl), remove(statement(Decl))), Input,
407            Expected);
408 }
409 
410 TEST_F(TransformerTest, MultiChange) {
411   std::string Input = R"cc(
412     void foo() {
413       if (10 > 1.0)
414         log(1) << "oh no!";
415       else
416         log(0) << "ok";
417     }
418   )cc";
419   std::string Expected = R"(
420     void foo() {
421       if (true) { /* then */ }
422       else { /* else */ }
423     }
424   )";
425 
426   StringRef C = "C", T = "T", E = "E";
427   testRule(makeRule(ifStmt(hasCondition(expr().bind(C)),
428                            hasThen(stmt().bind(T)), hasElse(stmt().bind(E))),
429                     {change(node(C), text("true")),
430                      change(statement(T), text("{ /* then */ }")),
431                      change(statement(E), text("{ /* else */ }"))}),
432            Input, Expected);
433 }
434 
435 TEST_F(TransformerTest, OrderedRuleUnrelated) {
436   StringRef Flag = "flag";
437   RewriteRule FlagRule = makeRule(
438       cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
439                                     hasName("proto::ProtoCommandLineFlag"))))
440                                .bind(Flag)),
441                         unless(callee(cxxMethodDecl(hasName("GetProto"))))),
442       change(node(Flag), text("PROTO")));
443 
444   std::string Input = R"cc(
445     proto::ProtoCommandLineFlag flag;
446     int x = flag.foo();
447     int y = flag.GetProto().foo();
448     int f(string s) { return strlen(s.c_str()); }
449   )cc";
450   std::string Expected = R"cc(
451     proto::ProtoCommandLineFlag flag;
452     int x = PROTO.foo();
453     int y = flag.GetProto().foo();
454     int f(string s) { return REPLACED; }
455   )cc";
456 
457   testRule(applyFirst({ruleStrlenSize(), FlagRule}), Input, Expected);
458 }
459 
460 // Version of ruleStrlenSizeAny that inserts a method with a different name than
461 // ruleStrlenSize, so we can tell their effect apart.
462 RewriteRule ruleStrlenSizeDistinct() {
463   StringRef S;
464   return makeRule(
465       callExpr(callee(functionDecl(hasName("strlen"))),
466                hasArgument(0, cxxMemberCallExpr(
467                                   on(expr().bind(S)),
468                                   callee(cxxMethodDecl(hasName("c_str")))))),
469       change(text("DISTINCT")));
470 }
471 
472 TEST_F(TransformerTest, OrderedRuleRelated) {
473   std::string Input = R"cc(
474     namespace foo {
475     struct mystring {
476       char* c_str();
477     };
478     int f(mystring s) { return strlen(s.c_str()); }
479     }  // namespace foo
480     int g(string s) { return strlen(s.c_str()); }
481   )cc";
482   std::string Expected = R"cc(
483     namespace foo {
484     struct mystring {
485       char* c_str();
486     };
487     int f(mystring s) { return DISTINCT; }
488     }  // namespace foo
489     int g(string s) { return REPLACED; }
490   )cc";
491 
492   testRule(applyFirst({ruleStrlenSize(), ruleStrlenSizeDistinct()}), Input,
493            Expected);
494 }
495 
496 // Change the order of the rules to get a different result.
497 TEST_F(TransformerTest, OrderedRuleRelatedSwapped) {
498   std::string Input = R"cc(
499     namespace foo {
500     struct mystring {
501       char* c_str();
502     };
503     int f(mystring s) { return strlen(s.c_str()); }
504     }  // namespace foo
505     int g(string s) { return strlen(s.c_str()); }
506   )cc";
507   std::string Expected = R"cc(
508     namespace foo {
509     struct mystring {
510       char* c_str();
511     };
512     int f(mystring s) { return DISTINCT; }
513     }  // namespace foo
514     int g(string s) { return DISTINCT; }
515   )cc";
516 
517   testRule(applyFirst({ruleStrlenSizeDistinct(), ruleStrlenSize()}), Input,
518            Expected);
519 }
520 
521 //
522 // Negative tests (where we expect no transformation to occur).
523 //
524 
525 // Tests for a conflict in edits from a single match for a rule.
526 TEST_F(TransformerTest, TextGeneratorFailure) {
527   std::string Input = "int conflictOneRule() { return 3 + 7; }";
528   // Try to change the whole binary-operator expression AND one its operands:
529   StringRef O = "O";
530   auto AlwaysFail = [](const ast_matchers::MatchFinder::MatchResult &)
531       -> llvm::Expected<std::string> {
532     return llvm::createStringError(llvm::errc::invalid_argument, "ERROR");
533   };
534   Transformer T(makeRule(binaryOperator().bind(O), change(node(O), AlwaysFail)),
535                 consumer());
536   T.registerMatchers(&MatchFinder);
537   EXPECT_FALSE(rewrite(Input));
538   EXPECT_THAT(Changes, IsEmpty());
539   EXPECT_EQ(ErrorCount, 1);
540 }
541 
542 // Tests for a conflict in edits from a single match for a rule.
543 TEST_F(TransformerTest, OverlappingEditsInRule) {
544   std::string Input = "int conflictOneRule() { return 3 + 7; }";
545   // Try to change the whole binary-operator expression AND one its operands:
546   StringRef O = "O", L = "L";
547   Transformer T(makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O),
548                          {change(node(O), text("DELETE_OP")),
549                           change(node(L), text("DELETE_LHS"))}),
550                 consumer());
551   T.registerMatchers(&MatchFinder);
552   EXPECT_FALSE(rewrite(Input));
553   EXPECT_THAT(Changes, IsEmpty());
554   EXPECT_EQ(ErrorCount, 1);
555 }
556 
557 // Tests for a conflict in edits across multiple matches (of the same rule).
558 TEST_F(TransformerTest, OverlappingEditsMultipleMatches) {
559   std::string Input = "int conflictOneRule() { return -7; }";
560   // Try to change the whole binary-operator expression AND one its operands:
561   StringRef E = "E";
562   Transformer T(makeRule(expr().bind(E), change(node(E), text("DELETE_EXPR"))),
563                 consumer());
564   T.registerMatchers(&MatchFinder);
565   // The rewrite process fails because the changes conflict with each other...
566   EXPECT_FALSE(rewrite(Input));
567   // ... but two changes were produced.
568   EXPECT_EQ(Changes.size(), 2u);
569   EXPECT_EQ(ErrorCount, 0);
570 }
571 
572 TEST_F(TransformerTest, ErrorOccurredMatchSkipped) {
573   // Syntax error in the function body:
574   std::string Input = "void errorOccurred() { 3 }";
575   Transformer T(makeRule(functionDecl(hasName("errorOccurred")),
576                          change(text("DELETED;"))),
577                 consumer());
578   T.registerMatchers(&MatchFinder);
579   // The rewrite process itself fails...
580   EXPECT_FALSE(rewrite(Input));
581   // ... and no changes or errors are produced in the process.
582   EXPECT_THAT(Changes, IsEmpty());
583   EXPECT_EQ(ErrorCount, 0);
584 }
585 
586 TEST_F(TransformerTest, NoTransformationInMacro) {
587   std::string Input = R"cc(
588 #define MACRO(str) strlen((str).c_str())
589     int f(string s) { return MACRO(s); })cc";
590   testRule(ruleStrlenSize(), Input, Input);
591 }
592 
593 // This test handles the corner case where a macro called within another macro
594 // expands to matching code, but the matched code is an argument to the nested
595 // macro.  A simple check of isMacroArgExpansion() vs. isMacroBodyExpansion()
596 // will get this wrong, and transform the code. This test verifies that no such
597 // transformation occurs.
598 TEST_F(TransformerTest, NoTransformationInNestedMacro) {
599   std::string Input = R"cc(
600 #define NESTED(e) e
601 #define MACRO(str) NESTED(strlen((str).c_str()))
602     int f(string s) { return MACRO(s); })cc";
603   testRule(ruleStrlenSize(), Input, Input);
604 }
605 } // namespace
606