brintos

brintos / llvm-project-archived public Read only

0
0
Text · 56.9 KiB · cbd84ab Raw
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