+ LLVMSupport + LLVMTestingSupport clang/unittests/Tooling/CMakeLists already has this at the top: set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} Support )
I think we link Support twice now. Did adding TestingSupport up there not work? I'd expect that your addition would break shared library builds, or something like that. (Also, I tried to fix a build error that old gcc versions had with this change in r361208.) *From: *Yitzhak Mandelbaum via cfe-commits <cfe-commits@lists.llvm.org> *Date: *Mon, May 20, 2019 at 9:12 AM *To: * <cfe-commits@lists.llvm.org> Author: ymandel > Date: Mon May 20 06:15:14 2019 > New Revision: 361152 > > URL: http://llvm.org/viewvc/llvm-project?rev=361152&view=rev > Log: > [LibTooling] Add RangeSelector library for defining source ranges based on > bound AST nodes. > > Summary: > > The RangeSelector library defines a combinator language for specifying > source > ranges based on bound ids for AST nodes. The combinator approach follows > the > design of the AST matchers. The RangeSelectors defined here will be used > in > both RewriteRule, for specifying source affected by edit, and in Stencil > for > specifying source to use constructively in a replacement. > > Reviewers: ilya-biryukov > > Subscribers: mgorny, cfe-commits > > Tags: #clang > > Differential Revision: https://reviews.llvm.org/D61774 > > Added: > cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h > cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp > cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp > Modified: > cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt > cfe/trunk/unittests/Tooling/CMakeLists.txt > > Added: cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h?rev=361152&view=auto > > ============================================================================== > --- cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h (added) > +++ cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h Mon May 20 > 06:15:14 2019 > @@ -0,0 +1,80 @@ > +//===--- RangeSelector.h - Source-selection library ---------*- C++ > -*-===// > +// > +// 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 > +// > > +//===----------------------------------------------------------------------===// > +/// > +/// \file > +/// Defines a combinator library supporting the definition of > _selectors_, > +/// which select source ranges based on (bound) AST nodes. > +/// > > +//===----------------------------------------------------------------------===// > + > +#ifndef LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_ > +#define LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_ > + > +#include "clang/ASTMatchers/ASTMatchFinder.h" > +#include "clang/Basic/SourceLocation.h" > +#include "llvm/ADT/StringRef.h" > +#include "llvm/Support/Error.h" > +#include <functional> > + > +namespace clang { > +namespace tooling { > +using RangeSelector = std::function<Expected<CharSourceRange>( > + const ast_matchers::MatchFinder::MatchResult &)>; > + > +inline RangeSelector charRange(CharSourceRange R) { > + return [R](const ast_matchers::MatchFinder::MatchResult &) > + -> Expected<CharSourceRange> { return R; }; > +} > + > +/// Selects from the start of \p Begin and to the end of \p End. > +RangeSelector range(RangeSelector Begin, RangeSelector End); > + > +/// Convenience version of \c range where end-points are bound nodes. > +RangeSelector range(StringRef BeginID, StringRef EndID); > + > +/// Selects a node, including trailing semicolon (for non-expression > +/// statements). \p ID is the node's binding in the match result. > +RangeSelector node(StringRef ID); > + > +/// Selects a node, including trailing semicolon (always). Useful for > selecting > +/// expression statements. \p ID is the node's binding in the match > result. > +RangeSelector statement(StringRef ID); > + > +/// Given a \c MemberExpr, selects the member token. \p ID is the node's > +/// binding in the match result. > +RangeSelector member(StringRef ID); > + > +/// Given a node with a "name", (like \c NamedDecl, \c DeclRefExpr or \c > +/// CxxCtorInitializer) selects the name's token. Only selects the final > +/// identifier of a qualified name, but not any qualifiers or template > +/// arguments. For example, for `::foo::bar::baz` and > `::foo::bar::baz<int>`, > +/// it selects only `baz`. > +/// > +/// \param ID is the node's binding in the match result. > +RangeSelector name(StringRef ID); > + > +// Given a \c CallExpr (bound to \p ID), selects the arguments' source > text (all > +// source between the call's parentheses). > +RangeSelector callArgs(StringRef ID); > + > +// Given a \c CompoundStmt (bound to \p ID), selects the source of the > +// statements (all source between the braces). > +RangeSelector statements(StringRef ID); > + > +// Given a \c InitListExpr (bound to \p ID), selects the range of the > elements > +// (all source between the braces). > +RangeSelector initListElements(StringRef ID); > + > +/// Selects the range from which `S` was expanded (possibly along with > other > +/// source), if `S` is an expansion, and `S` itself, otherwise. > Corresponds to > +/// `SourceManager::getExpansionRange`. > +RangeSelector expansion(RangeSelector S); > +} // namespace tooling > +} // namespace clang > + > +#endif // LLVM_CLANG_TOOLING_REFACTOR_RANGE_SELECTOR_H_ > > Modified: cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt?rev=361152&r1=361151&r2=361152&view=diff > > ============================================================================== > --- cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt (original) > +++ cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt Mon May 20 06:15:14 > 2019 > @@ -6,6 +6,7 @@ add_clang_library(clangToolingRefactor > AtomicChange.cpp > Extract/Extract.cpp > Extract/SourceExtraction.cpp > + RangeSelector.cpp > RefactoringActions.cpp > Rename/RenamingAction.cpp > Rename/SymbolOccurrences.cpp > > Added: cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp?rev=361152&view=auto > > ============================================================================== > --- cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp (added) > +++ cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp Mon May 20 > 06:15:14 2019 > @@ -0,0 +1,264 @@ > +//===--- RangeSelector.cpp - RangeSelector implementations ------*- C++ > -*-===// > +// > +// 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/Refactoring/RangeSelector.h" > +#include "clang/AST/Expr.h" > +#include "clang/ASTMatchers/ASTMatchFinder.h" > +#include "clang/Basic/SourceLocation.h" > +#include "clang/Lex/Lexer.h" > +#include "clang/Tooling/Refactoring/SourceCode.h" > +#include "llvm/ADT/StringRef.h" > +#include "llvm/Support/Errc.h" > +#include "llvm/Support/Error.h" > +#include <string> > +#include <utility> > +#include <vector> > + > +using namespace clang; > +using namespace tooling; > + > +using ast_matchers::MatchFinder; > +using ast_type_traits::ASTNodeKind; > +using ast_type_traits::DynTypedNode; > +using llvm::Error; > +using llvm::StringError; > + > +using MatchResult = MatchFinder::MatchResult; > + > +static Error invalidArgumentError(Twine Message) { > + return llvm::make_error<StringError>(llvm::errc::invalid_argument, > Message); > +} > + > +static Error typeError(StringRef ID, const ASTNodeKind &Kind) { > + return invalidArgumentError("mismatched type (node id=" + ID + > + " kind=" + Kind.asStringRef() + ")"); > +} > + > +static Error typeError(StringRef ID, const ASTNodeKind &Kind, > + Twine ExpectedType) { > + return invalidArgumentError("mismatched type: expected one of " + > + ExpectedType + " (node id=" + ID + > + " kind=" + Kind.asStringRef() + ")"); > +} > + > +static Error missingPropertyError(StringRef ID, Twine Description, > + StringRef Property) { > + return invalidArgumentError(Description + " requires property '" + > Property + > + "' (node id=" + ID + ")"); > +} > + > +static Expected<DynTypedNode> getNode(const ast_matchers::BoundNodes > &Nodes, > + StringRef ID) { > + auto &NodesMap = Nodes.getMap(); > + auto It = NodesMap.find(ID); > + if (It == NodesMap.end()) > + return invalidArgumentError("ID not bound: " + ID); > + return It->second; > +} > + > +// FIXME: handling of macros should be configurable. > +static SourceLocation findPreviousTokenStart(SourceLocation Start, > + const SourceManager &SM, > + const LangOptions &LangOpts) > { > + if (Start.isInvalid() || Start.isMacroID()) > + return SourceLocation(); > + > + SourceLocation BeforeStart = Start.getLocWithOffset(-1); > + if (BeforeStart.isInvalid() || BeforeStart.isMacroID()) > + return SourceLocation(); > + > + return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts); > +} > + > +// Finds the start location of the previous token of kind \p TK. > +// FIXME: handling of macros should be configurable. > +static SourceLocation findPreviousTokenKind(SourceLocation Start, > + const SourceManager &SM, > + const LangOptions &LangOpts, > + tok::TokenKind TK) { > + while (true) { > + SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts); > + if (L.isInvalid() || L.isMacroID()) > + return SourceLocation(); > + > + Token T; > + if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true)) > + return SourceLocation(); > + > + if (T.is(TK)) > + return T.getLocation(); > + > + Start = L; > + } > +} > + > +static SourceLocation findOpenParen(const CallExpr &E, const > SourceManager &SM, > + const LangOptions &LangOpts) { > + SourceLocation EndLoc = > + E.getNumArgs() == 0 ? E.getRParenLoc() : E.getArg(0)->getBeginLoc(); > + return findPreviousTokenKind(EndLoc, SM, LangOpts, > tok::TokenKind::l_paren); > +} > + > +RangeSelector tooling::node(StringRef ID) { > + return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { > + Expected<DynTypedNode> Node = getNode(Result.Nodes, ID); > + if (!Node) > + return Node.takeError(); > + return Node->get<Stmt>() != nullptr && Node->get<Expr>() == nullptr > + ? getExtendedRange(*Node, tok::TokenKind::semi, > *Result.Context) > + : CharSourceRange::getTokenRange(Node->getSourceRange()); > + }; > +} > + > +RangeSelector tooling::statement(StringRef ID) { > + return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { > + Expected<DynTypedNode> Node = getNode(Result.Nodes, ID); > + if (!Node) > + return Node.takeError(); > + return getExtendedRange(*Node, tok::TokenKind::semi, *Result.Context); > + }; > +} > + > +RangeSelector tooling::range(RangeSelector Begin, RangeSelector End) { > + return [Begin, End](const MatchResult &Result) -> > Expected<CharSourceRange> { > + Expected<CharSourceRange> BeginRange = Begin(Result); > + if (!BeginRange) > + return BeginRange.takeError(); > + Expected<CharSourceRange> EndRange = End(Result); > + if (!EndRange) > + return EndRange.takeError(); > + SourceLocation B = BeginRange->getBegin(); > + SourceLocation E = EndRange->getEnd(); > + // Note: we are precluding the possibility of sub-token ranges in the > case > + // that EndRange is a token range. > + if (Result.SourceManager->isBeforeInTranslationUnit(E, B)) { > + return invalidArgumentError("Bad range: out of order"); > + } > + return CharSourceRange(SourceRange(B, E), EndRange->isTokenRange()); > + }; > +} > + > +RangeSelector tooling::range(StringRef BeginID, StringRef EndID) { > + return tooling::range(node(BeginID), node(EndID)); > +} > + > +RangeSelector tooling::member(StringRef ID) { > + return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { > + Expected<DynTypedNode> Node = getNode(Result.Nodes, ID); > + if (!Node) > + return Node.takeError(); > + if (auto *M = Node->get<clang::MemberExpr>()) > + return CharSourceRange::getTokenRange( > + M->getMemberNameInfo().getSourceRange()); > + return typeError(ID, Node->getNodeKind(), "MemberExpr"); > + }; > +} > + > +RangeSelector tooling::name(StringRef ID) { > + return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { > + Expected<DynTypedNode> N = getNode(Result.Nodes, ID); > + if (!N) > + return N.takeError(); > + auto &Node = *N; > + if (const auto *D = Node.get<NamedDecl>()) { > + if (!D->getDeclName().isIdentifier()) > + return missingPropertyError(ID, "name", "identifier"); > + SourceLocation L = D->getLocation(); > + auto R = CharSourceRange::getTokenRange(L, L); > + // Verify that the range covers exactly the name. > + // FIXME: extend this code to support cases like `operator +` or > + // `foo<int>` for which this range will be too short. Doing so will > + // require subcasing `NamedDecl`, because it doesn't provide virtual > + // access to the \c DeclarationNameInfo. > + if (getText(R, *Result.Context) != D->getName()) > + return CharSourceRange(); > + return R; > + } > + if (const auto *E = Node.get<DeclRefExpr>()) { > + if (!E->getNameInfo().getName().isIdentifier()) > + return missingPropertyError(ID, "name", "identifier"); > + SourceLocation L = E->getLocation(); > + return CharSourceRange::getTokenRange(L, L); > + } > + if (const auto *I = Node.get<CXXCtorInitializer>()) { > + if (!I->isMemberInitializer() && I->isWritten()) > + return missingPropertyError(ID, "name", "explicit member > initializer"); > + SourceLocation L = I->getMemberLocation(); > + return CharSourceRange::getTokenRange(L, L); > + } > + return typeError(ID, Node.getNodeKind(), > + "DeclRefExpr, NamedDecl, CXXCtorInitializer"); > + }; > +} > + > +namespace { > +// Creates a selector from a range-selection function \p Func, which > selects a > +// range that is relative to a bound node id. \c T is the node type > expected by > +// \p Func. > +template <typename T, CharSourceRange (*Func)(const MatchResult &, const > T &)> > +class RelativeSelector { > + std::string ID; > + > +public: > + RelativeSelector(StringRef ID) : ID(ID) {} > + > + Expected<CharSourceRange> operator()(const MatchResult &Result) { > + Expected<DynTypedNode> N = getNode(Result.Nodes, ID); > + if (!N) > + return N.takeError(); > + if (const auto *Arg = N->get<T>()) > + return Func(Result, *Arg); > + return typeError(ID, N->getNodeKind()); > + } > +}; > +} // namespace > + > +// Returns the range of the statements (all source between the braces). > +static CharSourceRange getStatementsRange(const MatchResult &, > + const CompoundStmt &CS) { > + return > CharSourceRange::getCharRange(CS.getLBracLoc().getLocWithOffset(1), > + CS.getRBracLoc()); > +} > + > +RangeSelector tooling::statements(StringRef ID) { > + return RelativeSelector<CompoundStmt, getStatementsRange>(ID); > +} > + > +// Returns the range of the source between the call's parentheses. > +static CharSourceRange getCallArgumentsRange(const MatchResult &Result, > + const CallExpr &CE) { > + return CharSourceRange::getCharRange( > + findOpenParen(CE, *Result.SourceManager, > Result.Context->getLangOpts()) > + .getLocWithOffset(1), > + CE.getRParenLoc()); > +} > + > +RangeSelector tooling::callArgs(StringRef ID) { > + return RelativeSelector<CallExpr, getCallArgumentsRange>(ID); > +} > + > +// Returns the range of the elements of the initializer list. Includes all > +// source between the braces. > +static CharSourceRange getElementsRange(const MatchResult &, > + const InitListExpr &E) { > + return > CharSourceRange::getCharRange(E.getLBraceLoc().getLocWithOffset(1), > + E.getRBraceLoc()); > +} > + > +RangeSelector tooling::initListElements(StringRef ID) { > + return RelativeSelector<InitListExpr, getElementsRange>(ID); > +} > + > +RangeSelector tooling::expansion(RangeSelector S) { > + return [S](const MatchResult &Result) -> Expected<CharSourceRange> { > + Expected<CharSourceRange> SRange = S(Result); > + if (!SRange) > + return SRange.takeError(); > + return Result.SourceManager->getExpansionRange(*SRange); > + }; > +} > > Modified: cfe/trunk/unittests/Tooling/CMakeLists.txt > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/CMakeLists.txt?rev=361152&r1=361151&r2=361152&view=diff > > ============================================================================== > --- cfe/trunk/unittests/Tooling/CMakeLists.txt (original) > +++ cfe/trunk/unittests/Tooling/CMakeLists.txt Mon May 20 06:15:14 2019 > @@ -22,6 +22,7 @@ add_clang_unittest(ToolingTests > LexicallyOrderedRecursiveASTVisitorTest.cpp > LookupTest.cpp > QualTypeNamesTest.cpp > + RangeSelectorTest.cpp > RecursiveASTVisitorTests/Attr.cpp > RecursiveASTVisitorTests/Class.cpp > RecursiveASTVisitorTests/ConstructExpr.cpp > @@ -69,6 +70,8 @@ target_link_libraries(ToolingTests > clangToolingCore > clangToolingInclusions > clangToolingRefactor > + LLVMSupport > + LLVMTestingSupport > ) > > > > Added: cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp?rev=361152&view=auto > > ============================================================================== > --- cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp (added) > +++ cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp Mon May 20 06:15:14 > 2019 > @@ -0,0 +1,496 @@ > +//===- 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/Refactoring/RangeSelector.h" > +#include "clang/ASTMatchers/ASTMatchers.h" > +#include "clang/Tooling/FixIt.h" > +#include "clang/Tooling/Tooling.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 tooling; > +using namespace ast_matchers; > + > +namespace { > +using ::testing::AllOf; > +using ::testing::HasSubstr; > +using MatchResult = MatchFinder::MatchResult; > +using ::llvm::Expected; > +using ::llvm::Failed; > +using ::llvm::HasValue; > +using ::llvm::StringError; > + > +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<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 = 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> apply(RangeSelector Selector, const TestMatch &Match) > { > + Expected<CharSourceRange> Range = Selector(Match.Result); > + if (!Range) > + return Range.takeError(); > + return fixit::internal::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> applyToTrivial(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> applyToAssorted(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(applyToTrivial(node("unbound_id")), > + Failed<StringError>(withUnboundNodeMessage())); > +} > + > +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(apply(range(Arg0, Arg1), Match), HasValue("3, 7")); > + // General version: > + EXPECT_THAT_EXPECTED(apply(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(apply(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(apply(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(apply(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(apply(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(apply(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(apply(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(apply(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(apply(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(apply(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(apply(name(Init), Match), HasValue("field")); > +} > + > +TEST(RangeSelectorTest, NameOpErrors) { > + EXPECT_THAT_EXPECTED(applyToTrivial(name("unbound_id")), > + Failed<StringError>(withUnboundNodeMessage())); > + EXPECT_THAT_EXPECTED(applyToAssorted(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(apply(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(apply(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(apply(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(apply(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(apply(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(apply(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)); > + EXPECT_THAT_EXPECTED(apply(callArgs(ID), Match), HasValue(R"( // Header > + 1, // foo > + 2 // bar > + )")); > +} > + > +TEST(RangeSelectorTest, CallArgsErrors) { > + EXPECT_THAT_EXPECTED(applyToTrivial(callArgs("unbound_id")), > + Failed<StringError>(withUnboundNodeMessage())); > + EXPECT_THAT_EXPECTED(applyToAssorted(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( > + apply(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(apply(statements(ID), Match), HasValue("")); > +} > + > +TEST(RangeSelectorTest, StatementsOpErrors) { > + EXPECT_THAT_EXPECTED(applyToTrivial(statements("unbound_id")), > + Failed<StringError>(withUnboundNodeMessage())); > + EXPECT_THAT_EXPECTED(applyToAssorted(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( > + apply(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(apply(initListElements(ID), Match), HasValue("")); > +} > + > +TEST(RangeSelectorTest, ElementsOpErrors) { > + EXPECT_THAT_EXPECTED(applyToTrivial(initListElements("unbound_id")), > + Failed<StringError>(withUnboundNodeMessage())); > + EXPECT_THAT_EXPECTED(applyToAssorted(initListElements("stmt")), > + Failed<StringError>(withTypeErrorMessage("stmt"))); > +} > + > +// 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(apply(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(apply(expansion(node(Ret)), Match), > + HasValue("BADDECL(x * x)")); > +} > + > +} // namespace > > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits