123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- //===- unittest/Tooling/RangeSelectorTest.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/RangeSelector.h"
- #include "clang/ASTMatchers/ASTMatchers.h"
- #include "clang/Frontend/ASTUnit.h"
- #include "clang/Tooling/Tooling.h"
- #include "clang/Tooling/Transformer/SourceCode.h"
- #include "llvm/Support/Error.h"
- #include "llvm/Testing/Support/Error.h"
- #include "gmock/gmock.h"
- #include "gtest/gtest.h"
- using namespace clang;
- using namespace transformer;
- using namespace ast_matchers;
- namespace {
- using ::llvm::Expected;
- using ::llvm::Failed;
- using ::llvm::HasValue;
- using ::llvm::StringError;
- using ::testing::AllOf;
- using ::testing::HasSubstr;
- using ::testing::Property;
- using MatchResult = MatchFinder::MatchResult;
- struct TestMatch {
- // The AST unit from which `result` is built. We bundle it because it backs
- // the result. Users are not expected to access it.
- std::unique_ptr<clang::ASTUnit> ASTUnit;
- // The result to use in the test. References `ast_unit`.
- MatchResult Result;
- };
- template <typename M> TestMatch matchCode(StringRef Code, M Matcher) {
- auto ASTUnit = tooling::buildASTFromCode(Code);
- assert(ASTUnit != nullptr && "AST construction failed");
- ASTContext &Context = ASTUnit->getASTContext();
- assert(!Context.getDiagnostics().hasErrorOccurred() && "Compilation error");
- auto Matches = ast_matchers::match(Matcher, Context);
- // We expect a single, exact match.
- assert(Matches.size() != 0 && "no matches found");
- assert(Matches.size() == 1 && "too many matches");
- return TestMatch{std::move(ASTUnit), MatchResult(Matches[0], &Context)};
- }
- // Applies \p Selector to \p Match and, on success, returns the selected source.
- Expected<StringRef> select(RangeSelector Selector, const TestMatch &Match) {
- Expected<CharSourceRange> Range = Selector(Match.Result);
- if (!Range)
- return Range.takeError();
- return tooling::getText(*Range, *Match.Result.Context);
- }
- // Applies \p Selector to a trivial match with only a single bound node with id
- // "bound_node_id". For use in testing unbound-node errors.
- Expected<CharSourceRange> selectFromTrivial(const RangeSelector &Selector) {
- // We need to bind the result to something, or the match will fail. Use a
- // binding that is not used in the unbound node tests.
- TestMatch Match =
- matchCode("static int x = 0;", varDecl().bind("bound_node_id"));
- return Selector(Match.Result);
- }
- // Matches the message expected for unbound-node failures.
- testing::Matcher<StringError> withUnboundNodeMessage() {
- return testing::Property(
- &StringError::getMessage,
- AllOf(HasSubstr("unbound_id"), HasSubstr("not bound")));
- }
- // Applies \p Selector to code containing assorted node types, where the match
- // binds each one: a statement ("stmt"), a (non-member) ctor-initializer
- // ("init"), an expression ("expr") and a (nameless) declaration ("decl"). Used
- // to test failures caused by applying selectors to nodes of the wrong type.
- Expected<CharSourceRange> selectFromAssorted(RangeSelector Selector) {
- StringRef Code = R"cc(
- struct A {};
- class F : public A {
- public:
- F(int) {}
- };
- void g() { F f(1); }
- )cc";
- auto Matcher =
- compoundStmt(
- hasDescendant(
- cxxConstructExpr(
- hasDeclaration(
- decl(hasDescendant(cxxCtorInitializer(isBaseInitializer())
- .bind("init")))
- .bind("decl")))
- .bind("expr")))
- .bind("stmt");
- return Selector(matchCode(Code, Matcher).Result);
- }
- // Matches the message expected for type-error failures.
- testing::Matcher<StringError> withTypeErrorMessage(StringRef NodeID) {
- return testing::Property(
- &StringError::getMessage,
- AllOf(HasSubstr(NodeID), HasSubstr("mismatched type")));
- }
- TEST(RangeSelectorTest, UnboundNode) {
- EXPECT_THAT_EXPECTED(selectFromTrivial(node("unbound_id")),
- Failed<StringError>(withUnboundNodeMessage()));
- }
- MATCHER_P(EqualsCharSourceRange, Range, "") {
- return Range.getAsRange() == arg.getAsRange() &&
- Range.isTokenRange() == arg.isTokenRange();
- }
- // FIXME: here and elsewhere: use llvm::Annotations library to explicitly mark
- // points and ranges of interest, enabling more readable tests.
- TEST(RangeSelectorTest, BeforeOp) {
- StringRef Code = R"cc(
- int f(int x, int y, int z) { return 3; }
- int g() { return f(/* comment */ 3, 7 /* comment */, 9); }
- )cc";
- StringRef Call = "call";
- TestMatch Match = matchCode(Code, callExpr().bind(Call));
- const auto* E = Match.Result.Nodes.getNodeAs<Expr>(Call);
- assert(E != nullptr);
- auto ExprBegin = E->getSourceRange().getBegin();
- EXPECT_THAT_EXPECTED(
- before(node(Call))(Match.Result),
- HasValue(EqualsCharSourceRange(
- CharSourceRange::getCharRange(ExprBegin, ExprBegin))));
- }
- TEST(RangeSelectorTest, AfterOp) {
- StringRef Code = R"cc(
- int f(int x, int y, int z) { return 3; }
- int g() { return f(/* comment */ 3, 7 /* comment */, 9); }
- )cc";
- StringRef Call = "call";
- TestMatch Match = matchCode(Code, callExpr().bind(Call));
- const auto* E = Match.Result.Nodes.getNodeAs<Expr>(Call);
- assert(E != nullptr);
- const SourceRange Range = E->getSourceRange();
- // The end token, a right paren, is one character wide, so advance by one,
- // bringing us to the semicolon.
- const SourceLocation SemiLoc = Range.getEnd().getLocWithOffset(1);
- const auto ExpectedAfter = CharSourceRange::getCharRange(SemiLoc, SemiLoc);
- // Test with a char range.
- auto CharRange = CharSourceRange::getCharRange(Range.getBegin(), SemiLoc);
- EXPECT_THAT_EXPECTED(after(charRange(CharRange))(Match.Result),
- HasValue(EqualsCharSourceRange(ExpectedAfter)));
- // Test with a token range.
- auto TokenRange = CharSourceRange::getTokenRange(Range);
- EXPECT_THAT_EXPECTED(after(charRange(TokenRange))(Match.Result),
- HasValue(EqualsCharSourceRange(ExpectedAfter)));
- }
- TEST(RangeSelectorTest, RangeOp) {
- StringRef Code = R"cc(
- int f(int x, int y, int z) { return 3; }
- int g() { return f(/* comment */ 3, 7 /* comment */, 9); }
- )cc";
- StringRef Arg0 = "a0";
- StringRef Arg1 = "a1";
- StringRef Call = "call";
- auto Matcher = callExpr(hasArgument(0, expr().bind(Arg0)),
- hasArgument(1, expr().bind(Arg1)))
- .bind(Call);
- TestMatch Match = matchCode(Code, Matcher);
- // Node-id specific version:
- EXPECT_THAT_EXPECTED(select(range(Arg0, Arg1), Match), HasValue("3, 7"));
- // General version:
- EXPECT_THAT_EXPECTED(select(range(node(Arg0), node(Arg1)), Match),
- HasValue("3, 7"));
- }
- TEST(RangeSelectorTest, NodeOpStatement) {
- StringRef Code = "int f() { return 3; }";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, returnStmt().bind(ID));
- EXPECT_THAT_EXPECTED(select(node(ID), Match), HasValue("return 3;"));
- }
- TEST(RangeSelectorTest, NodeOpExpression) {
- StringRef Code = "int f() { return 3; }";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, expr().bind(ID));
- EXPECT_THAT_EXPECTED(select(node(ID), Match), HasValue("3"));
- }
- TEST(RangeSelectorTest, StatementOp) {
- StringRef Code = "int f() { return 3; }";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, expr().bind(ID));
- EXPECT_THAT_EXPECTED(select(statement(ID), Match), HasValue("3;"));
- }
- TEST(RangeSelectorTest, MemberOp) {
- StringRef Code = R"cc(
- struct S {
- int member;
- };
- int g() {
- S s;
- return s.member;
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, memberExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(member(ID), Match), HasValue("member"));
- }
- // Tests that member does not select any qualifiers on the member name.
- TEST(RangeSelectorTest, MemberOpQualified) {
- StringRef Code = R"cc(
- struct S {
- int member;
- };
- struct T : public S {
- int field;
- };
- int g() {
- T t;
- return t.S::member;
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, memberExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(member(ID), Match), HasValue("member"));
- }
- TEST(RangeSelectorTest, MemberOpTemplate) {
- StringRef Code = R"cc(
- struct S {
- template <typename T> T foo(T t);
- };
- int f(int x) {
- S s;
- return s.template foo<int>(3);
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, memberExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(member(ID), Match), HasValue("foo"));
- }
- TEST(RangeSelectorTest, MemberOpOperator) {
- StringRef Code = R"cc(
- struct S {
- int operator*();
- };
- int f(int x) {
- S s;
- return s.operator *();
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, memberExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(member(ID), Match), HasValue("operator *"));
- }
- TEST(RangeSelectorTest, NameOpNamedDecl) {
- StringRef Code = R"cc(
- int myfun() {
- return 3;
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, functionDecl().bind(ID));
- EXPECT_THAT_EXPECTED(select(name(ID), Match), HasValue("myfun"));
- }
- TEST(RangeSelectorTest, NameOpDeclRef) {
- StringRef Code = R"cc(
- int foo(int x) {
- return x;
- }
- int g(int x) { return foo(x) * x; }
- )cc";
- StringRef Ref = "ref";
- TestMatch Match = matchCode(Code, declRefExpr(to(functionDecl())).bind(Ref));
- EXPECT_THAT_EXPECTED(select(name(Ref), Match), HasValue("foo"));
- }
- TEST(RangeSelectorTest, NameOpCtorInitializer) {
- StringRef Code = R"cc(
- class C {
- public:
- C() : field(3) {}
- int field;
- };
- )cc";
- StringRef Init = "init";
- TestMatch Match = matchCode(Code, cxxCtorInitializer().bind(Init));
- EXPECT_THAT_EXPECTED(select(name(Init), Match), HasValue("field"));
- }
- TEST(RangeSelectorTest, NameOpErrors) {
- EXPECT_THAT_EXPECTED(selectFromTrivial(name("unbound_id")),
- Failed<StringError>(withUnboundNodeMessage()));
- EXPECT_THAT_EXPECTED(selectFromAssorted(name("stmt")),
- Failed<StringError>(withTypeErrorMessage("stmt")));
- }
- TEST(RangeSelectorTest, NameOpDeclRefError) {
- StringRef Code = R"cc(
- struct S {
- int operator*();
- };
- int f(int x) {
- S s;
- return *s + x;
- }
- )cc";
- StringRef Ref = "ref";
- TestMatch Match = matchCode(Code, declRefExpr(to(functionDecl())).bind(Ref));
- EXPECT_THAT_EXPECTED(
- name(Ref)(Match.Result),
- Failed<StringError>(testing::Property(
- &StringError::getMessage,
- AllOf(HasSubstr(Ref), HasSubstr("requires property 'identifier'")))));
- }
- TEST(RangeSelectorTest, CallArgsOp) {
- const StringRef Code = R"cc(
- struct C {
- int bar(int, int);
- };
- int f() {
- C x;
- return x.bar(3, 4);
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, callExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue("3, 4"));
- }
- TEST(RangeSelectorTest, CallArgsOpNoArgs) {
- const StringRef Code = R"cc(
- struct C {
- int bar();
- };
- int f() {
- C x;
- return x.bar();
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, callExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue(""));
- }
- TEST(RangeSelectorTest, CallArgsOpNoArgsWithComments) {
- const StringRef Code = R"cc(
- struct C {
- int bar();
- };
- int f() {
- C x;
- return x.bar(/*empty*/);
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, callExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue("/*empty*/"));
- }
- // Tests that arguments are extracted correctly when a temporary (with parens)
- // is used.
- TEST(RangeSelectorTest, CallArgsOpWithParens) {
- const StringRef Code = R"cc(
- struct C {
- int bar(int, int) { return 3; }
- };
- int f() {
- C x;
- return C().bar(3, 4);
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match =
- matchCode(Code, callExpr(callee(functionDecl(hasName("bar")))).bind(ID));
- EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue("3, 4"));
- }
- TEST(RangeSelectorTest, CallArgsOpLeadingComments) {
- const StringRef Code = R"cc(
- struct C {
- int bar(int, int) { return 3; }
- };
- int f() {
- C x;
- return x.bar(/*leading*/ 3, 4);
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, callExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(callArgs(ID), Match),
- HasValue("/*leading*/ 3, 4"));
- }
- TEST(RangeSelectorTest, CallArgsOpTrailingComments) {
- const StringRef Code = R"cc(
- struct C {
- int bar(int, int) { return 3; }
- };
- int f() {
- C x;
- return x.bar(3 /*trailing*/, 4);
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, callExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(callArgs(ID), Match),
- HasValue("3 /*trailing*/, 4"));
- }
- TEST(RangeSelectorTest, CallArgsOpEolComments) {
- const StringRef Code = R"cc(
- struct C {
- int bar(int, int) { return 3; }
- };
- int f() {
- C x;
- return x.bar( // Header
- 1, // foo
- 2 // bar
- );
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, callExpr().bind(ID));
- std::string ExpectedString = R"( // Header
- 1, // foo
- 2 // bar
- )";
- EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue(ExpectedString));
- }
- TEST(RangeSelectorTest, CallArgsErrors) {
- EXPECT_THAT_EXPECTED(selectFromTrivial(callArgs("unbound_id")),
- Failed<StringError>(withUnboundNodeMessage()));
- EXPECT_THAT_EXPECTED(selectFromAssorted(callArgs("stmt")),
- Failed<StringError>(withTypeErrorMessage("stmt")));
- }
- TEST(RangeSelectorTest, StatementsOp) {
- StringRef Code = R"cc(
- void g();
- void f() { /* comment */ g(); /* comment */ g(); /* comment */ }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, compoundStmt().bind(ID));
- EXPECT_THAT_EXPECTED(
- select(statements(ID), Match),
- HasValue(" /* comment */ g(); /* comment */ g(); /* comment */ "));
- }
- TEST(RangeSelectorTest, StatementsOpEmptyList) {
- StringRef Code = "void f() {}";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, compoundStmt().bind(ID));
- EXPECT_THAT_EXPECTED(select(statements(ID), Match), HasValue(""));
- }
- TEST(RangeSelectorTest, StatementsOpErrors) {
- EXPECT_THAT_EXPECTED(selectFromTrivial(statements("unbound_id")),
- Failed<StringError>(withUnboundNodeMessage()));
- EXPECT_THAT_EXPECTED(selectFromAssorted(statements("decl")),
- Failed<StringError>(withTypeErrorMessage("decl")));
- }
- TEST(RangeSelectorTest, ElementsOp) {
- StringRef Code = R"cc(
- void f() {
- int v[] = {/* comment */ 3, /* comment*/ 4 /* comment */};
- (void)v;
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, initListExpr().bind(ID));
- EXPECT_THAT_EXPECTED(
- select(initListElements(ID), Match),
- HasValue("/* comment */ 3, /* comment*/ 4 /* comment */"));
- }
- TEST(RangeSelectorTest, ElementsOpEmptyList) {
- StringRef Code = R"cc(
- void f() {
- int v[] = {};
- (void)v;
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, initListExpr().bind(ID));
- EXPECT_THAT_EXPECTED(select(initListElements(ID), Match), HasValue(""));
- }
- TEST(RangeSelectorTest, ElementsOpErrors) {
- EXPECT_THAT_EXPECTED(selectFromTrivial(initListElements("unbound_id")),
- Failed<StringError>(withUnboundNodeMessage()));
- EXPECT_THAT_EXPECTED(selectFromAssorted(initListElements("stmt")),
- Failed<StringError>(withTypeErrorMessage("stmt")));
- }
- TEST(RangeSelectorTest, ElseBranchOpSingleStatement) {
- StringRef Code = R"cc(
- int f() {
- int x = 0;
- if (true) x = 3;
- else x = 4;
- return x + 5;
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, ifStmt().bind(ID));
- EXPECT_THAT_EXPECTED(select(elseBranch(ID), Match), HasValue("else x = 4;"));
- }
- TEST(RangeSelectorTest, ElseBranchOpCompoundStatement) {
- StringRef Code = R"cc(
- int f() {
- int x = 0;
- if (true) x = 3;
- else { x = 4; }
- return x + 5;
- }
- )cc";
- StringRef ID = "id";
- TestMatch Match = matchCode(Code, ifStmt().bind(ID));
- EXPECT_THAT_EXPECTED(select(elseBranch(ID), Match),
- HasValue("else { x = 4; }"));
- }
- // Tests case where the matched node is the complete expanded text.
- TEST(RangeSelectorTest, ExpansionOp) {
- StringRef Code = R"cc(
- #define BADDECL(E) int bad(int x) { return E; }
- BADDECL(x * x)
- )cc";
- StringRef Fun = "Fun";
- TestMatch Match = matchCode(Code, functionDecl(hasName("bad")).bind(Fun));
- EXPECT_THAT_EXPECTED(select(expansion(node(Fun)), Match),
- HasValue("BADDECL(x * x)"));
- }
- // Tests case where the matched node is (only) part of the expanded text.
- TEST(RangeSelectorTest, ExpansionOpPartial) {
- StringRef Code = R"cc(
- #define BADDECL(E) int bad(int x) { return E; }
- BADDECL(x * x)
- )cc";
- StringRef Ret = "Ret";
- TestMatch Match = matchCode(Code, returnStmt().bind(Ret));
- EXPECT_THAT_EXPECTED(select(expansion(node(Ret)), Match),
- HasValue("BADDECL(x * x)"));
- }
- TEST(RangeSelectorTest, IfBoundOpBound) {
- StringRef Code = R"cc(
- int f() {
- return 3 + 5;
- }
- )cc";
- StringRef ID = "id", Op = "op";
- TestMatch Match =
- matchCode(Code, binaryOperator(hasLHS(expr().bind(ID))).bind(Op));
- EXPECT_THAT_EXPECTED(select(ifBound(ID, node(ID), node(Op)), Match),
- HasValue("3"));
- }
- TEST(RangeSelectorTest, IfBoundOpUnbound) {
- StringRef Code = R"cc(
- int f() {
- return 3 + 5;
- }
- )cc";
- StringRef ID = "id", Op = "op";
- TestMatch Match = matchCode(Code, binaryOperator().bind(Op));
- EXPECT_THAT_EXPECTED(select(ifBound(ID, node(ID), node(Op)), Match),
- HasValue("3 + 5"));
- }
- } // namespace
|