1753 lines · cpp
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-exception6//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/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"20#include <optional>21 22using namespace clang;23using namespace tooling;24using namespace ast_matchers;25namespace {26using ::clang::transformer::addInclude;27using ::clang::transformer::applyFirst;28using ::clang::transformer::before;29using ::clang::transformer::cat;30using ::clang::transformer::changeTo;31using ::clang::transformer::editList;32using ::clang::transformer::makeRule;33using ::clang::transformer::member;34using ::clang::transformer::name;35using ::clang::transformer::node;36using ::clang::transformer::noEdits;37using ::clang::transformer::remove;38using ::clang::transformer::rewriteDescendants;39using ::clang::transformer::RewriteRule;40using ::clang::transformer::RewriteRuleWith;41using ::clang::transformer::statement;42using ::testing::ElementsAre;43using ::testing::IsEmpty;44using ::testing::ResultOf;45using ::testing::UnorderedElementsAre;46 47constexpr char KHeaderContents[] = R"cc(48 struct string {49 string(const char*);50 char* c_str();51 int size();52 };53 int strlen(const char*);54 55 namespace proto {56 struct PCFProto {57 int foo();58 };59 struct ProtoCommandLineFlag : PCFProto {60 PCFProto& GetProto();61 };62 } // namespace proto63 class Logger {};64 void operator<<(Logger& l, string msg);65 Logger& log(int level);66)cc";67 68static ast_matchers::internal::Matcher<clang::QualType>69isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {70 return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));71}72 73static 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);78 if (!Formatted) {79 ADD_FAILURE() << "Could not format code: "80 << llvm::toString(Formatted.takeError());81 return std::string();82 }83 return *Formatted;84}85 86static 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));95}96 97// FIXME: consider separating this class into its own file(s).98class ClangRefactoringTestBase : public testing::Test {99protected:100 void appendToHeader(StringRef S) { FileContents[0].second += S; }101 102 void addFile(StringRef Filename, StringRef Content) {103 FileContents.emplace_back(std::string(Filename), std::string(Content));104 }105 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>(),112 FileContents)) {113 llvm::errs() << "Running tool failed.\n";114 return std::nullopt;115 }116 if (ErrorCount != 0) {117 llvm::errs() << "Generating changes failed.\n";118 return std::nullopt;119 }120 auto ChangedCode =121 applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());122 if (!ChangedCode) {123 llvm::errs() << "Applying changes failed: "124 << llvm::toString(ChangedCode.takeError()) << "\n";125 return std::nullopt;126 }127 return *ChangedCode;128 }129 130 Transformer::ChangeSetConsumer consumer() {131 return [this](Expected<MutableArrayRef<AtomicChange>> C) {132 if (C) {133 Changes.insert(Changes.end(), std::make_move_iterator(C->begin()),134 std::make_move_iterator(C->end()));135 } else {136 // FIXME: stash this error rather than printing.137 llvm::errs() << "Error generating changes: "138 << llvm::toString(C.takeError()) << "\n";139 ++ErrorCount;140 }141 };142 }143 144 auto consumerWithStringMetadata() {145 return [this](Expected<TransformerResult<std::string>> C) {146 if (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));151 } else {152 // FIXME: stash this error rather than printing.153 llvm::errs() << "Error generating changes: "154 << llvm::toString(C.takeError()) << "\n";155 ++ErrorCount;156 }157 };158 }159 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));165 }166 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));173 }174 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";180 }181 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";187 }188 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.193 int ErrorCount = 0;194 AtomicChanges Changes;195 std::vector<std::string> StringMetadata;196 197private:198 FileContentMappings FileContents = {{"header.h", ""}};199};200 201class TransformerTest : public ClangRefactoringTestBase {202protected:203 TransformerTest() { appendToHeader(KHeaderContents); }204};205 206// Given string s, change strlen($s.c_str()) to REPLACED.207static RewriteRuleWith<std::string> ruleStrlenSize() {208 StringRef StringExpr = "strexpr";209 auto StringType = namedDecl(hasAnyName("::basic_string", "::string"));210 auto R = makeRule(211 callExpr(callee(functionDecl(hasName("strlen"))),212 hasArgument(0, cxxMemberCallExpr(213 on(expr(hasType(isOrPointsTo(StringType)))214 .bind(StringExpr)),215 callee(cxxMethodDecl(hasName("c_str")))))),216 changeTo(cat("REPLACED")), cat("Use size() method directly on string."));217 return R;218}219 220TEST_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);224}225 226// Tests that no change is applied when a match is not expected.227TEST_F(TransformerTest, NoMatch) {228 std::string Input = "int f(string s) { return s.size(); }";229 testRule(ruleStrlenSize(), Input, Input);230}231 232// Tests replacing an expression.233TEST_F(TransformerTest, Flag) {234 StringRef Flag = "flag";235 RewriteRule Rule = makeRule(236 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(237 hasName("proto::ProtoCommandLineFlag"))))238 .bind(Flag)),239 unless(callee(cxxMethodDecl(hasName("GetProto"))))),240 changeTo(node(std::string(Flag)), cat("EXPR")));241 242 std::string Input = R"cc(243 proto::ProtoCommandLineFlag flag;244 int x = flag.foo();245 int y = flag.GetProto().foo();246 )cc";247 std::string Expected = R"cc(248 proto::ProtoCommandLineFlag flag;249 int x = EXPR.foo();250 int y = flag.GetProto().foo();251 )cc";252 253 testRule(std::move(Rule), Input, Expected);254}255 256TEST_F(TransformerTest, AddIncludeQuoted) {257 RewriteRule Rule =258 makeRule(callExpr(callee(functionDecl(hasName("f")))),259 {addInclude("clang/OtherLib.h"), changeTo(cat("other()"))});260 261 std::string Input = R"cc(262 int f(int x);263 int h(int x) { return f(x); }264 )cc";265 std::string Expected = R"cc(#include "clang/OtherLib.h"266 267 int f(int x);268 int h(int x) { return other(); }269 )cc";270 271 testRule(Rule, Input, Expected);272}273 274TEST_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()"))});279 280 std::string Input = R"cc(281 int f(int x);282 int h(int x) { return f(x); }283 )cc";284 std::string Expected = R"cc(#include <clang/OtherLib.h>285 286 int f(int x);287 int h(int x) { return other(); }288 )cc";289 290 testRule(Rule, Input, Expected);291}292 293TEST_F(TransformerTest, AddIncludeQuotedForRule) {294 RewriteRule Rule = makeRule(callExpr(callee(functionDecl(hasName("f")))),295 changeTo(cat("other()")));296 addInclude(Rule, "clang/OtherLib.h");297 298 std::string Input = R"cc(299 int f(int x);300 int h(int x) { return f(x); }301 )cc";302 std::string Expected = R"cc(#include "clang/OtherLib.h"303 304 int f(int x);305 int h(int x) { return other(); }306 )cc";307 308 testRule(Rule, Input, Expected);309}310 311TEST_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);315 316 std::string Input = R"cc(317 int f(int x);318 int h(int x) { return f(x); }319 )cc";320 std::string Expected = R"cc(#include <clang/OtherLib.h>321 322 int f(int x);323 int h(int x) { return other(); }324 )cc";325 326 testRule(Rule, Input, Expected);327}328 329TEST_F(TransformerTest, NodePartNameNamedDecl) {330 StringRef Fun = "fun";331 RewriteRule Rule = makeRule(functionDecl(hasName("bad")).bind(Fun),332 changeTo(name(std::string(Fun)), cat("good")));333 334 std::string Input = R"cc(335 int bad(int x);336 int bad(int x) { return x * x; }337 )cc";338 std::string Expected = R"cc(339 int good(int x);340 int good(int x) { return x * x; }341 )cc";342 343 testRule(Rule, Input, Expected);344}345 346TEST_F(TransformerTest, NodePartNameDeclRef) {347 std::string Input = R"cc(348 template <typename T>349 T bad(T x) {350 return x;351 }352 int neutral(int x) { return bad<int>(x) * x; }353 )cc";354 std::string Expected = R"cc(355 template <typename T>356 T bad(T x) {357 return x;358 }359 int neutral(int x) { return good<int>(x) * x; }360 )cc";361 362 StringRef Ref = "ref";363 testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref),364 changeTo(name(std::string(Ref)), cat("good"))),365 Input, Expected);366}367 368TEST_F(TransformerTest, NodePartNameDeclRefFailure) {369 std::string Input = R"cc(370 struct Y {371 int operator*();372 };373 int neutral(int x) {374 Y y;375 int (Y::*ptr)() = &Y::operator*;376 return *y + x;377 }378 )cc";379 380 StringRef Ref = "ref";381 Transformer T(makeRule(declRefExpr(to(functionDecl())).bind(Ref),382 changeTo(name(std::string(Ref)), cat("good"))),383 consumer());384 T.registerMatchers(&MatchFinder);385 EXPECT_FALSE(rewrite(Input));386}387 388TEST_F(TransformerTest, NodePartMember) {389 StringRef E = "expr";390 RewriteRule Rule =391 makeRule(memberExpr(clang::ast_matchers::member(hasName("bad"))).bind(E),392 changeTo(member(std::string(E)), cat("good")));393 394 std::string Input = R"cc(395 struct S {396 int bad;397 };398 int g() {399 S s;400 return s.bad;401 }402 )cc";403 std::string Expected = R"cc(404 struct S {405 int bad;406 };407 int g() {408 S s;409 return s.good;410 }411 )cc";412 413 testRule(Rule, Input, Expected);414}415 416TEST_F(TransformerTest, NodePartMemberQualified) {417 std::string Input = R"cc(418 struct S {419 int bad;420 int good;421 };422 struct T : public S {423 int bad;424 };425 int g() {426 T t;427 return t.S::bad;428 }429 )cc";430 std::string Expected = R"cc(431 struct S {432 int bad;433 int good;434 };435 struct T : public S {436 int bad;437 };438 int g() {439 T t;440 return t.S::good;441 }442 )cc";443 444 StringRef E = "expr";445 testRule(makeRule(memberExpr().bind(E),446 changeTo(member(std::string(E)), cat("good"))),447 Input, Expected);448}449 450TEST_F(TransformerTest, NodePartMemberMultiToken) {451 std::string Input = R"cc(452 struct Y {453 int operator*();454 int good();455 template <typename T> void foo(T t);456 };457 int neutral(int x) {458 Y y;459 y.template foo<int>(3);460 return y.operator *();461 }462 )cc";463 std::string Expected = R"cc(464 struct Y {465 int operator*();466 int good();467 template <typename T> void foo(T t);468 };469 int neutral(int x) {470 Y y;471 y.template good<int>(3);472 return y.good();473 }474 )cc";475 476 StringRef MemExpr = "member";477 testRule(makeRule(memberExpr().bind(MemExpr),478 changeTo(member(std::string(MemExpr)), cat("good"))),479 Input, Expected);480}481 482TEST_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);486}487 488TEST_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"))),493 Input, Input);494}495 496TEST_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;")))),502 Input, Expected);503}504 505TEST_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;")))),512 Input, Expected);513}514 515TEST_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")))520 .bind("function"),521 shrinkTo(node("function"), node("return"))),522 Input, Expected);523}524 525// Rewrite various Stmts inside a Decl.526TEST_F(TransformerTest, RewriteDescendantsDeclChangeStmt) {527 std::string Input =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; }";531 auto InlineX =532 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));533 testRule(makeRule(functionDecl(hasName("f")).bind("fun"),534 rewriteDescendants("fun", InlineX)),535 Input, Expected);536}537 538// Rewrite various TypeLocs inside a Decl.539TEST_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)),546 Input, Expected);547}548 549TEST_F(TransformerTest, RewriteDescendantsStmt) {550 // Add an unrelated definition to the header that also has a variable named551 // "x", to test that the rewrite is limited to the scope we intend.552 appendToHeader(R"cc(int g(int x) { return x; })cc");553 std::string Input =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; }";557 auto InlineX =558 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));559 testRule(makeRule(functionDecl(hasName("f"), hasBody(stmt().bind("body"))),560 rewriteDescendants("body", InlineX)),561 Input, Expected);562}563 564TEST_F(TransformerTest, RewriteDescendantsStmtWithAdditionalChange) {565 std::string Input =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; }";569 auto InlineX =570 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));571 testRule(572 makeRule(573 functionDecl(hasName("f"), hasBody(stmt().bind("body"))).bind("f"),574 flatten(changeTo(name("f"), cat("newName")),575 rewriteDescendants("body", InlineX))),576 Input, Expected);577}578 579TEST_F(TransformerTest, RewriteDescendantsTypeLoc) {580 std::string Input = "int f(int *x) { return *x; }";581 std::string Expected = "int f(char *x) { return *x; }";582 auto IntToChar =583 makeRule(typeLoc(loc(qualType(isInteger(), builtinType()))).bind("loc"),584 changeTo(cat("char")));585 testRule(586 makeRule(functionDecl(hasName("f"),587 hasParameter(0, varDecl(hasTypeLoc(588 typeLoc().bind("parmType"))))),589 rewriteDescendants("parmType", IntToChar)),590 Input, Expected);591}592 593TEST_F(TransformerTest, RewriteDescendantsReferToParentBinding) {594 std::string Input =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)))),600 changeTo(cat("3")));601 testRule(makeRule(functionDecl(hasName("f"),602 hasParameter(0, varDecl().bind(VarId)))603 .bind("fun"),604 rewriteDescendants("fun", InlineVar)),605 Input, Expected);606}607 608TEST_F(TransformerTest, RewriteDescendantsUnboundNode) {609 std::string Input =610 "int f(int x) { int y = x; { int z = x * x; } return x; }";611 auto InlineX =612 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));613 Transformer T(makeRule(functionDecl(hasName("f")),614 rewriteDescendants("UNBOUND", InlineX)),615 consumer());616 T.registerMatchers(&MatchFinder);617 EXPECT_FALSE(rewrite(Input));618 EXPECT_THAT(Changes, IsEmpty());619 EXPECT_EQ(ErrorCount, 1);620}621 622TEST_F(TransformerTest, RewriteDescendantsInvalidNodeType) {623 std::string Input =624 "int f(int x) { int y = x; { int z = x * x; } return x; }";625 auto IntToChar =626 makeRule(qualType(isInteger(), builtinType()), changeTo(cat("char")));627 Transformer T(628 makeRule(functionDecl(629 hasName("f"),630 hasParameter(0, varDecl(hasType(qualType().bind("type"))))),631 rewriteDescendants("type", IntToChar)),632 consumer());633 T.registerMatchers(&MatchFinder);634 EXPECT_FALSE(rewrite(Input));635 EXPECT_THAT(Changes, IsEmpty());636 EXPECT_EQ(ErrorCount, 1);637}638 639//640// We include one test per typed overload. We don't test extensively since that641// is already covered by the tests above.642//643 644TEST_F(TransformerTest, RewriteDescendantsTypedStmt) {645 // Add an unrelated definition to the header that also has a variable named646 // "x", to test that the rewrite is limited to the scope we intend.647 appendToHeader(R"cc(int g(int x) { return x; })cc");648 std::string Input =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; }";652 auto InlineX =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(659 *Node, InlineX, R);660 }),661 Input, Expected);662}663 664TEST_F(TransformerTest, RewriteDescendantsTypedDecl) {665 std::string Input =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; }";669 auto InlineX =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(676 *Node, InlineX, R);677 }),678 Input, Expected);679}680 681TEST_F(TransformerTest, RewriteDescendantsTypedTypeLoc) {682 std::string Input = "int f(int *x) { return *x; }";683 std::string Expected = "int f(char *x) { return *x; }";684 auto IntToChar =685 makeRule(typeLoc(loc(qualType(isInteger(), builtinType()))).bind("loc"),686 changeTo(cat("char")));687 testRule(688 makeRule(689 functionDecl(690 hasName("f"),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);696 }),697 Input, Expected);698}699 700TEST_F(TransformerTest, RewriteDescendantsTypedDynTyped) {701 // Add an unrelated definition to the header that also has a variable named702 // "x", to test that the rewrite is limited to the scope we intend.703 appendToHeader(R"cc(int g(int x) { return x; })cc");704 std::string Input =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; }";708 auto InlineX =709 makeRule(declRefExpr(to(varDecl(hasName("x")))), changeTo(cat("3")));710 testRule(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,716 InlineX, R);717 }),718 Input, Expected);719}720 721TEST_F(TransformerTest, InsertBeforeEdit) {722 std::string Input = R"cc(723 int f() {724 return 7;725 }726 )cc";727 std::string Expected = R"cc(728 int f() {729 int y = 3;730 return 7;731 }732 )cc";733 734 StringRef Ret = "return";735 testRule(736 makeRule(returnStmt().bind(Ret),737 insertBefore(statement(std::string(Ret)), cat("int y = 3;"))),738 Input, Expected);739}740 741TEST_F(TransformerTest, InsertAfterEdit) {742 std::string Input = R"cc(743 int f() {744 int x = 5;745 return 7;746 }747 )cc";748 std::string Expected = R"cc(749 int f() {750 int x = 5;751 int y = 3;752 return 7;753 }754 )cc";755 756 StringRef Decl = "decl";757 testRule(758 makeRule(declStmt().bind(Decl),759 insertAfter(statement(std::string(Decl)), cat("int y = 3;"))),760 Input, Expected);761}762 763TEST_F(TransformerTest, RemoveEdit) {764 std::string Input = R"cc(765 int f() {766 int x = 5;767 return 7;768 }769 )cc";770 std::string Expected = R"cc(771 int f() {772 return 7;773 }774 )cc";775 776 StringRef Decl = "decl";777 testRule(778 makeRule(declStmt().bind(Decl), remove(statement(std::string(Decl)))),779 Input, Expected);780}781 782TEST_F(TransformerTest, WithMetadata) {783 auto makeMetadata = [](const MatchFinder::MatchResult &R) -> llvm::Any {784 int N =785 R.Nodes.getNodeAs<IntegerLiteral>("int")->getValue().getLimitedValue();786 return N;787 };788 789 std::string Input = R"cc(790 int f() {791 int x = 5;792 return 7;793 }794 )cc";795 796 Transformer T(797 makeRule(798 declStmt(containsDeclaration(0, varDecl(hasInitializer(799 integerLiteral().bind("int")))))800 .bind("decl"),801 withMetadata(remove(statement(std::string("decl"))), makeMetadata)),802 consumer());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);812}813 814TEST_F(TransformerTest, MultiChange) {815 std::string Input = R"cc(816 void foo() {817 if (10 > 1.0)818 log(1) << "oh no!";819 else820 log(0) << "ok";821 }822 )cc";823 std::string Expected = R"(824 void foo() {825 if (true) { /* then */ }826 else { /* else */ }827 }828 )";829 830 StringRef C = "C", T = "T", E = "E";831 testRule(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 */ }"))}),837 Input, Expected);838}839 840TEST_F(TransformerTest, EditList) {841 std::string Input = R"cc(842 void foo() {843 if (10 > 1.0)844 log(1) << "oh no!";845 else846 log(0) << "ok";847 }848 )cc";849 std::string Expected = R"(850 void foo() {851 if (true) { /* then */ }852 else { /* else */ }853 }854 )";855 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 */ }"))})),864 Input, Expected);865}866 867TEST_F(TransformerTest, Flatten) {868 std::string Input = R"cc(869 void foo() {870 if (10 > 1.0)871 log(1) << "oh no!";872 else873 log(0) << "ok";874 }875 )cc";876 std::string Expected = R"(877 void foo() {878 if (true) { /* then */ }879 else { /* else */ }880 }881 )";882 883 StringRef C = "C", T = "T", E = "E";884 testRule(885 makeRule(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 */ }")))),891 Input, Expected);892}893 894TEST_F(TransformerTest, FlattenWithMixedArgs) {895 using clang::transformer::editList;896 std::string Input = R"cc(897 void foo() {898 if (10 > 1.0)899 log(1) << "oh no!";900 else901 log(0) << "ok";902 }903 )cc";904 std::string Expected = R"(905 void foo() {906 if (true) { /* then */ }907 else { /* else */ }908 }909 )";910 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 */ }"))}))),919 Input, Expected);920}921 922TEST_F(TransformerTest, OrderedRuleUnrelated) {923 StringRef Flag = "flag";924 RewriteRuleWith<std::string> FlagRule = makeRule(925 cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(926 hasName("proto::ProtoCommandLineFlag"))))927 .bind(Flag)),928 unless(callee(cxxMethodDecl(hasName("GetProto"))))),929 changeTo(node(std::string(Flag)), cat("PROTO")), cat(""));930 931 std::string Input = R"cc(932 proto::ProtoCommandLineFlag flag;933 int x = flag.foo();934 int y = flag.GetProto().foo();935 int f(string s) { return strlen(s.c_str()); }936 )cc";937 std::string Expected = R"cc(938 proto::ProtoCommandLineFlag flag;939 int x = PROTO.foo();940 int y = flag.GetProto().foo();941 int f(string s) { return REPLACED; }942 )cc";943 944 testRule(applyFirst({ruleStrlenSize(), FlagRule}), Input, Expected);945}946 947TEST_F(TransformerTest, OrderedRuleRelated) {948 std::string Input = R"cc(949 void f1();950 void f2();951 void call_f1() { f1(); }952 void call_f2() { f2(); }953 )cc";954 std::string Expected = R"cc(955 void f1();956 void f2();957 void call_f1() { REPLACE_F1; }958 void call_f2() { REPLACE_F1_OR_F2; }959 )cc";960 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);968}969 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.972TEST_F(TransformerTest, OrderedRuleRelatedSwapped) {973 std::string Input = R"cc(974 void f1();975 void f2();976 void call_f1() { f1(); }977 void call_f2() { f2(); }978 )cc";979 std::string Expected = R"cc(980 void f1();981 void f2();982 void call_f1() { REPLACE_F1_OR_F2; }983 void call_f2() { REPLACE_F1_OR_F2; }984 )cc";985 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);993}994 995// Verify that a set of rules whose matchers have different base kinds works996// properly, including that `applyFirst` produces multiple matchers. We test997// two different kinds of rules: Expr and Decl. We place the Decl rule in the998// middle to test that `buildMatchers` works even when the kinds aren't grouped999// together.1000TEST_F(TransformerTest, OrderedRuleMultipleKinds) {1001 std::string Input = R"cc(1002 void f1();1003 void f2();1004 void call_f1() { f1(); }1005 void call_f2() { f2(); }1006 )cc";1007 std::string Expected = R"cc(1008 void f1();1009 void DECL_RULE();1010 void call_f1() { REPLACE_F1; }1011 void call_f2() { REPLACE_F1_OR_F2; }1012 )cc";1013 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")));1022 1023 RewriteRule Rule = applyFirst({ReplaceF1, DeclRule, ReplaceF1OrF2});1024 EXPECT_EQ(transformer::detail::buildMatchers(Rule).size(), 2UL);1025 testRule(Rule, Input, Expected);1026}1027 1028// Verifies that a rule with a top-level matcher for an implicit node (like1029// `implicitCastExpr`) works correctly -- the implicit nodes are not skipped.1030TEST_F(TransformerTest, OrderedRuleImplicitMatched) {1031 std::string Input = R"cc(1032 void f1();1033 int f2();1034 void call_f1() { f1(); }1035 float call_f2() { return f2(); }1036 )cc";1037 std::string Expected = R"cc(1038 void f1();1039 int f2();1040 void call_f1() { REPLACE_F1; }1041 float call_f2() { return REPLACE_F2; }1042 )cc";1043 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);1051}1052 1053//1054// Negative tests (where we expect no transformation to occur).1055//1056 1057// Tests for a conflict in edits from a single match for a rule.1058TEST_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:1061 StringRef O = "O";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");1066 }1067 std::string toString() const override { return "AlwaysFail"; }1068 };1069 Transformer T(1070 makeRule(binaryOperator().bind(O),1071 changeTo(node(std::string(O)), std::make_shared<AlwaysFail>())),1072 consumer());1073 T.registerMatchers(&MatchFinder);1074 EXPECT_FALSE(rewrite(Input));1075 EXPECT_THAT(Changes, IsEmpty());1076 EXPECT_EQ(ErrorCount, 1);1077}1078 1079// Tests for a conflict in edits from a single match for a rule.1080TEST_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"))}),1087 consumer());1088 T.registerMatchers(&MatchFinder);1089 EXPECT_FALSE(rewrite(Input));1090 EXPECT_THAT(Changes, IsEmpty());1091 EXPECT_EQ(ErrorCount, 1);1092}1093 1094// Tests for a conflict in edits across multiple matches (of the same rule).1095TEST_F(TransformerTest, OverlappingEditsMultipleMatches) {1096 std::string Input = "int conflictOneRule() { return -7; }";1097 // Try to change the whole binary-operator expression AND one its operands:1098 StringRef E = "E";1099 Transformer T(makeRule(expr().bind(E),1100 changeTo(node(std::string(E)), cat("DELETE_EXPR"))),1101 consumer());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);1108}1109 1110TEST_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;"))),1115 consumer());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);1122}1123 1124TEST_F(TransformerTest, ImplicitNodes_ConstructorDecl) {1125 1126 std::string OtherStructPrefix = R"cpp(1127struct Other {1128)cpp";1129 std::string OtherStructSuffix = "};";1130 1131 std::string CopyableStructName = "struct Copyable";1132 std::string BrokenStructName = "struct explicit Copyable";1133 1134 std::string CodeSuffix = R"cpp(1135{1136 Other m_i;1137 Copyable();1138};1139)cpp";1140 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;";1145 1146 std::string RewriteInput = OtherStructPrefix + CopyCtor + OtherStructSuffix +1147 CopyableStructName + CodeSuffix;1148 std::string ExpectedRewriteOutput = OtherStructPrefix + ExplicitCopyCtor +1149 OtherStructSuffix + CopyableStructName +1150 CodeSuffix;1151 std::string BrokenRewriteOutput = OtherStructPrefix + BrokenExplicitCopyCtor +1152 OtherStructSuffix + BrokenStructName +1153 CodeSuffix;1154 1155 auto MatchedRecord =1156 cxxConstructorDecl(isCopyConstructor()).bind("copyConstructor");1157 1158 auto RewriteRule =1159 changeTo(before(node("copyConstructor")), cat("explicit "));1160 1161 testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedRecord),1162 RewriteRule),1163 RewriteInput, ExpectedRewriteOutput);1164 1165 testRule(makeRule(traverse(TK_AsIs, MatchedRecord), RewriteRule),1166 RewriteInput, BrokenRewriteOutput);1167}1168 1169TEST_F(TransformerTest, ImplicitNodes_RangeFor) {1170 1171 std::string CodePrefix = R"cpp(1172struct Container1173{1174 int* begin() const;1175 int* end() const;1176 int* cbegin() const;1177 int* cend() const;1178};1179 1180void foo()1181{1182 const Container c;1183)cpp";1184 1185 std::string BeginCallBefore = " c.begin();";1186 std::string BeginCallAfter = " c.cbegin();";1187 1188 std::string ForLoop = "for (auto i : c)";1189 std::string BrokenForLoop = "for (auto i :.cbegin() c)";1190 1191 std::string CodeSuffix = R"cpp(1192 {1193 }1194}1195)cpp";1196 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;1203 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");1211 1212 auto RewriteRule =1213 changeTo(node("constBeginCall"), cat(name("callTarget"), ".cbegin()"));1214 1215 testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedRecord),1216 RewriteRule),1217 RewriteInput, ExpectedRewriteOutput);1218 1219 testRule(makeRule(traverse(TK_AsIs, MatchedRecord), RewriteRule),1220 RewriteInput, BrokenRewriteOutput);1221}1222 1223TEST_F(TransformerTest, ImplicitNodes_ForStmt) {1224 1225 std::string CodePrefix = R"cpp(1226struct NonTrivial {1227 NonTrivial() {}1228 NonTrivial(NonTrivial&) {}1229 NonTrivial& operator=(NonTrivial const&) { return *this; }1230 1231 ~NonTrivial() {}1232};1233 1234struct ContainsArray {1235 NonTrivial arr[2];1236 ContainsArray& operator=(ContainsArray const&) = default;1237};1238 1239void testIt()1240{1241 ContainsArray ca1;1242 ContainsArray ca2;1243 ca2 = ca1;1244)cpp";1245 1246 auto CodeSuffix = "}";1247 1248 auto LoopBody = R"cpp(1249 {1250 1251 }1252)cpp";1253 1254 auto RawLoop = "for (auto i = 0; i != 5; ++i)";1255 1256 auto RangeLoop = "for (auto i : boost::irange(5))";1257 1258 // Expect to rewrite the raw loop to the ranged loop.1259 // This works in TK_IgnoreUnlessSpelledInSource mode, but TK_AsIs1260 // mode also matches the hidden for loop generated in the copy assignment1261 // operator of ContainsArray. Transformer then fails to transform the code at1262 // all.1263 1264 auto RewriteInput =1265 CodePrefix + RawLoop + LoopBody + RawLoop + LoopBody + CodeSuffix;1266 1267 auto RewriteOutput =1268 CodePrefix + RangeLoop + LoopBody + RangeLoop + LoopBody + CodeSuffix;1269 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")));1281 1282 auto RewriteRule =1283 changeTo(transformer::enclose(node("loopVar"), node("incrementOp")),1284 cat("auto ", name("loopVar"), " : boost::irange(",1285 node("upperBoundExpr"), ")"));1286 1287 testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedLoop),1288 RewriteRule),1289 RewriteInput, RewriteOutput);1290 1291 testRuleFailure(makeRule(traverse(TK_AsIs, MatchedLoop), RewriteRule),1292 RewriteInput);1293}1294 1295TEST_F(TransformerTest, ImplicitNodes_ForStmt2) {1296 1297 std::string CodePrefix = R"cpp(1298struct NonTrivial {1299 NonTrivial() {}1300 NonTrivial(NonTrivial&) {}1301 NonTrivial& operator=(NonTrivial const&) { return *this; }1302 1303 ~NonTrivial() {}1304};1305 1306struct ContainsArray {1307 NonTrivial arr[2];1308 ContainsArray& operator=(ContainsArray const&) = default;1309};1310 1311void testIt()1312{1313 ContainsArray ca1;1314 ContainsArray ca2;1315 ca2 = ca1;1316)cpp";1317 1318 auto CodeSuffix = "}";1319 1320 auto LoopBody = R"cpp(1321 {1322 1323 }1324)cpp";1325 1326 auto RawLoop = "for (auto i = 0; i != 5; ++i)";1327 1328 auto RangeLoop = "for (auto i : boost::irange(5))";1329 1330 // Expect to rewrite the raw loop to the ranged loop.1331 // This works in TK_IgnoreUnlessSpelledInSource mode, but TK_AsIs1332 // mode also matches the hidden for loop generated in the copy assignment1333 // operator of ContainsArray. Transformer then fails to transform the code at1334 // all.1335 1336 auto RewriteInput =1337 CodePrefix + RawLoop + LoopBody + RawLoop + LoopBody + CodeSuffix;1338 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")));1352 1353 auto RewriteRule =1354 changeTo(transformer::enclose(node("loopVar"), node("incrementOp")),1355 cat("auto ", name("loopVar"), " : boost::irange(",1356 node("upperBoundExpr"), ")"));1357 1358 testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedLoop),1359 RewriteRule),1360 RewriteInput, RewriteOutput);1361 1362 testRuleFailure(makeRule(traverse(TK_AsIs, MatchedLoop), RewriteRule),1363 RewriteInput);1364}1365 1366TEST_F(TransformerTest, TemplateInstantiation) {1367 1368 std::string NonTemplatesInput = R"cpp(1369struct S {1370 int m_i;1371};1372)cpp";1373 std::string NonTemplatesExpected = R"cpp(1374struct S {1375 safe_int m_i;1376};1377)cpp";1378 1379 std::string TemplatesInput = R"cpp(1380template<typename T>1381struct TemplStruct {1382 TemplStruct() {}1383 ~TemplStruct() {}1384 1385private:1386 T m_t;1387};1388 1389void instantiate()1390{1391 TemplStruct<int> ti;1392}1393)cpp";1394 1395 auto MatchedField = fieldDecl(hasType(asString("int"))).bind("theField");1396 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);1402 1403 // In AsIs mode, template instantiations are modified, which is1404 // often not desired:1405 1406 std::string IncorrectTemplatesExpected = R"cpp(1407template<typename T>1408struct TemplStruct {1409 TemplStruct() {}1410 ~TemplStruct() {}1411 1412private:1413 safe_int m_t;1414};1415 1416void instantiate()1417{1418 TemplStruct<int> ti;1419}1420)cpp";1421 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"), ";"))),1425 1426 NonTemplatesInput + TemplatesInput,1427 NonTemplatesExpected + IncorrectTemplatesExpected);1428}1429 1430// Transformation of macro source text when the change encompasses the entirety1431// of the expanded text.1432TEST_F(TransformerTest, SimpleMacro) {1433 std::string Input = R"cc(1434#define ZERO 01435 int f(string s) { return ZERO; }1436 )cc";1437 std::string Expected = R"cc(1438#define ZERO 01439 int f(string s) { return 999; }1440 )cc";1441 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);1446}1447 1448// Transformation of macro source text when the change encompasses the entirety1449// of the expanded text, for the case of function-style macros.1450TEST_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); }1454 )cc";1455 std::string Expected = R"cc(1456#define MACRO(str) strlen((str).c_str())1457 int f(string s) { return REPLACED; }1458 )cc";1459 1460 testRule(ruleStrlenSize(), Input, Expected);1461}1462 1463// Tests that expressions in macro arguments can be rewritten.1464TEST_F(TransformerTest, MacroArg) {1465 std::string Input = R"cc(1466#define PLUS(e) e + 11467 int f(string s) { return PLUS(strlen(s.c_str())); }1468 )cc";1469 std::string Expected = R"cc(1470#define PLUS(e) e + 11471 int f(string s) { return PLUS(REPLACED); }1472 )cc";1473 1474 testRule(ruleStrlenSize(), Input, Expected);1475}1476 1477// Tests that expressions in macro arguments can be rewritten, even when the1478// macro call occurs inside another macro's definition.1479TEST_F(TransformerTest, MacroArgInMacroDef) {1480 std::string Input = R"cc(1481#define NESTED(e) e1482#define MACRO(str) NESTED(strlen((str).c_str()))1483 int f(string s) { return MACRO(s); }1484 )cc";1485 std::string Expected = R"cc(1486#define NESTED(e) e1487#define MACRO(str) NESTED(strlen((str).c_str()))1488 int f(string s) { return REPLACED; }1489 )cc";1490 1491 testRule(ruleStrlenSize(), Input, Expected);1492}1493 1494// Tests the corner case of the identity macro, specifically that it is1495// discarded in the rewrite rather than preserved (like PLUS is preserved in the1496// previous test). This behavior is of dubious value (and marked with a FIXME1497// in the code), but we test it to verify (and demonstrate) how this case is1498// handled.1499TEST_F(TransformerTest, IdentityMacro) {1500 std::string Input = R"cc(1501#define ID(e) e1502 int f(string s) { return ID(strlen(s.c_str())); }1503 )cc";1504 std::string Expected = R"cc(1505#define ID(e) e1506 int f(string s) { return REPLACED; }1507 )cc";1508 1509 testRule(ruleStrlenSize(), Input, Expected);1510}1511 1512// Tests that two changes in a single macro expansion do not lead to conflicts1513// in applying the changes.1514TEST_F(TransformerTest, TwoChangesInOneMacroExpansion) {1515 std::string Input = R"cc(1516#define PLUS(a,b) (a) + (b)1517 int f() { return PLUS(3, 4); }1518 )cc";1519 std::string Expected = R"cc(1520#define PLUS(a,b) (a) + (b)1521 int f() { return PLUS(LIT, LIT); }1522 )cc";1523 1524 testRule(makeRule(integerLiteral(), changeTo(cat("LIT"))), Input, Expected);1525}1526 1527// Tests case where the rule's match spans both source from the macro and its1528// arg, with the begin location (the "anchor") being the arg.1529TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNot) {1530 std::string Input = R"cc(1531#define PLUS_ONE(a) a + 11532 int f() { return PLUS_ONE(3); }1533 )cc";1534 std::string Expected = R"cc(1535#define PLUS_ONE(a) a + 11536 int f() { return PLUS_ONE(LIT); }1537 )cc";1538 1539 StringRef E = "expr";1540 testRule(makeRule(binaryOperator(hasLHS(expr().bind(E))),1541 changeTo(node(std::string(E)), cat("LIT"))),1542 Input, Expected);1543}1544 1545// Tests case where the rule's match spans both source from the macro and its1546// arg, with the begin location (the "anchor") being inside the macro.1547TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNotAnchoredInMacro) {1548 std::string Input = R"cc(1549#define PLUS_ONE(a) 1 + a1550 int f() { return PLUS_ONE(3); }1551 )cc";1552 std::string Expected = R"cc(1553#define PLUS_ONE(a) 1 + a1554 int f() { return PLUS_ONE(LIT); }1555 )cc";1556 1557 StringRef E = "expr";1558 testRule(makeRule(binaryOperator(hasRHS(expr().bind(E))),1559 changeTo(node(std::string(E)), cat("LIT"))),1560 Input, Expected);1561}1562 1563// No rewrite is applied when the changed text does not encompass the entirety1564// of the expanded text. That is, the edit would have to be applied to the1565// macro's definition to succeed and editing the expansion point would not1566// suffice.1567TEST_F(TransformerTest, NoPartialRewriteOMacroExpansion) {1568 std::string Input = R"cc(1569#define ZERO_PLUS 0 + 31570 int f(string s) { return ZERO_PLUS; })cc";1571 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);1576}1577 1578// This test handles the corner case where a macro expands within another macro1579// to matching code, but that code is an argument to the nested macro call. A1580// simple check of isMacroArgExpansion() vs. isMacroBodyExpansion() will get1581// this wrong, and transform the code.1582TEST_F(TransformerTest, NoPartialRewriteOfMacroExpansionForMacroArgs) {1583 std::string Input = R"cc(1584#define NESTED(e) e1585#define MACRO(str) 1 + NESTED(strlen((str).c_str()))1586 int f(string s) { return MACRO(s); }1587 )cc";1588 1589 testRule(ruleStrlenSize(), Input, Input);1590}1591 1592#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST1593// Verifies that `Type` and `QualType` are not allowed as top-level matchers in1594// rules.1595TEST(TransformerDeathTest, OrderedRuleTypes) {1596 RewriteRule QualTypeRule = makeRule(qualType(), changeTo(cat("Q")));1597 EXPECT_DEATH(transformer::detail::buildMatchers(QualTypeRule),1598 "Matcher must be.*node matcher");1599 1600 RewriteRule TypeRule = makeRule(arrayType(), changeTo(cat("T")));1601 EXPECT_DEATH(transformer::detail::buildMatchers(TypeRule),1602 "Matcher must be.*node matcher");1603}1604#endif1605 1606// Edits are able to span multiple files; in this case, a header and an1607// implementation file.1608TEST_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";1612 Transformer T(1613 makeRule(functionDecl(hasName("RemoveThisFunction")), changeTo(cat(""))),1614 consumer());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}}));1621 1622 llvm::sort(Changes, [](const AtomicChange &L, const AtomicChange &R) {1623 return L.getFilePath() < R.getFilePath();1624 });1625 1626 ASSERT_EQ(llvm::sys::path::convert_to_slash(Changes[0].getFilePath()),1627 "./input.h");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), "");1636 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"));1645}1646 1647TEST_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";1651 Transformer T(1652 makeRule(callExpr(callee(1653 functionDecl(hasName("RemoveThisFunction")).bind("fun"))),1654 addInclude(node("fun"), "header.h")),1655 consumer());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}}));1662 1663 ASSERT_EQ(Changes.size(), 1U);1664 ASSERT_EQ(llvm::sys::path::convert_to_slash(Changes[0].getFilePath()),1665 "./input.h");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));1674}1675 1676// A single change set can span multiple files.1677TEST_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"1681 void Caller() {1682 int id = 0;1683 Func(id);1684 })cc";1685 int ErrorCount = 0;1686 std::vector<AtomicChanges> ChangeSets;1687 clang::ast_matchers::MatchFinder MatchFinder;1688 Transformer T(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) {1695 if (Changes)1696 ChangeSets.push_back(AtomicChanges(Changes->begin(), Changes->end()));1697 else1698 ++ErrorCount;1699 });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}}));1706 1707 auto GetPathWithSlashes = [](const AtomicChange &C) {1708 return llvm::sys::path::convert_to_slash(C.getFilePath());1709 };1710 1711 EXPECT_EQ(ErrorCount, 0);1712 EXPECT_THAT(ChangeSets, UnorderedElementsAre(UnorderedElementsAre(1713 ResultOf(GetPathWithSlashes, "input.cc"),1714 ResultOf(GetPathWithSlashes, "./input.h"))));1715}1716 1717TEST_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"));1725}1726 1727TEST_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"));1734}1735 1736TEST_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");1741 }1742 std::string toString() const override { return "AlwaysFail"; }1743 };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);1750}1751 1752} // namespace1753