123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806 |
- //===- unittest/Tooling/TransformerTest.cpp -------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "clang/Tooling/Transformer/Transformer.h"
- #include "clang/ASTMatchers/ASTMatchers.h"
- #include "clang/Tooling/Transformer/RangeSelector.h"
- #include "clang/Tooling/Tooling.h"
- #include "llvm/Support/Errc.h"
- #include "llvm/Support/Error.h"
- #include "gmock/gmock.h"
- #include "gtest/gtest.h"
- using namespace clang;
- using namespace tooling;
- using namespace ast_matchers;
- namespace {
- using ::testing::IsEmpty;
- using transformer::RewriteRule;
- constexpr char KHeaderContents[] = R"cc(
- struct string {
- string(const char*);
- char* c_str();
- int size();
- };
- int strlen(const char*);
- namespace proto {
- struct PCFProto {
- int foo();
- };
- struct ProtoCommandLineFlag : PCFProto {
- PCFProto& GetProto();
- };
- } // namespace proto
- class Logger {};
- void operator<<(Logger& l, string msg);
- Logger& log(int level);
- )cc";
- static ast_matchers::internal::Matcher<clang::QualType>
- isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) {
- return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher));
- }
- static std::string format(StringRef Code) {
- const std::vector<Range> Ranges(1, Range(0, Code.size()));
- auto Style = format::getLLVMStyle();
- const auto Replacements = format::reformat(Style, Code, Ranges);
- auto Formatted = applyAllReplacements(Code, Replacements);
- if (!Formatted) {
- ADD_FAILURE() << "Could not format code: "
- << llvm::toString(Formatted.takeError());
- return std::string();
- }
- return *Formatted;
- }
- static void compareSnippets(StringRef Expected,
- const llvm::Optional<std::string> &MaybeActual) {
- ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected;
- auto Actual = *MaybeActual;
- std::string HL = "#include \"header.h\"\n";
- auto I = Actual.find(HL);
- if (I != std::string::npos)
- Actual.erase(I, HL.size());
- EXPECT_EQ(format(Expected), format(Actual));
- }
- // FIXME: consider separating this class into its own file(s).
- class ClangRefactoringTestBase : public testing::Test {
- protected:
- void appendToHeader(StringRef S) { FileContents[0].second += S; }
- void addFile(StringRef Filename, StringRef Content) {
- FileContents.emplace_back(Filename, Content);
- }
- llvm::Optional<std::string> rewrite(StringRef Input) {
- std::string Code = ("#include \"header.h\"\n" + Input).str();
- auto Factory = newFrontendActionFactory(&MatchFinder);
- if (!runToolOnCodeWithArgs(
- Factory->create(), Code, std::vector<std::string>(), "input.cc",
- "clang-tool", std::make_shared<PCHContainerOperations>(),
- FileContents)) {
- llvm::errs() << "Running tool failed.\n";
- return None;
- }
- if (ErrorCount != 0) {
- llvm::errs() << "Generating changes failed.\n";
- return None;
- }
- auto ChangedCode =
- applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec());
- if (!ChangedCode) {
- llvm::errs() << "Applying changes failed: "
- << llvm::toString(ChangedCode.takeError()) << "\n";
- return None;
- }
- return *ChangedCode;
- }
- Transformer::ChangeConsumer consumer() {
- return [this](Expected<AtomicChange> C) {
- if (C) {
- Changes.push_back(std::move(*C));
- } else {
- consumeError(C.takeError());
- ++ErrorCount;
- }
- };
- }
- template <typename R>
- void testRule(R Rule, StringRef Input, StringRef Expected) {
- Transformer T(std::move(Rule), consumer());
- T.registerMatchers(&MatchFinder);
- compareSnippets(Expected, rewrite(Input));
- }
- clang::ast_matchers::MatchFinder MatchFinder;
- // Records whether any errors occurred in individual changes.
- int ErrorCount = 0;
- AtomicChanges Changes;
- private:
- FileContentMappings FileContents = {{"header.h", ""}};
- };
- class TransformerTest : public ClangRefactoringTestBase {
- protected:
- TransformerTest() { appendToHeader(KHeaderContents); }
- };
- // Given string s, change strlen($s.c_str()) to REPLACED.
- static RewriteRule ruleStrlenSize() {
- StringRef StringExpr = "strexpr";
- auto StringType = namedDecl(hasAnyName("::basic_string", "::string"));
- auto R = makeRule(
- callExpr(callee(functionDecl(hasName("strlen"))),
- hasArgument(0, cxxMemberCallExpr(
- on(expr(hasType(isOrPointsTo(StringType)))
- .bind(StringExpr)),
- callee(cxxMethodDecl(hasName("c_str")))))),
- change(text("REPLACED")), text("Use size() method directly on string."));
- return R;
- }
- TEST_F(TransformerTest, StrlenSize) {
- std::string Input = "int f(string s) { return strlen(s.c_str()); }";
- std::string Expected = "int f(string s) { return REPLACED; }";
- testRule(ruleStrlenSize(), Input, Expected);
- }
- // Tests that no change is applied when a match is not expected.
- TEST_F(TransformerTest, NoMatch) {
- std::string Input = "int f(string s) { return s.size(); }";
- testRule(ruleStrlenSize(), Input, Input);
- }
- // Tests replacing an expression.
- TEST_F(TransformerTest, Flag) {
- StringRef Flag = "flag";
- RewriteRule Rule = makeRule(
- cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
- hasName("proto::ProtoCommandLineFlag"))))
- .bind(Flag)),
- unless(callee(cxxMethodDecl(hasName("GetProto"))))),
- change(node(Flag), text("EXPR")));
- std::string Input = R"cc(
- proto::ProtoCommandLineFlag flag;
- int x = flag.foo();
- int y = flag.GetProto().foo();
- )cc";
- std::string Expected = R"cc(
- proto::ProtoCommandLineFlag flag;
- int x = EXPR.foo();
- int y = flag.GetProto().foo();
- )cc";
- testRule(std::move(Rule), Input, Expected);
- }
- TEST_F(TransformerTest, AddIncludeQuoted) {
- RewriteRule Rule = makeRule(callExpr(callee(functionDecl(hasName("f")))),
- change(text("other()")));
- addInclude(Rule, "clang/OtherLib.h");
- std::string Input = R"cc(
- int f(int x);
- int h(int x) { return f(x); }
- )cc";
- std::string Expected = R"cc(#include "clang/OtherLib.h"
- int f(int x);
- int h(int x) { return other(); }
- )cc";
- testRule(Rule, Input, Expected);
- }
- TEST_F(TransformerTest, AddIncludeAngled) {
- RewriteRule Rule = makeRule(callExpr(callee(functionDecl(hasName("f")))),
- change(text("other()")));
- addInclude(Rule, "clang/OtherLib.h", transformer::IncludeFormat::Angled);
- std::string Input = R"cc(
- int f(int x);
- int h(int x) { return f(x); }
- )cc";
- std::string Expected = R"cc(#include <clang/OtherLib.h>
- int f(int x);
- int h(int x) { return other(); }
- )cc";
- testRule(Rule, Input, Expected);
- }
- TEST_F(TransformerTest, NodePartNameNamedDecl) {
- StringRef Fun = "fun";
- RewriteRule Rule = makeRule(functionDecl(hasName("bad")).bind(Fun),
- change(name(Fun), text("good")));
- std::string Input = R"cc(
- int bad(int x);
- int bad(int x) { return x * x; }
- )cc";
- std::string Expected = R"cc(
- int good(int x);
- int good(int x) { return x * x; }
- )cc";
- testRule(Rule, Input, Expected);
- }
- TEST_F(TransformerTest, NodePartNameDeclRef) {
- std::string Input = R"cc(
- template <typename T>
- T bad(T x) {
- return x;
- }
- int neutral(int x) { return bad<int>(x) * x; }
- )cc";
- std::string Expected = R"cc(
- template <typename T>
- T bad(T x) {
- return x;
- }
- int neutral(int x) { return good<int>(x) * x; }
- )cc";
- StringRef Ref = "ref";
- testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref),
- change(name(Ref), text("good"))),
- Input, Expected);
- }
- TEST_F(TransformerTest, NodePartNameDeclRefFailure) {
- std::string Input = R"cc(
- struct Y {
- int operator*();
- };
- int neutral(int x) {
- Y y;
- int (Y::*ptr)() = &Y::operator*;
- return *y + x;
- }
- )cc";
- StringRef Ref = "ref";
- Transformer T(makeRule(declRefExpr(to(functionDecl())).bind(Ref),
- change(name(Ref), text("good"))),
- consumer());
- T.registerMatchers(&MatchFinder);
- EXPECT_FALSE(rewrite(Input));
- }
- TEST_F(TransformerTest, NodePartMember) {
- StringRef E = "expr";
- RewriteRule Rule = makeRule(memberExpr(member(hasName("bad"))).bind(E),
- change(member(E), text("good")));
- std::string Input = R"cc(
- struct S {
- int bad;
- };
- int g() {
- S s;
- return s.bad;
- }
- )cc";
- std::string Expected = R"cc(
- struct S {
- int bad;
- };
- int g() {
- S s;
- return s.good;
- }
- )cc";
- testRule(Rule, Input, Expected);
- }
- TEST_F(TransformerTest, NodePartMemberQualified) {
- std::string Input = R"cc(
- struct S {
- int bad;
- int good;
- };
- struct T : public S {
- int bad;
- };
- int g() {
- T t;
- return t.S::bad;
- }
- )cc";
- std::string Expected = R"cc(
- struct S {
- int bad;
- int good;
- };
- struct T : public S {
- int bad;
- };
- int g() {
- T t;
- return t.S::good;
- }
- )cc";
- StringRef E = "expr";
- testRule(makeRule(memberExpr().bind(E), change(member(E), text("good"))),
- Input, Expected);
- }
- TEST_F(TransformerTest, NodePartMemberMultiToken) {
- std::string Input = R"cc(
- struct Y {
- int operator*();
- int good();
- template <typename T> void foo(T t);
- };
- int neutral(int x) {
- Y y;
- y.template foo<int>(3);
- return y.operator *();
- }
- )cc";
- std::string Expected = R"cc(
- struct Y {
- int operator*();
- int good();
- template <typename T> void foo(T t);
- };
- int neutral(int x) {
- Y y;
- y.template good<int>(3);
- return y.good();
- }
- )cc";
- StringRef MemExpr = "member";
- testRule(makeRule(memberExpr().bind(MemExpr),
- change(member(MemExpr), text("good"))),
- Input, Expected);
- }
- TEST_F(TransformerTest, InsertBeforeEdit) {
- std::string Input = R"cc(
- int f() {
- return 7;
- }
- )cc";
- std::string Expected = R"cc(
- int f() {
- int y = 3;
- return 7;
- }
- )cc";
- StringRef Ret = "return";
- testRule(makeRule(returnStmt().bind(Ret),
- insertBefore(statement(Ret), text("int y = 3;"))),
- Input, Expected);
- }
- TEST_F(TransformerTest, InsertAfterEdit) {
- std::string Input = R"cc(
- int f() {
- int x = 5;
- return 7;
- }
- )cc";
- std::string Expected = R"cc(
- int f() {
- int x = 5;
- int y = 3;
- return 7;
- }
- )cc";
- StringRef Decl = "decl";
- testRule(makeRule(declStmt().bind(Decl),
- insertAfter(statement(Decl), text("int y = 3;"))),
- Input, Expected);
- }
- TEST_F(TransformerTest, RemoveEdit) {
- std::string Input = R"cc(
- int f() {
- int x = 5;
- return 7;
- }
- )cc";
- std::string Expected = R"cc(
- int f() {
- return 7;
- }
- )cc";
- StringRef Decl = "decl";
- testRule(makeRule(declStmt().bind(Decl), remove(statement(Decl))), Input,
- Expected);
- }
- TEST_F(TransformerTest, MultiChange) {
- std::string Input = R"cc(
- void foo() {
- if (10 > 1.0)
- log(1) << "oh no!";
- else
- log(0) << "ok";
- }
- )cc";
- std::string Expected = R"(
- void foo() {
- if (true) { /* then */ }
- else { /* else */ }
- }
- )";
- StringRef C = "C", T = "T", E = "E";
- testRule(makeRule(ifStmt(hasCondition(expr().bind(C)),
- hasThen(stmt().bind(T)), hasElse(stmt().bind(E))),
- {change(node(C), text("true")),
- change(statement(T), text("{ /* then */ }")),
- change(statement(E), text("{ /* else */ }"))}),
- Input, Expected);
- }
- TEST_F(TransformerTest, OrderedRuleUnrelated) {
- StringRef Flag = "flag";
- RewriteRule FlagRule = makeRule(
- cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl(
- hasName("proto::ProtoCommandLineFlag"))))
- .bind(Flag)),
- unless(callee(cxxMethodDecl(hasName("GetProto"))))),
- change(node(Flag), text("PROTO")));
- std::string Input = R"cc(
- proto::ProtoCommandLineFlag flag;
- int x = flag.foo();
- int y = flag.GetProto().foo();
- int f(string s) { return strlen(s.c_str()); }
- )cc";
- std::string Expected = R"cc(
- proto::ProtoCommandLineFlag flag;
- int x = PROTO.foo();
- int y = flag.GetProto().foo();
- int f(string s) { return REPLACED; }
- )cc";
- testRule(applyFirst({ruleStrlenSize(), FlagRule}), Input, Expected);
- }
- TEST_F(TransformerTest, OrderedRuleRelated) {
- std::string Input = R"cc(
- void f1();
- void f2();
- void call_f1() { f1(); }
- void call_f2() { f2(); }
- )cc";
- std::string Expected = R"cc(
- void f1();
- void f2();
- void call_f1() { REPLACE_F1; }
- void call_f2() { REPLACE_F1_OR_F2; }
- )cc";
- RewriteRule ReplaceF1 =
- makeRule(callExpr(callee(functionDecl(hasName("f1")))),
- change(text("REPLACE_F1")));
- RewriteRule ReplaceF1OrF2 =
- makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
- change(text("REPLACE_F1_OR_F2")));
- testRule(applyFirst({ReplaceF1, ReplaceF1OrF2}), Input, Expected);
- }
- // Change the order of the rules to get a different result. When `ReplaceF1OrF2`
- // comes first, it applies for both uses, so `ReplaceF1` never applies.
- TEST_F(TransformerTest, OrderedRuleRelatedSwapped) {
- std::string Input = R"cc(
- void f1();
- void f2();
- void call_f1() { f1(); }
- void call_f2() { f2(); }
- )cc";
- std::string Expected = R"cc(
- void f1();
- void f2();
- void call_f1() { REPLACE_F1_OR_F2; }
- void call_f2() { REPLACE_F1_OR_F2; }
- )cc";
- RewriteRule ReplaceF1 =
- makeRule(callExpr(callee(functionDecl(hasName("f1")))),
- change(text("REPLACE_F1")));
- RewriteRule ReplaceF1OrF2 =
- makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
- change(text("REPLACE_F1_OR_F2")));
- testRule(applyFirst({ReplaceF1OrF2, ReplaceF1}), Input, Expected);
- }
- // Verify that a set of rules whose matchers have different base kinds works
- // properly, including that `applyFirst` produces multiple matchers. We test
- // two different kinds of rules: Expr and Decl. We place the Decl rule in the
- // middle to test that `buildMatchers` works even when the kinds aren't grouped
- // together.
- TEST_F(TransformerTest, OrderedRuleMultipleKinds) {
- std::string Input = R"cc(
- void f1();
- void f2();
- void call_f1() { f1(); }
- void call_f2() { f2(); }
- )cc";
- std::string Expected = R"cc(
- void f1();
- void DECL_RULE();
- void call_f1() { REPLACE_F1; }
- void call_f2() { REPLACE_F1_OR_F2; }
- )cc";
- RewriteRule ReplaceF1 =
- makeRule(callExpr(callee(functionDecl(hasName("f1")))),
- change(text("REPLACE_F1")));
- RewriteRule ReplaceF1OrF2 =
- makeRule(callExpr(callee(functionDecl(hasAnyName("f1", "f2")))),
- change(text("REPLACE_F1_OR_F2")));
- RewriteRule DeclRule = makeRule(functionDecl(hasName("f2")).bind("fun"),
- change(name("fun"), text("DECL_RULE")));
- RewriteRule Rule = applyFirst({ReplaceF1, DeclRule, ReplaceF1OrF2});
- EXPECT_EQ(transformer::detail::buildMatchers(Rule).size(), 2UL);
- testRule(Rule, Input, Expected);
- }
- //
- // Negative tests (where we expect no transformation to occur).
- //
- // Tests for a conflict in edits from a single match for a rule.
- TEST_F(TransformerTest, TextGeneratorFailure) {
- std::string Input = "int conflictOneRule() { return 3 + 7; }";
- // Try to change the whole binary-operator expression AND one its operands:
- StringRef O = "O";
- auto AlwaysFail = [](const ast_matchers::MatchFinder::MatchResult &)
- -> llvm::Expected<std::string> {
- return llvm::createStringError(llvm::errc::invalid_argument, "ERROR");
- };
- Transformer T(makeRule(binaryOperator().bind(O), change(node(O), AlwaysFail)),
- consumer());
- T.registerMatchers(&MatchFinder);
- EXPECT_FALSE(rewrite(Input));
- EXPECT_THAT(Changes, IsEmpty());
- EXPECT_EQ(ErrorCount, 1);
- }
- // Tests for a conflict in edits from a single match for a rule.
- TEST_F(TransformerTest, OverlappingEditsInRule) {
- std::string Input = "int conflictOneRule() { return 3 + 7; }";
- // Try to change the whole binary-operator expression AND one its operands:
- StringRef O = "O", L = "L";
- Transformer T(makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O),
- {change(node(O), text("DELETE_OP")),
- change(node(L), text("DELETE_LHS"))}),
- consumer());
- T.registerMatchers(&MatchFinder);
- EXPECT_FALSE(rewrite(Input));
- EXPECT_THAT(Changes, IsEmpty());
- EXPECT_EQ(ErrorCount, 1);
- }
- // Tests for a conflict in edits across multiple matches (of the same rule).
- TEST_F(TransformerTest, OverlappingEditsMultipleMatches) {
- std::string Input = "int conflictOneRule() { return -7; }";
- // Try to change the whole binary-operator expression AND one its operands:
- StringRef E = "E";
- Transformer T(makeRule(expr().bind(E), change(node(E), text("DELETE_EXPR"))),
- consumer());
- T.registerMatchers(&MatchFinder);
- // The rewrite process fails because the changes conflict with each other...
- EXPECT_FALSE(rewrite(Input));
- // ... but two changes were produced.
- EXPECT_EQ(Changes.size(), 2u);
- EXPECT_EQ(ErrorCount, 0);
- }
- TEST_F(TransformerTest, ErrorOccurredMatchSkipped) {
- // Syntax error in the function body:
- std::string Input = "void errorOccurred() { 3 }";
- Transformer T(makeRule(functionDecl(hasName("errorOccurred")),
- change(text("DELETED;"))),
- consumer());
- T.registerMatchers(&MatchFinder);
- // The rewrite process itself fails...
- EXPECT_FALSE(rewrite(Input));
- // ... and no changes or errors are produced in the process.
- EXPECT_THAT(Changes, IsEmpty());
- EXPECT_EQ(ErrorCount, 0);
- }
- // Transformation of macro source text when the change encompasses the entirety
- // of the expanded text.
- TEST_F(TransformerTest, SimpleMacro) {
- std::string Input = R"cc(
- #define ZERO 0
- int f(string s) { return ZERO; }
- )cc";
- std::string Expected = R"cc(
- #define ZERO 0
- int f(string s) { return 999; }
- )cc";
- StringRef zero = "zero";
- RewriteRule R = makeRule(integerLiteral(equals(0)).bind(zero),
- change(node(zero), text("999")));
- testRule(R, Input, Expected);
- }
- // Transformation of macro source text when the change encompasses the entirety
- // of the expanded text, for the case of function-style macros.
- TEST_F(TransformerTest, FunctionMacro) {
- std::string Input = R"cc(
- #define MACRO(str) strlen((str).c_str())
- int f(string s) { return MACRO(s); }
- )cc";
- std::string Expected = R"cc(
- #define MACRO(str) strlen((str).c_str())
- int f(string s) { return REPLACED; }
- )cc";
- testRule(ruleStrlenSize(), Input, Expected);
- }
- // Tests that expressions in macro arguments can be rewritten.
- TEST_F(TransformerTest, MacroArg) {
- std::string Input = R"cc(
- #define PLUS(e) e + 1
- int f(string s) { return PLUS(strlen(s.c_str())); }
- )cc";
- std::string Expected = R"cc(
- #define PLUS(e) e + 1
- int f(string s) { return PLUS(REPLACED); }
- )cc";
- testRule(ruleStrlenSize(), Input, Expected);
- }
- // Tests that expressions in macro arguments can be rewritten, even when the
- // macro call occurs inside another macro's definition.
- TEST_F(TransformerTest, MacroArgInMacroDef) {
- std::string Input = R"cc(
- #define NESTED(e) e
- #define MACRO(str) NESTED(strlen((str).c_str()))
- int f(string s) { return MACRO(s); }
- )cc";
- std::string Expected = R"cc(
- #define NESTED(e) e
- #define MACRO(str) NESTED(strlen((str).c_str()))
- int f(string s) { return REPLACED; }
- )cc";
- testRule(ruleStrlenSize(), Input, Expected);
- }
- // Tests the corner case of the identity macro, specifically that it is
- // discarded in the rewrite rather than preserved (like PLUS is preserved in the
- // previous test). This behavior is of dubious value (and marked with a FIXME
- // in the code), but we test it to verify (and demonstrate) how this case is
- // handled.
- TEST_F(TransformerTest, IdentityMacro) {
- std::string Input = R"cc(
- #define ID(e) e
- int f(string s) { return ID(strlen(s.c_str())); }
- )cc";
- std::string Expected = R"cc(
- #define ID(e) e
- int f(string s) { return REPLACED; }
- )cc";
- testRule(ruleStrlenSize(), Input, Expected);
- }
- // Tests that two changes in a single macro expansion do not lead to conflicts
- // in applying the changes.
- TEST_F(TransformerTest, TwoChangesInOneMacroExpansion) {
- std::string Input = R"cc(
- #define PLUS(a,b) (a) + (b)
- int f() { return PLUS(3, 4); }
- )cc";
- std::string Expected = R"cc(
- #define PLUS(a,b) (a) + (b)
- int f() { return PLUS(LIT, LIT); }
- )cc";
- testRule(makeRule(integerLiteral(), change(text("LIT"))), Input, Expected);
- }
- // Tests case where the rule's match spans both source from the macro and its
- // arg, with the begin location (the "anchor") being the arg.
- TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNot) {
- std::string Input = R"cc(
- #define PLUS_ONE(a) a + 1
- int f() { return PLUS_ONE(3); }
- )cc";
- std::string Expected = R"cc(
- #define PLUS_ONE(a) a + 1
- int f() { return PLUS_ONE(LIT); }
- )cc";
- StringRef E = "expr";
- testRule(makeRule(binaryOperator(hasLHS(expr().bind(E))),
- change(node(E), text("LIT"))),
- Input, Expected);
- }
- // Tests case where the rule's match spans both source from the macro and its
- // arg, with the begin location (the "anchor") being inside the macro.
- TEST_F(TransformerTest, MatchSpansMacroTextButChangeDoesNotAnchoredInMacro) {
- std::string Input = R"cc(
- #define PLUS_ONE(a) 1 + a
- int f() { return PLUS_ONE(3); }
- )cc";
- std::string Expected = R"cc(
- #define PLUS_ONE(a) 1 + a
- int f() { return PLUS_ONE(LIT); }
- )cc";
- StringRef E = "expr";
- testRule(makeRule(binaryOperator(hasRHS(expr().bind(E))),
- change(node(E), text("LIT"))),
- Input, Expected);
- }
- // No rewrite is applied when the changed text does not encompass the entirety
- // of the expanded text. That is, the edit would have to be applied to the
- // macro's definition to succeed and editing the expansion point would not
- // suffice.
- TEST_F(TransformerTest, NoPartialRewriteOMacroExpansion) {
- std::string Input = R"cc(
- #define ZERO_PLUS 0 + 3
- int f(string s) { return ZERO_PLUS; })cc";
- StringRef zero = "zero";
- RewriteRule R = makeRule(integerLiteral(equals(0)).bind(zero),
- change(node(zero), text("0")));
- testRule(R, Input, Input);
- }
- // This test handles the corner case where a macro expands within another macro
- // to matching code, but that code is an argument to the nested macro call. A
- // simple check of isMacroArgExpansion() vs. isMacroBodyExpansion() will get
- // this wrong, and transform the code.
- TEST_F(TransformerTest, NoPartialRewriteOfMacroExpansionForMacroArgs) {
- std::string Input = R"cc(
- #define NESTED(e) e
- #define MACRO(str) 1 + NESTED(strlen((str).c_str()))
- int f(string s) { return MACRO(s); }
- )cc";
- testRule(ruleStrlenSize(), Input, Input);
- }
- #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
- // Verifies that `Type` and `QualType` are not allowed as top-level matchers in
- // rules.
- TEST(TransformerDeathTest, OrderedRuleTypes) {
- RewriteRule QualTypeRule = makeRule(qualType(), change(text("Q")));
- EXPECT_DEATH(transformer::detail::buildMatchers(QualTypeRule),
- "Matcher must be.*node matcher");
- RewriteRule TypeRule = makeRule(arrayType(), change(text("T")));
- EXPECT_DEATH(transformer::detail::buildMatchers(TypeRule),
- "Matcher must be.*node matcher");
- }
- #endif
- } // namespace
|