Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Transformer/RangeSelector.cpp
35269 views
//===--- RangeSelector.cpp - RangeSelector implementations ------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Tooling/Transformer/RangeSelector.h"9#include "clang/AST/Expr.h"10#include "clang/AST/TypeLoc.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/Basic/SourceLocation.h"13#include "clang/Lex/Lexer.h"14#include "clang/Tooling/Transformer/SourceCode.h"15#include "llvm/ADT/StringRef.h"16#include "llvm/Support/Errc.h"17#include "llvm/Support/Error.h"18#include <string>19#include <utility>20#include <vector>2122using namespace clang;23using namespace transformer;2425using ast_matchers::MatchFinder;26using llvm::Error;27using llvm::StringError;2829using MatchResult = MatchFinder::MatchResult;3031static Error invalidArgumentError(Twine Message) {32return llvm::make_error<StringError>(llvm::errc::invalid_argument, Message);33}3435static Error typeError(StringRef ID, const ASTNodeKind &Kind) {36return invalidArgumentError("mismatched type (node id=" + ID +37" kind=" + Kind.asStringRef() + ")");38}3940static Error typeError(StringRef ID, const ASTNodeKind &Kind,41Twine ExpectedType) {42return invalidArgumentError("mismatched type: expected one of " +43ExpectedType + " (node id=" + ID +44" kind=" + Kind.asStringRef() + ")");45}4647static Error missingPropertyError(StringRef ID, Twine Description,48StringRef Property) {49return invalidArgumentError(Description + " requires property '" + Property +50"' (node id=" + ID + ")");51}5253static Expected<DynTypedNode> getNode(const ast_matchers::BoundNodes &Nodes,54StringRef ID) {55auto &NodesMap = Nodes.getMap();56auto It = NodesMap.find(ID);57if (It == NodesMap.end())58return invalidArgumentError("ID not bound: " + ID);59return It->second;60}6162// FIXME: handling of macros should be configurable.63static SourceLocation findPreviousTokenStart(SourceLocation Start,64const SourceManager &SM,65const LangOptions &LangOpts) {66if (Start.isInvalid() || Start.isMacroID())67return SourceLocation();6869SourceLocation BeforeStart = Start.getLocWithOffset(-1);70if (BeforeStart.isInvalid() || BeforeStart.isMacroID())71return SourceLocation();7273return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts);74}7576// Finds the start location of the previous token of kind \p TK.77// FIXME: handling of macros should be configurable.78static SourceLocation findPreviousTokenKind(SourceLocation Start,79const SourceManager &SM,80const LangOptions &LangOpts,81tok::TokenKind TK) {82while (true) {83SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);84if (L.isInvalid() || L.isMacroID())85return SourceLocation();8687Token T;88if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true))89return SourceLocation();9091if (T.is(TK))92return T.getLocation();9394Start = L;95}96}9798RangeSelector transformer::before(RangeSelector Selector) {99return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {100Expected<CharSourceRange> SelectedRange = Selector(Result);101if (!SelectedRange)102return SelectedRange.takeError();103return CharSourceRange::getCharRange(SelectedRange->getBegin());104};105}106107RangeSelector transformer::after(RangeSelector Selector) {108return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {109Expected<CharSourceRange> SelectedRange = Selector(Result);110if (!SelectedRange)111return SelectedRange.takeError();112SourceLocation End = SelectedRange->getEnd();113if (SelectedRange->isTokenRange()) {114// We need to find the actual (exclusive) end location from which to115// create a new source range. However, that's not guaranteed to be valid,116// even if the token location itself is valid. So, we create a token range117// consisting only of the last token, then map that range back to the118// source file. If that succeeds, we have a valid location for the end of119// the generated range.120CharSourceRange Range = Lexer::makeFileCharRange(121CharSourceRange::getTokenRange(SelectedRange->getEnd()),122*Result.SourceManager, Result.Context->getLangOpts());123if (Range.isInvalid())124return invalidArgumentError(125"after: can't resolve sub-range to valid source range");126End = Range.getEnd();127}128129return CharSourceRange::getCharRange(End);130};131}132133RangeSelector transformer::node(std::string ID) {134return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {135Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);136if (!Node)137return Node.takeError();138return (Node->get<Decl>() != nullptr ||139(Node->get<Stmt>() != nullptr && Node->get<Expr>() == nullptr))140? tooling::getExtendedRange(*Node, tok::TokenKind::semi,141*Result.Context)142: CharSourceRange::getTokenRange(Node->getSourceRange());143};144}145146RangeSelector transformer::statement(std::string ID) {147return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {148Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);149if (!Node)150return Node.takeError();151return tooling::getExtendedRange(*Node, tok::TokenKind::semi,152*Result.Context);153};154}155156RangeSelector transformer::enclose(RangeSelector Begin, RangeSelector End) {157return [Begin, End](const MatchResult &Result) -> Expected<CharSourceRange> {158Expected<CharSourceRange> BeginRange = Begin(Result);159if (!BeginRange)160return BeginRange.takeError();161Expected<CharSourceRange> EndRange = End(Result);162if (!EndRange)163return EndRange.takeError();164SourceLocation B = BeginRange->getBegin();165SourceLocation E = EndRange->getEnd();166// Note: we are precluding the possibility of sub-token ranges in the case167// that EndRange is a token range.168if (Result.SourceManager->isBeforeInTranslationUnit(E, B)) {169return invalidArgumentError("Bad range: out of order");170}171return CharSourceRange(SourceRange(B, E), EndRange->isTokenRange());172};173}174175RangeSelector transformer::encloseNodes(std::string BeginID,176std::string EndID) {177return transformer::enclose(node(std::move(BeginID)), node(std::move(EndID)));178}179180RangeSelector transformer::member(std::string ID) {181return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {182Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);183if (!Node)184return Node.takeError();185if (auto *M = Node->get<clang::MemberExpr>())186return CharSourceRange::getTokenRange(187M->getMemberNameInfo().getSourceRange());188return typeError(ID, Node->getNodeKind(), "MemberExpr");189};190}191192RangeSelector transformer::name(std::string ID) {193return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {194Expected<DynTypedNode> N = getNode(Result.Nodes, ID);195if (!N)196return N.takeError();197auto &Node = *N;198if (const auto *D = Node.get<NamedDecl>()) {199if (!D->getDeclName().isIdentifier())200return missingPropertyError(ID, "name", "identifier");201SourceLocation L = D->getLocation();202auto R = CharSourceRange::getTokenRange(L, L);203// Verify that the range covers exactly the name.204// FIXME: extend this code to support cases like `operator +` or205// `foo<int>` for which this range will be too short. Doing so will206// require subcasing `NamedDecl`, because it doesn't provide virtual207// access to the \c DeclarationNameInfo.208if (tooling::getText(R, *Result.Context) != D->getName())209return CharSourceRange();210return R;211}212if (const auto *E = Node.get<DeclRefExpr>()) {213if (!E->getNameInfo().getName().isIdentifier())214return missingPropertyError(ID, "name", "identifier");215SourceLocation L = E->getLocation();216return CharSourceRange::getTokenRange(L, L);217}218if (const auto *I = Node.get<CXXCtorInitializer>()) {219if (!I->isMemberInitializer() && I->isWritten())220return missingPropertyError(ID, "name", "explicit member initializer");221SourceLocation L = I->getMemberLocation();222return CharSourceRange::getTokenRange(L, L);223}224if (const auto *T = Node.get<TypeLoc>()) {225TypeLoc Loc = *T;226auto ET = Loc.getAs<ElaboratedTypeLoc>();227if (!ET.isNull())228Loc = ET.getNamedTypeLoc();229if (auto SpecLoc = Loc.getAs<TemplateSpecializationTypeLoc>();230!SpecLoc.isNull())231return CharSourceRange::getTokenRange(SpecLoc.getTemplateNameLoc());232return CharSourceRange::getTokenRange(Loc.getSourceRange());233}234return typeError(ID, Node.getNodeKind(),235"DeclRefExpr, NamedDecl, CXXCtorInitializer, TypeLoc");236};237}238239namespace {240// FIXME: make this available in the public API for users to easily create their241// own selectors.242243// Creates a selector from a range-selection function \p Func, which selects a244// range that is relative to a bound node id. \c T is the node type expected by245// \p Func.246template <typename T, CharSourceRange (*Func)(const MatchResult &, const T &)>247class RelativeSelector {248std::string ID;249250public:251RelativeSelector(std::string ID) : ID(std::move(ID)) {}252253Expected<CharSourceRange> operator()(const MatchResult &Result) {254Expected<DynTypedNode> N = getNode(Result.Nodes, ID);255if (!N)256return N.takeError();257if (const auto *Arg = N->get<T>())258return Func(Result, *Arg);259return typeError(ID, N->getNodeKind());260}261};262} // namespace263264// FIXME: Change the following functions from being in an anonymous namespace265// to static functions, after the minimum Visual C++ has _MSC_VER >= 1915266// (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous267// namespace works around a bug in earlier versions.268namespace {269// Returns the range of the statements (all source between the braces).270CharSourceRange getStatementsRange(const MatchResult &,271const CompoundStmt &CS) {272return CharSourceRange::getCharRange(CS.getLBracLoc().getLocWithOffset(1),273CS.getRBracLoc());274}275} // namespace276277RangeSelector transformer::statements(std::string ID) {278return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID));279}280281namespace {282283SourceLocation getRLoc(const CallExpr &E) { return E.getRParenLoc(); }284285SourceLocation getRLoc(const CXXConstructExpr &E) {286return E.getParenOrBraceRange().getEnd();287}288289tok::TokenKind getStartToken(const CallExpr &E) {290return tok::TokenKind::l_paren;291}292293tok::TokenKind getStartToken(const CXXConstructExpr &E) {294return isa<CXXTemporaryObjectExpr>(E) ? tok::TokenKind::l_paren295: tok::TokenKind::l_brace;296}297298template <typename ExprWithArgs>299SourceLocation findArgStartDelimiter(const ExprWithArgs &E, SourceLocation RLoc,300const SourceManager &SM,301const LangOptions &LangOpts) {302SourceLocation Loc = E.getNumArgs() == 0 ? RLoc : E.getArg(0)->getBeginLoc();303return findPreviousTokenKind(Loc, SM, LangOpts, getStartToken(E));304}305// Returns the range of the source between the call's or construct expr's306// parentheses/braces.307template <typename ExprWithArgs>308CharSourceRange getArgumentsRange(const MatchResult &Result,309const ExprWithArgs &CE) {310const SourceLocation RLoc = getRLoc(CE);311return CharSourceRange::getCharRange(312findArgStartDelimiter(CE, RLoc, *Result.SourceManager,313Result.Context->getLangOpts())314.getLocWithOffset(1),315RLoc);316}317} // namespace318319RangeSelector transformer::callArgs(std::string ID) {320return RelativeSelector<CallExpr, getArgumentsRange<CallExpr>>(std::move(ID));321}322323RangeSelector transformer::constructExprArgs(std::string ID) {324return RelativeSelector<CXXConstructExpr,325getArgumentsRange<CXXConstructExpr>>(std::move(ID));326}327328namespace {329// Returns the range of the elements of the initializer list. Includes all330// source between the braces.331CharSourceRange getElementsRange(const MatchResult &,332const InitListExpr &E) {333return CharSourceRange::getCharRange(E.getLBraceLoc().getLocWithOffset(1),334E.getRBraceLoc());335}336} // namespace337338RangeSelector transformer::initListElements(std::string ID) {339return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID));340}341342namespace {343// Returns the range of the else branch, including the `else` keyword.344CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) {345return tooling::maybeExtendRange(346CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()),347tok::TokenKind::semi, *Result.Context);348}349} // namespace350351RangeSelector transformer::elseBranch(std::string ID) {352return RelativeSelector<IfStmt, getElseRange>(std::move(ID));353}354355RangeSelector transformer::expansion(RangeSelector S) {356return [S](const MatchResult &Result) -> Expected<CharSourceRange> {357Expected<CharSourceRange> SRange = S(Result);358if (!SRange)359return SRange.takeError();360return Result.SourceManager->getExpansionRange(*SRange);361};362}363364365