Nice, thanks for r361208. As for support -- no, I didn't try that. I was following a pattern I saw elsewhere. Do you suggest that I make that change? If so, any particular way to test it?
thanks On Mon, May 20, 2019 at 8:26 PM Nico Weber <tha...@chromium.org> wrote: > + 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 >> >
smime.p7s
Description: S/MIME Cryptographic Signature
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits