1 //===- unittest/Tooling/TransformerTest.cpp -------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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/RewriteRule.h"
14 #include "clang/Tooling/Transformer/Stencil.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/Support/Errc.h"
17 #include "llvm/Support/Error.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
22 using namespace clang
;
23 using namespace tooling
;
24 using namespace ast_matchers
;
26 using ::clang::transformer::addInclude
;
27 using ::clang::transformer::applyFirst
;
28 using ::clang::transformer::before
;
29 using ::clang::transformer::cat
;
30 using ::clang::transformer::changeTo
;
31 using ::clang::transformer::editList
;
32 using ::clang::transformer::makeRule
;
33 using ::clang::transformer::member
;
34 using ::clang::transformer::name
;
35 using ::clang::transformer::node
;
36 using ::clang::transformer::noEdits
;
37 using ::clang::transformer::remove
;
38 using ::clang::transformer::rewriteDescendants
;
39 using ::clang::transformer::RewriteRule
;
40 using ::clang::transformer::RewriteRuleWith
;
41 using ::clang::transformer::statement
;
42 using ::testing::ElementsAre
;
43 using ::testing::IsEmpty
;
44 using ::testing::ResultOf
;
45 using ::testing::UnorderedElementsAre
;
47 constexpr char KHeaderContents
[] = R
"cc(
53 int strlen(const char*);
59 struct ProtoCommandLineFlag : PCFProto {
64 void operator<<(Logger& l, string msg);
65 Logger& log(int level);
68 static ast_matchers::internal::Matcher
<clang::QualType
>
69 isOrPointsTo(const clang::ast_matchers::DeclarationMatcher
&TypeMatcher
) {
70 return anyOf(hasDeclaration(TypeMatcher
), pointsTo(TypeMatcher
));
73 static std::string
format(StringRef Code
) {
74 const std::vector
<Range
> Ranges(1, Range(0, Code
.size()));
75 auto Style
= format::getLLVMStyle();
76 const auto Replacements
= format::reformat(Style
, Code
, Ranges
);
77 auto Formatted
= applyAllReplacements(Code
, Replacements
);
79 ADD_FAILURE() << "Could not format code: "
80 << llvm::toString(Formatted
.takeError());
86 static void compareSnippets(StringRef Expected
,
87 const std::optional
<std::string
> &MaybeActual
) {
88 ASSERT_TRUE(MaybeActual
) << "Rewrite failed. Expecting: " << Expected
;
89 auto Actual
= *MaybeActual
;
90 std::string HL
= "#include \"header.h\"\n";
91 auto I
= Actual
.find(HL
);
92 if (I
!= std::string::npos
)
93 Actual
.erase(I
, HL
.size());
94 EXPECT_EQ(format(Expected
), format(Actual
));
97 // FIXME: consider separating this class into its own file(s).
98 class ClangRefactoringTestBase
: public testing::Test
{
100 void appendToHeader(StringRef S
) { FileContents
[0].second
+= S
; }
102 void addFile(StringRef Filename
, StringRef Content
) {
103 FileContents
.emplace_back(std::string(Filename
), std::string(Content
));
106 std::optional
<std::string
> rewrite(StringRef Input
) {
107 std::string Code
= ("#include \"header.h\"\n" + Input
).str();
108 auto Factory
= newFrontendActionFactory(&MatchFinder
);
109 if (!runToolOnCodeWithArgs(
110 Factory
->create(), Code
, std::vector
<std::string
>(), "input.cc",
111 "clang-tool", std::make_shared
<PCHContainerOperations
>(),
113 llvm::errs() << "Running tool failed.\n";
116 if (ErrorCount
!= 0) {
117 llvm::errs() << "Generating changes failed.\n";
121 applyAtomicChanges("input.cc", Code
, Changes
, ApplyChangesSpec());
123 llvm::errs() << "Applying changes failed: "
124 << llvm::toString(ChangedCode
.takeError()) << "\n";
130 Transformer::ChangeSetConsumer
consumer() {
131 return [this](Expected
<MutableArrayRef
<AtomicChange
>> C
) {
133 Changes
.insert(Changes
.end(), std::make_move_iterator(C
->begin()),
134 std::make_move_iterator(C
->end()));
136 // FIXME: stash this error rather than printing.
137 llvm::errs() << "Error generating changes: "
138 << llvm::toString(C
.takeError()) << "\n";
144 auto consumerWithStringMetadata() {
145 return [this](Expected
<TransformerResult
<std::string
>> C
) {
147 Changes
.insert(Changes
.end(),
148 std::make_move_iterator(C
->Changes
.begin()),
149 std::make_move_iterator(C
->Changes
.end()));
150 StringMetadata
.push_back(std::move(C
->Metadata
));
152 // FIXME: stash this error rather than printing.
153 llvm::errs() << "Error generating changes: "
154 << llvm::toString(C
.takeError()) << "\n";
160 void testRule(RewriteRule Rule
, StringRef Input
, StringRef Expected
) {
161 Transformers
.push_back(
162 std::make_unique
<Transformer
>(std::move(Rule
), consumer()));
163 Transformers
.back()->registerMatchers(&MatchFinder
);
164 compareSnippets(Expected
, rewrite(Input
));
167 void testRule(RewriteRuleWith
<std::string
> Rule
, StringRef Input
,
168 StringRef Expected
) {
169 Transformers
.push_back(std::make_unique
<Transformer
>(
170 std::move(Rule
), consumerWithStringMetadata()));
171 Transformers
.back()->registerMatchers(&MatchFinder
);
172 compareSnippets(Expected
, rewrite(Input
));
175 void testRuleFailure(RewriteRule Rule
, StringRef Input
) {
176 Transformers
.push_back(
177 std::make_unique
<Transformer
>(std::move(Rule
), consumer()));
178 Transformers
.back()->registerMatchers(&MatchFinder
);
179 ASSERT_FALSE(rewrite(Input
)) << "Expected failure to rewrite code";
182 void testRuleFailure(RewriteRuleWith
<std::string
> Rule
, StringRef Input
) {
183 Transformers
.push_back(std::make_unique
<Transformer
>(
184 std::move(Rule
), consumerWithStringMetadata()));
185 Transformers
.back()->registerMatchers(&MatchFinder
);
186 ASSERT_FALSE(rewrite(Input
)) << "Expected failure to rewrite code";
189 // Transformers are referenced by MatchFinder.
190 std::vector
<std::unique_ptr
<Transformer
>> Transformers
;
191 clang::ast_matchers::MatchFinder MatchFinder
;
192 // Records whether any errors occurred in individual changes.
194 AtomicChanges Changes
;
195 std::vector
<std::string
> StringMetadata
;
198 FileContentMappings FileContents
= {{"header.h", ""}};
201 class TransformerTest
: public ClangRefactoringTestBase
{
203 TransformerTest() { appendToHeader(KHeaderContents
); }
206 // Given string s, change strlen($s.c_str()) to REPLACED.
207 static RewriteRuleWith
<std::string
> ruleStrlenSize() {
208 StringRef StringExpr
= "strexpr";
209 auto StringType
= namedDecl(hasAnyName("::basic_string", "::string"));
211 callExpr(callee(functionDecl(hasName("strlen"))),
212 hasArgument(0, cxxMemberCallExpr(
213 on(expr(hasType(isOrPointsTo(StringType
)))
215 callee(cxxMethodDecl(hasName("c_str")))))),
216 changeTo(cat("REPLACED")), cat("Use size() method directly on string."));
220 TEST_F(TransformerTest
, StrlenSize
) {
221 std::string Input
= "int f(string s) { return strlen(s.c_str()); }";
222 std::string Expected
= "int f(string s) { return REPLACED; }";
223 testRule(ruleStrlenSize(), Input
, Expected
);
226 // Tests that no change is applied when a match is not expected.
227 TEST_F(TransformerTest
, NoMatch
) {
228 std::string Input
= "int f(string s) { return s.size(); }";
229 testRule(ruleStrlenSize(), Input
, Input
);
232 // Tests replacing an expression.
233 TEST_F(TransformerTest
, Flag
) {
234 StringRef Flag
= "flag";
235 RewriteRule Rule
= makeRule(
236 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
237 hasName("proto::ProtoCommandLineFlag"))))
239 unless(callee(cxxMethodDecl(hasName("GetProto"))))),
240 changeTo(node(std::string(Flag
)), cat("EXPR")));
242 std::string Input
= R
"cc(
243 proto::ProtoCommandLineFlag flag;
245 int y = flag.GetProto().foo();
247 std::string Expected
= R
"cc(
248 proto::ProtoCommandLineFlag flag;
250 int y = flag.GetProto().foo();
253 testRule(std::move(Rule
), Input
, Expected
);
256 TEST_F(TransformerTest
, AddIncludeQuoted
) {
258 makeRule(callExpr(callee(functionDecl(hasName("f")))),
259 {addInclude("clang/OtherLib.h"), changeTo(cat("other()"))});
261 std::string Input
= R
"cc(
263 int h(int x) { return f(x); }
265 std::string Expected
= R
"cc(#include "clang
/OtherLib
.h
"
268 int h(int x) { return other(); }
271 testRule(Rule
, Input
, Expected
);
274 TEST_F(TransformerTest
, AddIncludeAngled
) {
275 RewriteRule Rule
= makeRule(
276 callExpr(callee(functionDecl(hasName("f")))),
277 {addInclude("clang/OtherLib.h", transformer::IncludeFormat::Angled
),
278 changeTo(cat("other()"))});
280 std::string Input
= R
"cc(
282 int h(int x) { return f(x); }
284 std::string Expected
= R
"cc(#include <clang/OtherLib.h>
287 int h(int x) { return other(); }
290 testRule(Rule
, Input
, Expected
);
293 TEST_F(TransformerTest
, AddIncludeQuotedForRule
) {
294 RewriteRule Rule
= makeRule(callExpr(callee(functionDecl(hasName("f")))),
295 changeTo(cat("other()")));
296 addInclude(Rule
, "clang/OtherLib.h");
298 std::string Input
= R
"cc(
300 int h(int x) { return f(x); }
302 std::string Expected
= R
"cc(#include "clang
/OtherLib
.h
"
305 int h(int x) { return other(); }
308 testRule(Rule
, Input
, Expected
);
311 TEST_F(TransformerTest
, AddIncludeAngledForRule
) {
312 RewriteRule Rule
= makeRule(callExpr(callee(functionDecl(hasName("f")))),
313 changeTo(cat("other()")));
314 addInclude(Rule
, "clang/OtherLib.h", transformer::IncludeFormat::Angled
);
316 std::string Input
= R
"cc(
318 int h(int x) { return f(x); }
320 std::string Expected
= R
"cc(#include <clang/OtherLib.h>
323 int h(int x) { return other(); }
326 testRule(Rule
, Input
, Expected
);
329 TEST_F(TransformerTest
, NodePartNameNamedDecl
) {
330 StringRef Fun
= "fun";
331 RewriteRule Rule
= makeRule(functionDecl(hasName("bad")).bind(Fun
),
332 changeTo(name(std::string(Fun
)), cat("good")));
334 std::string Input
= R
"cc(
336 int bad(int x) { return x * x; }
338 std::string Expected
= R
"cc(
340 int good(int x) { return x * x; }
343 testRule(Rule
, Input
, Expected
);
346 TEST_F(TransformerTest
, NodePartNameDeclRef
) {
347 std::string Input
= R
"cc(
348 template <typename T>
352 int neutral(int x) { return bad<int>(x) * x; }
354 std::string Expected
= R
"cc(
355 template <typename T>
359 int neutral(int x) { return good<int>(x) * x; }
362 StringRef Ref
= "ref";
363 testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref
),
364 changeTo(name(std::string(Ref
)), cat("good"))),
368 TEST_F(TransformerTest
, NodePartNameDeclRefFailure
) {
369 std::string Input
= R
"cc(
375 int (Y::*ptr)() = &Y::operator*;
380 StringRef Ref
= "ref";
381 Transformer
T(makeRule(declRefExpr(to(functionDecl())).bind(Ref
),
382 changeTo(name(std::string(Ref
)), cat("good"))),
384 T
.registerMatchers(&MatchFinder
);
385 EXPECT_FALSE(rewrite(Input
));
388 TEST_F(TransformerTest
, NodePartMember
) {
389 StringRef E
= "expr";
391 makeRule(memberExpr(clang::ast_matchers::member(hasName("bad"))).bind(E
),
392 changeTo(member(std::string(E
)), cat("good")));
394 std::string Input
= R
"cc(
403 std::string Expected
= R
"cc(
413 testRule(Rule
, Input
, Expected
);
416 TEST_F(TransformerTest
, NodePartMemberQualified
) {
417 std::string Input
= R
"cc(
422 struct T : public S {
430 std::string Expected
= R
"cc(
435 struct T : public S {
444 StringRef E
= "expr";
445 testRule(makeRule(memberExpr().bind(E
),
446 changeTo(member(std::string(E
)), cat("good"))),
450 TEST_F(TransformerTest
, NodePartMemberMultiToken
) {
451 std::string Input
= R
"cc(
455 template <typename T> void foo(T t);
459 y.template foo<int>(3);
460 return y.operator *();
463 std::string Expected
= R
"cc(
467 template <typename T> void foo(T t);
471 y.template good<int>(3);
476 StringRef MemExpr
= "member";
477 testRule(makeRule(memberExpr().bind(MemExpr
),
478 changeTo(member(std::string(MemExpr
)), cat("good"))),
482 TEST_F(TransformerTest
, NoEdits
) {
483 using transformer::noEdits
;
484 std::string Input
= "int f(int x) { return x; }";
485 testRule(makeRule(returnStmt().bind("return"), noEdits()), Input
, Input
);
488 TEST_F(TransformerTest
, NoopEdit
) {
489 using transformer::node
;
490 using transformer::noopEdit
;
491 std::string Input
= "int f(int x) { return x; }";
492 testRule(makeRule(returnStmt().bind("return"), noopEdit(node("return"))),
496 TEST_F(TransformerTest
, IfBound2Args
) {
497 using transformer::ifBound
;
498 std::string Input
= "int f(int x) { return x; }";
499 std::string Expected
= "int f(int x) { CHANGE; }";
500 testRule(makeRule(returnStmt().bind("return"),
501 ifBound("return", changeTo(cat("CHANGE;")))),
505 TEST_F(TransformerTest
, IfBound3Args
) {
506 using transformer::ifBound
;
507 std::string Input
= "int f(int x) { return x; }";
508 std::string Expected
= "int f(int x) { CHANGE; }";
509 testRule(makeRule(returnStmt().bind("return"),
510 ifBound("nothing", changeTo(cat("ERROR")),
511 changeTo(cat("CHANGE;")))),
515 TEST_F(TransformerTest
, ShrinkTo
) {
516 using transformer::shrinkTo
;
517 std::string Input
= "int f(int x) { return x; }";
518 std::string Expected
= "return x;";
519 testRule(makeRule(functionDecl(hasDescendant(returnStmt().bind("return")))
521 shrinkTo(node("function"), node("return"))),
525 // Rewrite various Stmts inside a Decl.
526 TEST_F(TransformerTest
, RewriteDescendantsDeclChangeStmt
) {
528 "int f(int x) { int y = x; { int z = x * x; } return x; }";
529 std::string Expected
=
530 "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }";
532 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));
533 testRule(makeRule(functionDecl(hasName("f")).bind("fun"),
534 rewriteDescendants("fun", InlineX
)),
538 // Rewrite various TypeLocs inside a Decl.
539 TEST_F(TransformerTest
, RewriteDescendantsDeclChangeTypeLoc
) {
540 std::string Input
= "int f(int *x) { return *x; }";
541 std::string Expected
= "char f(char *x) { return *x; }";
542 auto IntToChar
= makeRule(typeLoc(loc(qualType(isInteger(), builtinType()))),
543 changeTo(cat("char")));
544 testRule(makeRule(functionDecl(hasName("f")).bind("fun"),
545 rewriteDescendants("fun", IntToChar
)),
549 TEST_F(TransformerTest
, RewriteDescendantsStmt
) {
550 // Add an unrelated definition to the header that also has a variable named
551 // "x", to test that the rewrite is limited to the scope we intend.
552 appendToHeader(R
"cc(int g(int x) { return x; })cc");
554 "int f(int x) { int y = x; { int z = x * x; } return x; }";
555 std::string Expected
=
556 "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }";
558 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));
559 testRule(makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))),
560 rewriteDescendants("body", InlineX
)),
564 TEST_F(TransformerTest
, RewriteDescendantsStmtWithAdditionalChange
) {
566 "int f(int x) { int y = x; { int z = x * x; } return x; }";
567 std::string Expected
=
568 "int newName(int x) { int y = 3; { int z = 3 * 3; } return 3; }";
570 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));
573 functionDecl(hasName("f"), hasBody(stmt().bind("body"))).bind("f"),
574 flatten(changeTo(name("f"), cat("newName")),
575 rewriteDescendants("body", InlineX
))),
579 TEST_F(TransformerTest
, RewriteDescendantsTypeLoc
) {
580 std::string Input
= "int f(int *x) { return *x; }";
581 std::string Expected
= "int f(char *x) { return *x; }";
583 makeRule(typeLoc(loc(qualType(isInteger(), builtinType()))).bind("loc"),
584 changeTo(cat("char")));
586 makeRule(functionDecl(hasName("f"),
587 hasParameter(0, varDecl(hasTypeLoc(
588 typeLoc().bind("parmType"))))),
589 rewriteDescendants("parmType", IntToChar
)),
593 TEST_F(TransformerTest
, RewriteDescendantsReferToParentBinding
) {
595 "int f(int p) { int y = p; { int z = p * p; } return p; }";
596 std::string Expected
=
597 "int f(int p) { int y = 3; { int z = 3 * 3; } return 3; }";
598 std::string VarId
= "var";
599 auto InlineVar
= makeRule(declRefExpr(to(varDecl(equalsBoundNode(VarId
)))),
601 testRule(makeRule(functionDecl(hasName("f"),
602 hasParameter(0, varDecl().bind(VarId
)))
604 rewriteDescendants("fun", InlineVar
)),
608 TEST_F(TransformerTest
, RewriteDescendantsUnboundNode
) {
610 "int f(int x) { int y = x; { int z = x * x; } return x; }";
612 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));
613 Transformer
T(makeRule(functionDecl(hasName("f")),
614 rewriteDescendants("UNBOUND", InlineX
)),
616 T
.registerMatchers(&MatchFinder
);
617 EXPECT_FALSE(rewrite(Input
));
618 EXPECT_THAT(Changes
, IsEmpty());
619 EXPECT_EQ(ErrorCount
, 1);
622 TEST_F(TransformerTest
, RewriteDescendantsInvalidNodeType
) {
624 "int f(int x) { int y = x; { int z = x * x; } return x; }";
626 makeRule(qualType(isInteger(), builtinType()), changeTo(cat("char")));
628 makeRule(functionDecl(
630 hasParameter(0, varDecl(hasType(qualType().bind("type"))))),
631 rewriteDescendants("type", IntToChar
)),
633 T
.registerMatchers(&MatchFinder
);
634 EXPECT_FALSE(rewrite(Input
));
635 EXPECT_THAT(Changes
, IsEmpty());
636 EXPECT_EQ(ErrorCount
, 1);
640 // We include one test per typed overload. We don't test extensively since that
641 // is already covered by the tests above.
644 TEST_F(TransformerTest
, RewriteDescendantsTypedStmt
) {
645 // Add an unrelated definition to the header that also has a variable named
646 // "x", to test that the rewrite is limited to the scope we intend.
647 appendToHeader(R
"cc(int g(int x) { return x; })cc");
649 "int f(int x) { int y = x; { int z = x * x; } return x; }";
650 std::string Expected
=
651 "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }";
653 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));
654 testRule(makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))),
655 [&InlineX
](const MatchFinder::MatchResult
&R
) {
656 const auto *Node
= R
.Nodes
.getNodeAs
<Stmt
>("body");
657 assert(Node
!= nullptr && "body must be bound");
658 return transformer::detail::rewriteDescendants(
664 TEST_F(TransformerTest
, RewriteDescendantsTypedDecl
) {
666 "int f(int x) { int y = x; { int z = x * x; } return x; }";
667 std::string Expected
=
668 "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }";
670 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));
671 testRule(makeRule(functionDecl(hasName("f")).bind("fun"),
672 [&InlineX
](const MatchFinder::MatchResult
&R
) {
673 const auto *Node
= R
.Nodes
.getNodeAs
<Decl
>("fun");
674 assert(Node
!= nullptr && "fun must be bound");
675 return transformer::detail::rewriteDescendants(
681 TEST_F(TransformerTest
, RewriteDescendantsTypedTypeLoc
) {
682 std::string Input
= "int f(int *x) { return *x; }";
683 std::string Expected
= "int f(char *x) { return *x; }";
685 makeRule(typeLoc(loc(qualType(isInteger(), builtinType()))).bind("loc"),
686 changeTo(cat("char")));
691 hasParameter(0, varDecl(hasTypeLoc(typeLoc().bind("parmType"))))),
692 [&IntToChar
](const MatchFinder::MatchResult
&R
) {
693 const auto *Node
= R
.Nodes
.getNodeAs
<TypeLoc
>("parmType");
694 assert(Node
!= nullptr && "parmType must be bound");
695 return transformer::detail::rewriteDescendants(*Node
, IntToChar
, R
);
700 TEST_F(TransformerTest
, RewriteDescendantsTypedDynTyped
) {
701 // Add an unrelated definition to the header that also has a variable named
702 // "x", to test that the rewrite is limited to the scope we intend.
703 appendToHeader(R
"cc(int g(int x) { return x; })cc");
705 "int f(int x) { int y = x; { int z = x * x; } return x; }";
706 std::string Expected
=
707 "int f(int x) { int y = 3; { int z = 3 * 3; } return 3; }";
709 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));
711 makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))),
712 [&InlineX
](const MatchFinder::MatchResult
&R
) {
713 auto It
= R
.Nodes
.getMap().find("body");
714 assert(It
!= R
.Nodes
.getMap().end() && "body must be bound");
715 return transformer::detail::rewriteDescendants(It
->second
,
721 TEST_F(TransformerTest
, InsertBeforeEdit
) {
722 std::string Input
= R
"cc(
727 std::string Expected
= R
"cc(
734 StringRef Ret
= "return";
736 makeRule(returnStmt().bind(Ret
),
737 insertBefore(statement(std::string(Ret
)), cat("int y = 3;"))),
741 TEST_F(TransformerTest
, InsertAfterEdit
) {
742 std::string Input
= R
"cc(
748 std::string Expected
= R
"cc(
756 StringRef Decl
= "decl";
758 makeRule(declStmt().bind(Decl
),
759 insertAfter(statement(std::string(Decl
)), cat("int y = 3;"))),
763 TEST_F(TransformerTest
, RemoveEdit
) {
764 std::string Input
= R
"cc(
770 std::string Expected
= R
"cc(
776 StringRef Decl
= "decl";
778 makeRule(declStmt().bind(Decl
), remove(statement(std::string(Decl
)))),
782 TEST_F(TransformerTest
, WithMetadata
) {
783 auto makeMetadata
= [](const MatchFinder::MatchResult
&R
) -> llvm::Any
{
785 R
.Nodes
.getNodeAs
<IntegerLiteral
>("int")->getValue().getLimitedValue();
789 std::string Input
= R
"cc(
798 declStmt(containsDeclaration(0, varDecl(hasInitializer(
799 integerLiteral().bind("int")))))
801 withMetadata(remove(statement(std::string("decl"))), makeMetadata
)),
803 T
.registerMatchers(&MatchFinder
);
804 auto Factory
= newFrontendActionFactory(&MatchFinder
);
805 EXPECT_TRUE(runToolOnCodeWithArgs(
806 Factory
->create(), Input
, std::vector
<std::string
>(), "input.cc",
807 "clang-tool", std::make_shared
<PCHContainerOperations
>(), {}));
808 ASSERT_EQ(Changes
.size(), 1u);
809 const llvm::Any
&Metadata
= Changes
[0].getMetadata();
810 ASSERT_TRUE(llvm::any_cast
<int>(&Metadata
));
811 EXPECT_THAT(llvm::any_cast
<int>(Metadata
), 5);
814 TEST_F(TransformerTest
, MultiChange
) {
815 std::string Input
= R
"cc(
823 std::string Expected
= R
"(
825 if (true) { /* then */ }
830 StringRef C
= "C", T
= "T", E
= "E";
832 makeRule(ifStmt(hasCondition(expr().bind(C
)), hasThen(stmt().bind(T
)),
833 hasElse(stmt().bind(E
))),
834 {changeTo(node(std::string(C
)), cat("true")),
835 changeTo(statement(std::string(T
)), cat("{ /* then */ }")),
836 changeTo(statement(std::string(E
)), cat("{ /* else */ }"))}),
840 TEST_F(TransformerTest
, EditList
) {
841 std::string Input
= R
"cc(
849 std::string Expected
= R
"(
851 if (true) { /* then */ }
856 StringRef C
= "C", T
= "T", E
= "E";
857 testRule(makeRule(ifStmt(hasCondition(expr().bind(C
)),
858 hasThen(stmt().bind(T
)), hasElse(stmt().bind(E
))),
859 editList({changeTo(node(std::string(C
)), cat("true")),
860 changeTo(statement(std::string(T
)),
861 cat("{ /* then */ }")),
862 changeTo(statement(std::string(E
)),
863 cat("{ /* else */ }"))})),
867 TEST_F(TransformerTest
, Flatten
) {
868 std::string Input
= R
"cc(
876 std::string Expected
= R
"(
878 if (true) { /* then */ }
883 StringRef C
= "C", T
= "T", E
= "E";
886 ifStmt(hasCondition(expr().bind(C
)), hasThen(stmt().bind(T
)),
887 hasElse(stmt().bind(E
))),
888 flatten(changeTo(node(std::string(C
)), cat("true")),
889 changeTo(statement(std::string(T
)), cat("{ /* then */ }")),
890 changeTo(statement(std::string(E
)), cat("{ /* else */ }")))),
894 TEST_F(TransformerTest
, FlattenWithMixedArgs
) {
895 using clang::transformer::editList
;
896 std::string Input
= R
"cc(
904 std::string Expected
= R
"(
906 if (true) { /* then */ }
911 StringRef C
= "C", T
= "T", E
= "E";
912 testRule(makeRule(ifStmt(hasCondition(expr().bind(C
)),
913 hasThen(stmt().bind(T
)), hasElse(stmt().bind(E
))),
914 flatten(changeTo(node(std::string(C
)), cat("true")),
915 edit(changeTo(statement(std::string(T
)),
916 cat("{ /* then */ }"))),
917 editList({changeTo(statement(std::string(E
)),
918 cat("{ /* else */ }"))}))),
922 TEST_F(TransformerTest
, OrderedRuleUnrelated
) {
923 StringRef Flag
= "flag";
924 RewriteRuleWith
<std::string
> FlagRule
= makeRule(
925 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
926 hasName("proto::ProtoCommandLineFlag"))))
928 unless(callee(cxxMethodDecl(hasName("GetProto"))))),
929 changeTo(node(std::string(Flag
)), cat("PROTO")), cat(""));
931 std::string Input
= R
"cc(
932 proto::ProtoCommandLineFlag flag;
934 int y = flag.GetProto().foo();
935 int f(string s) { return strlen(s.c_str()); }
937 std::string Expected
= R
"cc(
938 proto::ProtoCommandLineFlag flag;
940 int y = flag.GetProto().foo();
941 int f(string s) { return REPLACED; }
944 testRule(applyFirst({ruleStrlenSize(), FlagRule
}), Input
, Expected
);
947 TEST_F(TransformerTest
, OrderedRuleRelated
) {
948 std::string Input
= R
"cc(
951 void call_f1() { f1(); }
952 void call_f2() { f2(); }
954 std::string Expected
= R
"cc(
957 void call_f1() { REPLACE_F1; }
958 void call_f2() { REPLACE_F1_OR_F2; }
961 RewriteRule ReplaceF1
=
962 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
963 changeTo(cat("REPLACE_F1")));
964 RewriteRule ReplaceF1OrF2
=
965 makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
966 changeTo(cat("REPLACE_F1_OR_F2")));
967 testRule(applyFirst({ReplaceF1
, ReplaceF1OrF2
}), Input
, Expected
);
970 // Change the order of the rules to get a different result. When `ReplaceF1OrF2`
971 // comes first, it applies for both uses, so `ReplaceF1` never applies.
972 TEST_F(TransformerTest
, OrderedRuleRelatedSwapped
) {
973 std::string Input
= R
"cc(
976 void call_f1() { f1(); }
977 void call_f2() { f2(); }
979 std::string Expected
= R
"cc(
982 void call_f1() { REPLACE_F1_OR_F2; }
983 void call_f2() { REPLACE_F1_OR_F2; }
986 RewriteRule ReplaceF1
=
987 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
988 changeTo(cat("REPLACE_F1")));
989 RewriteRule ReplaceF1OrF2
=
990 makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
991 changeTo(cat("REPLACE_F1_OR_F2")));
992 testRule(applyFirst({ReplaceF1OrF2
, ReplaceF1
}), Input
, Expected
);
995 // Verify that a set of rules whose matchers have different base kinds works
996 // properly, including that `applyFirst` produces multiple matchers. We test
997 // two different kinds of rules: Expr and Decl. We place the Decl rule in the
998 // middle to test that `buildMatchers` works even when the kinds aren't grouped
1000 TEST_F(TransformerTest
, OrderedRuleMultipleKinds
) {
1001 std::string Input
= R
"cc(
1004 void call_f1() { f1(); }
1005 void call_f2() { f2(); }
1007 std::string Expected
= R
"cc(
1010 void call_f1() { REPLACE_F1; }
1011 void call_f2() { REPLACE_F1_OR_F2; }
1014 RewriteRule ReplaceF1
=
1015 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
1016 changeTo(cat("REPLACE_F1")));
1017 RewriteRule ReplaceF1OrF2
=
1018 makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
1019 changeTo(cat("REPLACE_F1_OR_F2")));
1020 RewriteRule DeclRule
= makeRule(functionDecl(hasName("f2")).bind("fun"),
1021 changeTo(name("fun"), cat("DECL_RULE")));
1023 RewriteRule Rule
= applyFirst({ReplaceF1
, DeclRule
, ReplaceF1OrF2
});
1024 EXPECT_EQ(transformer::detail::buildMatchers(Rule
).size(), 2UL);
1025 testRule(Rule
, Input
, Expected
);
1028 // Verifies that a rule with a top-level matcher for an implicit node (like
1029 // `implicitCastExpr`) works correctly -- the implicit nodes are not skipped.
1030 TEST_F(TransformerTest
, OrderedRuleImplicitMatched
) {
1031 std::string Input
= R
"cc(
1034 void call_f1() { f1(); }
1035 float call_f2() { return f2(); }
1037 std::string Expected
= R
"cc(
1040 void call_f1() { REPLACE_F1; }
1041 float call_f2() { return REPLACE_F2; }
1044 RewriteRule ReplaceF1
=
1045 makeRule(callExpr(callee(functionDecl(hasName("f1")))),
1046 changeTo(cat("REPLACE_F1")));
1047 RewriteRule ReplaceF2
=
1048 makeRule(implicitCastExpr(hasSourceExpression(callExpr())),
1049 changeTo(cat("REPLACE_F2")));
1050 testRule(applyFirst({ReplaceF1
, ReplaceF2
}), Input
, Expected
);
1054 // Negative tests (where we expect no transformation to occur).
1057 // Tests for a conflict in edits from a single match for a rule.
1058 TEST_F(TransformerTest
, TextGeneratorFailure
) {
1059 std::string Input
= "int conflictOneRule() { return 3 + 7; }";
1060 // Try to change the whole binary-operator expression AND one its operands:
1062 class AlwaysFail
: public transformer::MatchComputation
<std::string
> {
1063 llvm::Error
eval(const ast_matchers::MatchFinder::MatchResult
&,
1064 std::string
*) const override
{
1065 return llvm::createStringError(llvm::errc::invalid_argument
, "ERROR");
1067 std::string
toString() const override
{ return "AlwaysFail"; }
1070 makeRule(binaryOperator().bind(O
),
1071 changeTo(node(std::string(O
)), std::make_shared
<AlwaysFail
>())),
1073 T
.registerMatchers(&MatchFinder
);
1074 EXPECT_FALSE(rewrite(Input
));
1075 EXPECT_THAT(Changes
, IsEmpty());
1076 EXPECT_EQ(ErrorCount
, 1);
1079 // Tests for a conflict in edits from a single match for a rule.
1080 TEST_F(TransformerTest
, OverlappingEditsInRule
) {
1081 std::string Input
= "int conflictOneRule() { return 3 + 7; }";
1082 // Try to change the whole binary-operator expression AND one its operands:
1083 StringRef O
= "O", L
= "L";
1084 Transformer
T(makeRule(binaryOperator(hasLHS(expr().bind(L
))).bind(O
),
1085 {changeTo(node(std::string(O
)), cat("DELETE_OP")),
1086 changeTo(node(std::string(L
)), cat("DELETE_LHS"))}),
1088 T
.registerMatchers(&MatchFinder
);
1089 EXPECT_FALSE(rewrite(Input
));
1090 EXPECT_THAT(Changes
, IsEmpty());
1091 EXPECT_EQ(ErrorCount
, 1);
1094 // Tests for a conflict in edits across multiple matches (of the same rule).
1095 TEST_F(TransformerTest
, OverlappingEditsMultipleMatches
) {
1096 std::string Input
= "int conflictOneRule() { return -7; }";
1097 // Try to change the whole binary-operator expression AND one its operands:
1099 Transformer
T(makeRule(expr().bind(E
),
1100 changeTo(node(std::string(E
)), cat("DELETE_EXPR"))),
1102 T
.registerMatchers(&MatchFinder
);
1103 // The rewrite process fails because the changes conflict with each other...
1104 EXPECT_FALSE(rewrite(Input
));
1105 // ... but two changes were produced.
1106 EXPECT_EQ(Changes
.size(), 2u);
1107 EXPECT_EQ(ErrorCount
, 0);
1110 TEST_F(TransformerTest
, ErrorOccurredMatchSkipped
) {
1111 // Syntax error in the function body:
1112 std::string Input
= "void errorOccurred() { 3 }";
1113 Transformer
T(makeRule(functionDecl(hasName("errorOccurred")),
1114 changeTo(cat("DELETED;"))),
1116 T
.registerMatchers(&MatchFinder
);
1117 // The rewrite process itself fails...
1118 EXPECT_FALSE(rewrite(Input
));
1119 // ... and no changes or errors are produced in the process.
1120 EXPECT_THAT(Changes
, IsEmpty());
1121 EXPECT_EQ(ErrorCount
, 0);
1124 TEST_F(TransformerTest
, ImplicitNodes_ConstructorDecl
) {
1126 std::string OtherStructPrefix
= R
"cpp(
1129 std::string OtherStructSuffix
= "};";
1131 std::string CopyableStructName
= "struct Copyable";
1132 std::string BrokenStructName
= "struct explicit Copyable";
1134 std::string CodeSuffix
= R
"cpp(
1141 std::string CopyCtor
= "Other(const Other&) = default;";
1142 std::string ExplicitCopyCtor
= "explicit Other(const Other&) = default;";
1143 std::string BrokenExplicitCopyCtor
=
1144 "explicit explicit explicit Other(const Other&) = default;";
1146 std::string RewriteInput
= OtherStructPrefix
+ CopyCtor
+ OtherStructSuffix
+
1147 CopyableStructName
+ CodeSuffix
;
1148 std::string ExpectedRewriteOutput
= OtherStructPrefix
+ ExplicitCopyCtor
+
1149 OtherStructSuffix
+ CopyableStructName
+
1151 std::string BrokenRewriteOutput
= OtherStructPrefix
+ BrokenExplicitCopyCtor
+
1152 OtherStructSuffix
+ BrokenStructName
+
1155 auto MatchedRecord
=
1156 cxxConstructorDecl(isCopyConstructor()).bind("copyConstructor");
1159 changeTo(before(node("copyConstructor")), cat("explicit "));
1161 testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource
, MatchedRecord
),
1163 RewriteInput
, ExpectedRewriteOutput
);
1165 testRule(makeRule(traverse(TK_AsIs
, MatchedRecord
), RewriteRule
),
1166 RewriteInput
, BrokenRewriteOutput
);
1169 TEST_F(TransformerTest
, ImplicitNodes_RangeFor
) {
1171 std::string CodePrefix
= R
"cpp(
1176 int* cbegin() const;
1185 std::string BeginCallBefore
= " c.begin();";
1186 std::string BeginCallAfter
= " c.cbegin();";
1188 std::string ForLoop
= "for (auto i : c)";
1189 std::string BrokenForLoop
= "for (auto i :.cbegin() c)";
1191 std::string CodeSuffix
= R
"cpp(
1197 std::string RewriteInput
=
1198 CodePrefix
+ BeginCallBefore
+ ForLoop
+ CodeSuffix
;
1199 std::string ExpectedRewriteOutput
=
1200 CodePrefix
+ BeginCallAfter
+ ForLoop
+ CodeSuffix
;
1201 std::string BrokenRewriteOutput
=
1202 CodePrefix
+ BeginCallAfter
+ BrokenForLoop
+ CodeSuffix
;
1204 auto MatchedRecord
=
1205 cxxMemberCallExpr(on(expr(hasType(qualType(isConstQualified(),
1206 hasDeclaration(cxxRecordDecl(
1207 hasName("Container"))))))
1208 .bind("callTarget")),
1209 callee(cxxMethodDecl(hasName("begin"))))
1210 .bind("constBeginCall");
1213 changeTo(node("constBeginCall"), cat(name("callTarget"), ".cbegin()"));
1215 testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource
, MatchedRecord
),
1217 RewriteInput
, ExpectedRewriteOutput
);
1219 testRule(makeRule(traverse(TK_AsIs
, MatchedRecord
), RewriteRule
),
1220 RewriteInput
, BrokenRewriteOutput
);
1223 TEST_F(TransformerTest
, ImplicitNodes_ForStmt
) {
1225 std::string CodePrefix
= R
"cpp(
1228 NonTrivial(NonTrivial&) {}
1229 NonTrivial& operator=(NonTrivial const&) { return *this; }
1234 struct ContainsArray {
1236 ContainsArray& operator=(ContainsArray const&) = default;
1246 auto CodeSuffix
= "}";
1248 auto LoopBody
= R
"cpp(
1254 auto RawLoop
= "for (auto i = 0; i != 5; ++i)";
1256 auto RangeLoop
= "for (auto i : boost::irange(5))";
1258 // Expect to rewrite the raw loop to the ranged loop.
1259 // This works in TK_IgnoreUnlessSpelledInSource mode, but TK_AsIs
1260 // mode also matches the hidden for loop generated in the copy assignment
1261 // operator of ContainsArray. Transformer then fails to transform the code at
1265 CodePrefix
+ RawLoop
+ LoopBody
+ RawLoop
+ LoopBody
+ CodeSuffix
;
1267 auto RewriteOutput
=
1268 CodePrefix
+ RangeLoop
+ LoopBody
+ RangeLoop
+ LoopBody
+ CodeSuffix
;
1270 auto MatchedLoop
= forStmt(
1271 has(declStmt(hasSingleDecl(
1272 varDecl(hasInitializer(integerLiteral(equals(0)))).bind("loopVar")))),
1273 has(binaryOperator(hasOperatorName("!="),
1274 hasLHS(ignoringImplicit(declRefExpr(
1275 to(varDecl(equalsBoundNode("loopVar")))))),
1276 hasRHS(expr().bind("upperBoundExpr")))),
1277 has(unaryOperator(hasOperatorName("++"),
1278 hasUnaryOperand(declRefExpr(
1279 to(varDecl(equalsBoundNode("loopVar"))))))
1280 .bind("incrementOp")));
1283 changeTo(transformer::enclose(node("loopVar"), node("incrementOp")),
1284 cat("auto ", name("loopVar"), " : boost::irange(",
1285 node("upperBoundExpr"), ")"));
1287 testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource
, MatchedLoop
),
1289 RewriteInput
, RewriteOutput
);
1291 testRuleFailure(makeRule(traverse(TK_AsIs
, MatchedLoop
), RewriteRule
),
1295 TEST_F(TransformerTest
, ImplicitNodes_ForStmt2
) {
1297 std::string CodePrefix
= R
"cpp(
1300 NonTrivial(NonTrivial&) {}
1301 NonTrivial& operator=(NonTrivial const&) { return *this; }
1306 struct ContainsArray {
1308 ContainsArray& operator=(ContainsArray const&) = default;
1318 auto CodeSuffix
= "}";
1320 auto LoopBody
= R
"cpp(
1326 auto RawLoop
= "for (auto i = 0; i != 5; ++i)";
1328 auto RangeLoop
= "for (auto i : boost::irange(5))";
1330 // Expect to rewrite the raw loop to the ranged loop.
1331 // This works in TK_IgnoreUnlessSpelledInSource mode, but TK_AsIs
1332 // mode also matches the hidden for loop generated in the copy assignment
1333 // operator of ContainsArray. Transformer then fails to transform the code at
1337 CodePrefix
+ RawLoop
+ LoopBody
+ RawLoop
+ LoopBody
+ CodeSuffix
;
1339 auto RewriteOutput
=
1340 CodePrefix
+ RangeLoop
+ LoopBody
+ RangeLoop
+ LoopBody
+ CodeSuffix
;
1341 auto MatchedLoop
= forStmt(
1342 hasLoopInit(declStmt(hasSingleDecl(
1343 varDecl(hasInitializer(integerLiteral(equals(0)))).bind("loopVar")))),
1344 hasCondition(binaryOperator(hasOperatorName("!="),
1345 hasLHS(ignoringImplicit(declRefExpr(to(
1346 varDecl(equalsBoundNode("loopVar")))))),
1347 hasRHS(expr().bind("upperBoundExpr")))),
1348 hasIncrement(unaryOperator(hasOperatorName("++"),
1349 hasUnaryOperand(declRefExpr(
1350 to(varDecl(equalsBoundNode("loopVar"))))))
1351 .bind("incrementOp")));
1354 changeTo(transformer::enclose(node("loopVar"), node("incrementOp")),
1355 cat("auto ", name("loopVar"), " : boost::irange(",
1356 node("upperBoundExpr"), ")"));
1358 testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource
, MatchedLoop
),
1360 RewriteInput
, RewriteOutput
);
1362 testRuleFailure(makeRule(traverse(TK_AsIs
, MatchedLoop
), RewriteRule
),
1366 TEST_F(TransformerTest
, TemplateInstantiation
) {
1368 std::string NonTemplatesInput
= R
"cpp(
1373 std::string NonTemplatesExpected
= R
"cpp(
1379 std::string TemplatesInput
= R
"cpp(
1380 template<typename T>
1381 struct TemplStruct {
1391 TemplStruct<int> ti;
1395 auto MatchedField
= fieldDecl(hasType(asString("int"))).bind("theField");
1397 // Changes the 'int' in 'S', but not the 'T' in 'TemplStruct':
1398 testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource
, MatchedField
),
1399 changeTo(cat("safe_int ", name("theField"), ";"))),
1400 NonTemplatesInput
+ TemplatesInput
,
1401 NonTemplatesExpected
+ TemplatesInput
);
1403 // In AsIs mode, template instantiations are modified, which is
1404 // often not desired:
1406 std::string IncorrectTemplatesExpected
= R
"cpp(
1407 template<typename T>
1408 struct TemplStruct {
1418 TemplStruct<int> ti;
1422 // Changes the 'int' in 'S', and (incorrectly) the 'T' in 'TemplStruct':
1423 testRule(makeRule(traverse(TK_AsIs
, MatchedField
),
1424 changeTo(cat("safe_int ", name("theField"), ";"))),
1426 NonTemplatesInput
+ TemplatesInput
,
1427 NonTemplatesExpected
+ IncorrectTemplatesExpected
);
1430 // Transformation of macro source text when the change encompasses the entirety
1431 // of the expanded text.
1432 TEST_F(TransformerTest
, SimpleMacro
) {
1433 std::string Input
= R
"cc(
1435 int f(string s) { return ZERO; }
1437 std::string Expected
= R
"cc(
1439 int f(string s) { return 999; }
1442 StringRef zero
= "zero";
1443 RewriteRule R
= makeRule(integerLiteral(equals(0)).bind(zero
),
1444 changeTo(node(std::string(zero
)), cat("999")));
1445 testRule(R
, Input
, Expected
);
1448 // Transformation of macro source text when the change encompasses the entirety
1449 // of the expanded text, for the case of function-style macros.
1450 TEST_F(TransformerTest
, FunctionMacro
) {
1451 std::string Input
= R
"cc(
1452 #define MACRO(str) strlen((str).c_str())
1453 int f(string s) { return MACRO(s); }
1455 std::string Expected
= R
"cc(
1456 #define MACRO(str) strlen((str).c_str())
1457 int f(string s) { return REPLACED; }
1460 testRule(ruleStrlenSize(), Input
, Expected
);
1463 // Tests that expressions in macro arguments can be rewritten.
1464 TEST_F(TransformerTest
, MacroArg
) {
1465 std::string Input
= R
"cc(
1466 #define PLUS(e) e + 1
1467 int f(string s) { return PLUS(strlen(s.c_str())); }
1469 std::string Expected
= R
"cc(
1470 #define PLUS(e) e + 1
1471 int f(string s) { return PLUS(REPLACED); }
1474 testRule(ruleStrlenSize(), Input
, Expected
);
1477 // Tests that expressions in macro arguments can be rewritten, even when the
1478 // macro call occurs inside another macro's definition.
1479 TEST_F(TransformerTest
, MacroArgInMacroDef
) {
1480 std::string Input
= R
"cc(
1482 #define MACRO(str) NESTED(strlen((str).c_str()))
1483 int f(string s) { return MACRO(s); }
1485 std::string Expected
= R
"cc(
1487 #define MACRO(str) NESTED(strlen((str).c_str()))
1488 int f(string s) { return REPLACED; }
1491 testRule(ruleStrlenSize(), Input
, Expected
);
1494 // Tests the corner case of the identity macro, specifically that it is
1495 // discarded in the rewrite rather than preserved (like PLUS is preserved in the
1496 // previous test). This behavior is of dubious value (and marked with a FIXME
1497 // in the code), but we test it to verify (and demonstrate) how this case is
1499 TEST_F(TransformerTest
, IdentityMacro
) {
1500 std::string Input
= R
"cc(
1502 int f(string s) { return ID(strlen(s.c_str())); }
1504 std::string Expected
= R
"cc(
1506 int f(string s) { return REPLACED; }
1509 testRule(ruleStrlenSize(), Input
, Expected
);
1512 // Tests that two changes in a single macro expansion do not lead to conflicts
1513 // in applying the changes.
1514 TEST_F(TransformerTest
, TwoChangesInOneMacroExpansion
) {
1515 std::string Input
= R
"cc(
1516 #define PLUS(a,b) (a) + (b)
1517 int f() { return PLUS(3, 4); }
1519 std::string Expected
= R
"cc(
1520 #define PLUS(a,b) (a) + (b)
1521 int f() { return PLUS(LIT, LIT); }
1524 testRule(makeRule(integerLiteral(), changeTo(cat("LIT"))), Input
, Expected
);
1527 // Tests case where the rule's match spans both source from the macro and its
1528 // arg, with the begin location (the "anchor") being the arg.
1529 TEST_F(TransformerTest
, MatchSpansMacroTextButChangeDoesNot
) {
1530 std::string Input
= R
"cc(
1531 #define PLUS_ONE(a) a + 1
1532 int f() { return PLUS_ONE(3); }
1534 std::string Expected
= R
"cc(
1535 #define PLUS_ONE(a) a + 1
1536 int f() { return PLUS_ONE(LIT); }
1539 StringRef E
= "expr";
1540 testRule(makeRule(binaryOperator(hasLHS(expr().bind(E
))),
1541 changeTo(node(std::string(E
)), cat("LIT"))),
1545 // Tests case where the rule's match spans both source from the macro and its
1546 // arg, with the begin location (the "anchor") being inside the macro.
1547 TEST_F(TransformerTest
, MatchSpansMacroTextButChangeDoesNotAnchoredInMacro
) {
1548 std::string Input
= R
"cc(
1549 #define PLUS_ONE(a) 1 + a
1550 int f() { return PLUS_ONE(3); }
1552 std::string Expected
= R
"cc(
1553 #define PLUS_ONE(a) 1 + a
1554 int f() { return PLUS_ONE(LIT); }
1557 StringRef E
= "expr";
1558 testRule(makeRule(binaryOperator(hasRHS(expr().bind(E
))),
1559 changeTo(node(std::string(E
)), cat("LIT"))),
1563 // No rewrite is applied when the changed text does not encompass the entirety
1564 // of the expanded text. That is, the edit would have to be applied to the
1565 // macro's definition to succeed and editing the expansion point would not
1567 TEST_F(TransformerTest
, NoPartialRewriteOMacroExpansion
) {
1568 std::string Input
= R
"cc(
1569 #define ZERO_PLUS 0 + 3
1570 int f(string s) { return ZERO_PLUS; })cc";
1572 StringRef zero
= "zero";
1573 RewriteRule R
= makeRule(integerLiteral(equals(0)).bind(zero
),
1574 changeTo(node(std::string(zero
)), cat("0")));
1575 testRule(R
, Input
, Input
);
1578 // This test handles the corner case where a macro expands within another macro
1579 // to matching code, but that code is an argument to the nested macro call. A
1580 // simple check of isMacroArgExpansion() vs. isMacroBodyExpansion() will get
1581 // this wrong, and transform the code.
1582 TEST_F(TransformerTest
, NoPartialRewriteOfMacroExpansionForMacroArgs
) {
1583 std::string Input
= R
"cc(
1585 #define MACRO(str) 1 + NESTED(strlen((str).c_str()))
1586 int f(string s) { return MACRO(s); }
1589 testRule(ruleStrlenSize(), Input
, Input
);
1592 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
1593 // Verifies that `Type` and `QualType` are not allowed as top-level matchers in
1595 TEST(TransformerDeathTest
, OrderedRuleTypes
) {
1596 RewriteRule QualTypeRule
= makeRule(qualType(), changeTo(cat("Q")));
1597 EXPECT_DEATH(transformer::detail::buildMatchers(QualTypeRule
),
1598 "Matcher must be.*node matcher");
1600 RewriteRule TypeRule
= makeRule(arrayType(), changeTo(cat("T")));
1601 EXPECT_DEATH(transformer::detail::buildMatchers(TypeRule
),
1602 "Matcher must be.*node matcher");
1606 // Edits are able to span multiple files; in this case, a header and an
1607 // implementation file.
1608 TEST_F(TransformerTest
, MultipleFiles
) {
1609 std::string Header
= R
"cc(void RemoveThisFunction();)cc";
1610 std::string Source
= R
"cc(#include "input
.h
"
1611 void RemoveThisFunction();)cc";
1613 makeRule(functionDecl(hasName("RemoveThisFunction")), changeTo(cat(""))),
1615 T
.registerMatchers(&MatchFinder
);
1616 auto Factory
= newFrontendActionFactory(&MatchFinder
);
1617 EXPECT_TRUE(runToolOnCodeWithArgs(
1618 Factory
->create(), Source
, std::vector
<std::string
>(), "input.cc",
1619 "clang-tool", std::make_shared
<PCHContainerOperations
>(),
1620 {{"input.h", Header
}}));
1622 llvm::sort(Changes
, [](const AtomicChange
&L
, const AtomicChange
&R
) {
1623 return L
.getFilePath() < R
.getFilePath();
1626 ASSERT_EQ(llvm::sys::path::convert_to_slash(Changes
[0].getFilePath()),
1628 EXPECT_THAT(Changes
[0].getInsertedHeaders(), IsEmpty());
1629 EXPECT_THAT(Changes
[0].getRemovedHeaders(), IsEmpty());
1630 llvm::Expected
<std::string
> UpdatedCode
=
1631 clang::tooling::applyAllReplacements(Header
,
1632 Changes
[0].getReplacements());
1633 ASSERT_TRUE(static_cast<bool>(UpdatedCode
))
1634 << "Could not update code: " << llvm::toString(UpdatedCode
.takeError());
1635 EXPECT_EQ(format(*UpdatedCode
), "");
1637 ASSERT_EQ(Changes
[1].getFilePath(), "input.cc");
1638 EXPECT_THAT(Changes
[1].getInsertedHeaders(), IsEmpty());
1639 EXPECT_THAT(Changes
[1].getRemovedHeaders(), IsEmpty());
1640 UpdatedCode
= clang::tooling::applyAllReplacements(
1641 Source
, Changes
[1].getReplacements());
1642 ASSERT_TRUE(static_cast<bool>(UpdatedCode
))
1643 << "Could not update code: " << llvm::toString(UpdatedCode
.takeError());
1644 EXPECT_EQ(format(*UpdatedCode
), format("#include \"input.h\"\n"));
1647 TEST_F(TransformerTest
, AddIncludeMultipleFiles
) {
1648 std::string Header
= R
"cc(void RemoveThisFunction();)cc";
1649 std::string Source
= R
"cc(#include "input
.h
"
1650 void Foo() {RemoveThisFunction();})cc";
1652 makeRule(callExpr(callee(
1653 functionDecl(hasName("RemoveThisFunction")).bind("fun"))),
1654 addInclude(node("fun"), "header.h")),
1656 T
.registerMatchers(&MatchFinder
);
1657 auto Factory
= newFrontendActionFactory(&MatchFinder
);
1658 EXPECT_TRUE(runToolOnCodeWithArgs(
1659 Factory
->create(), Source
, std::vector
<std::string
>(), "input.cc",
1660 "clang-tool", std::make_shared
<PCHContainerOperations
>(),
1661 {{"input.h", Header
}}));
1663 ASSERT_EQ(Changes
.size(), 1U);
1664 ASSERT_EQ(llvm::sys::path::convert_to_slash(Changes
[0].getFilePath()),
1666 EXPECT_THAT(Changes
[0].getInsertedHeaders(), ElementsAre("header.h"));
1667 EXPECT_THAT(Changes
[0].getRemovedHeaders(), IsEmpty());
1668 llvm::Expected
<std::string
> UpdatedCode
=
1669 clang::tooling::applyAllReplacements(Header
,
1670 Changes
[0].getReplacements());
1671 ASSERT_TRUE(static_cast<bool>(UpdatedCode
))
1672 << "Could not update code: " << llvm::toString(UpdatedCode
.takeError());
1673 EXPECT_EQ(format(*UpdatedCode
), format(Header
));
1676 // A single change set can span multiple files.
1677 TEST_F(TransformerTest
, MultiFileEdit
) {
1678 // NB: The fixture is unused for this test, but kept for the test suite name.
1679 std::string Header
= R
"cc(void Func(int id);)cc";
1680 std::string Source
= R
"cc(#include "input
.h
"
1686 std::vector
<AtomicChanges
> ChangeSets
;
1687 clang::ast_matchers::MatchFinder MatchFinder
;
1689 makeRule(callExpr(callee(functionDecl(hasName("Func"))),
1690 forEachArgumentWithParam(expr().bind("arg"),
1691 parmVarDecl().bind("param"))),
1692 {changeTo(node("arg"), cat("ARG")),
1693 changeTo(node("param"), cat("PARAM"))}),
1694 [&](Expected
<MutableArrayRef
<AtomicChange
>> Changes
) {
1696 ChangeSets
.push_back(AtomicChanges(Changes
->begin(), Changes
->end()));
1700 T
.registerMatchers(&MatchFinder
);
1701 auto Factory
= newFrontendActionFactory(&MatchFinder
);
1702 EXPECT_TRUE(runToolOnCodeWithArgs(
1703 Factory
->create(), Source
, std::vector
<std::string
>(), "input.cc",
1704 "clang-tool", std::make_shared
<PCHContainerOperations
>(),
1705 {{"input.h", Header
}}));
1707 auto GetPathWithSlashes
= [](const AtomicChange
&C
) {
1708 return llvm::sys::path::convert_to_slash(C
.getFilePath());
1711 EXPECT_EQ(ErrorCount
, 0);
1712 EXPECT_THAT(ChangeSets
, UnorderedElementsAre(UnorderedElementsAre(
1713 ResultOf(GetPathWithSlashes
, "input.cc"),
1714 ResultOf(GetPathWithSlashes
, "./input.h"))));
1717 TEST_F(TransformerTest
, GeneratesMetadata
) {
1718 std::string Input
= R
"cc(int target = 0;)cc";
1719 std::string Expected
= R
"cc(REPLACE)cc";
1720 RewriteRuleWith
<std::string
> Rule
= makeRule(
1721 varDecl(hasName("target")), changeTo(cat("REPLACE")), cat("METADATA"));
1722 testRule(std::move(Rule
), Input
, Expected
);
1723 EXPECT_EQ(ErrorCount
, 0);
1724 EXPECT_THAT(StringMetadata
, UnorderedElementsAre("METADATA"));
1727 TEST_F(TransformerTest
, GeneratesMetadataWithNoEdits
) {
1728 std::string Input
= R
"cc(int target = 0;)cc";
1729 RewriteRuleWith
<std::string
> Rule
= makeRule(
1730 varDecl(hasName("target")).bind("var"), noEdits(), cat("METADATA"));
1731 testRule(std::move(Rule
), Input
, Input
);
1732 EXPECT_EQ(ErrorCount
, 0);
1733 EXPECT_THAT(StringMetadata
, UnorderedElementsAre("METADATA"));
1736 TEST_F(TransformerTest
, PropagateMetadataErrors
) {
1737 class AlwaysFail
: public transformer::MatchComputation
<std::string
> {
1738 llvm::Error
eval(const ast_matchers::MatchFinder::MatchResult
&,
1739 std::string
*) const override
{
1740 return llvm::createStringError(llvm::errc::invalid_argument
, "ERROR");
1742 std::string
toString() const override
{ return "AlwaysFail"; }
1744 std::string Input
= R
"cc(int target = 0;)cc";
1745 RewriteRuleWith
<std::string
> Rule
= makeRule
<std::string
>(
1746 varDecl(hasName("target")).bind("var"), changeTo(cat("REPLACE")),
1747 std::make_shared
<AlwaysFail
>());
1748 testRuleFailure(std::move(Rule
), Input
);
1749 EXPECT_EQ(ErrorCount
, 1);