clang  14.0.0git
RangeSelector.cpp
Go to the documentation of this file.
1 //===--- RangeSelector.cpp - RangeSelector implementations ------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/AST/Expr.h"
11 #include "clang/AST/TypeLoc.h"
14 #include "clang/Lex/Lexer.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/Errc.h"
18 #include "llvm/Support/Error.h"
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 using namespace clang;
24 using namespace transformer;
25 
27 using llvm::Error;
28 using llvm::StringError;
29 
31 
32 static Error invalidArgumentError(Twine Message) {
33  return llvm::make_error<StringError>(llvm::errc::invalid_argument, Message);
34 }
35 
36 static Error typeError(StringRef ID, const ASTNodeKind &Kind) {
37  return invalidArgumentError("mismatched type (node id=" + ID +
38  " kind=" + Kind.asStringRef() + ")");
39 }
40 
41 static Error typeError(StringRef ID, const ASTNodeKind &Kind,
42  Twine ExpectedType) {
43  return invalidArgumentError("mismatched type: expected one of " +
44  ExpectedType + " (node id=" + ID +
45  " kind=" + Kind.asStringRef() + ")");
46 }
47 
48 static Error missingPropertyError(StringRef ID, Twine Description,
49  StringRef Property) {
50  return invalidArgumentError(Description + " requires property '" + Property +
51  "' (node id=" + ID + ")");
52 }
53 
55  StringRef ID) {
56  auto &NodesMap = Nodes.getMap();
57  auto It = NodesMap.find(ID);
58  if (It == NodesMap.end())
59  return invalidArgumentError("ID not bound: " + ID);
60  return It->second;
61 }
62 
63 // FIXME: handling of macros should be configurable.
65  const SourceManager &SM,
66  const LangOptions &LangOpts) {
67  if (Start.isInvalid() || Start.isMacroID())
68  return SourceLocation();
69 
70  SourceLocation BeforeStart = Start.getLocWithOffset(-1);
71  if (BeforeStart.isInvalid() || BeforeStart.isMacroID())
72  return SourceLocation();
73 
74  return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts);
75 }
76 
77 // Finds the start location of the previous token of kind \p TK.
78 // FIXME: handling of macros should be configurable.
80  const SourceManager &SM,
81  const LangOptions &LangOpts,
82  tok::TokenKind TK) {
83  while (true) {
84  SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);
85  if (L.isInvalid() || L.isMacroID())
86  return SourceLocation();
87 
88  Token T;
89  if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true))
90  return SourceLocation();
91 
92  if (T.is(TK))
93  return T.getLocation();
94 
95  Start = L;
96  }
97 }
98 
100  const LangOptions &LangOpts) {
101  SourceLocation EndLoc =
102  E.getNumArgs() == 0 ? E.getRParenLoc() : E.getArg(0)->getBeginLoc();
103  return findPreviousTokenKind(EndLoc, SM, LangOpts, tok::TokenKind::l_paren);
104 }
105 
107  return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
108  Expected<CharSourceRange> SelectedRange = Selector(Result);
109  if (!SelectedRange)
110  return SelectedRange.takeError();
111  return CharSourceRange::getCharRange(SelectedRange->getBegin());
112  };
113 }
114 
116  return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
117  Expected<CharSourceRange> SelectedRange = Selector(Result);
118  if (!SelectedRange)
119  return SelectedRange.takeError();
120  SourceLocation End = SelectedRange->getEnd();
121  if (SelectedRange->isTokenRange()) {
122  // We need to find the actual (exclusive) end location from which to
123  // create a new source range. However, that's not guaranteed to be valid,
124  // even if the token location itself is valid. So, we create a token range
125  // consisting only of the last token, then map that range back to the
126  // source file. If that succeeds, we have a valid location for the end of
127  // the generated range.
129  CharSourceRange::getTokenRange(SelectedRange->getEnd()),
130  *Result.SourceManager, Result.Context->getLangOpts());
131  if (Range.isInvalid())
132  return invalidArgumentError(
133  "after: can't resolve sub-range to valid source range");
134  End = Range.getEnd();
135  }
136 
138  };
139 }
140 
142  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
143  Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
144  if (!Node)
145  return Node.takeError();
146  return (Node->get<Decl>() != nullptr ||
147  (Node->get<Stmt>() != nullptr && Node->get<Expr>() == nullptr))
148  ? tooling::getExtendedRange(*Node, tok::TokenKind::semi,
149  *Result.Context)
151  };
152 }
153 
155  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
156  Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
157  if (!Node)
158  return Node.takeError();
159  return tooling::getExtendedRange(*Node, tok::TokenKind::semi,
160  *Result.Context);
161  };
162 }
163 
165  return [Begin, End](const MatchResult &Result) -> Expected<CharSourceRange> {
166  Expected<CharSourceRange> BeginRange = Begin(Result);
167  if (!BeginRange)
168  return BeginRange.takeError();
169  Expected<CharSourceRange> EndRange = End(Result);
170  if (!EndRange)
171  return EndRange.takeError();
172  SourceLocation B = BeginRange->getBegin();
173  SourceLocation E = EndRange->getEnd();
174  // Note: we are precluding the possibility of sub-token ranges in the case
175  // that EndRange is a token range.
176  if (Result.SourceManager->isBeforeInTranslationUnit(E, B)) {
177  return invalidArgumentError("Bad range: out of order");
178  }
179  return CharSourceRange(SourceRange(B, E), EndRange->isTokenRange());
180  };
181 }
182 
184  std::string EndID) {
185  return transformer::enclose(node(std::move(BeginID)), node(std::move(EndID)));
186 }
187 
189  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
190  Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
191  if (!Node)
192  return Node.takeError();
193  if (auto *M = Node->get<clang::MemberExpr>())
195  M->getMemberNameInfo().getSourceRange());
196  return typeError(ID, Node->getNodeKind(), "MemberExpr");
197  };
198 }
199 
201  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
202  Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
203  if (!N)
204  return N.takeError();
205  auto &Node = *N;
206  if (const auto *D = Node.get<NamedDecl>()) {
207  if (!D->getDeclName().isIdentifier())
208  return missingPropertyError(ID, "name", "identifier");
209  SourceLocation L = D->getLocation();
210  auto R = CharSourceRange::getTokenRange(L, L);
211  // Verify that the range covers exactly the name.
212  // FIXME: extend this code to support cases like `operator +` or
213  // `foo<int>` for which this range will be too short. Doing so will
214  // require subcasing `NamedDecl`, because it doesn't provide virtual
215  // access to the \c DeclarationNameInfo.
216  if (tooling::getText(R, *Result.Context) != D->getName())
217  return CharSourceRange();
218  return R;
219  }
220  if (const auto *E = Node.get<DeclRefExpr>()) {
221  if (!E->getNameInfo().getName().isIdentifier())
222  return missingPropertyError(ID, "name", "identifier");
223  SourceLocation L = E->getLocation();
224  return CharSourceRange::getTokenRange(L, L);
225  }
226  if (const auto *I = Node.get<CXXCtorInitializer>()) {
227  if (!I->isMemberInitializer() && I->isWritten())
228  return missingPropertyError(ID, "name", "explicit member initializer");
229  SourceLocation L = I->getMemberLocation();
230  return CharSourceRange::getTokenRange(L, L);
231  }
232  if (const auto *T = Node.get<TypeLoc>()) {
233  TypeLoc Loc = *T;
234  auto ET = Loc.getAs<ElaboratedTypeLoc>();
235  if (!ET.isNull()) {
236  Loc = ET.getNamedTypeLoc();
237  }
239  }
240  return typeError(ID, Node.getNodeKind(),
241  "DeclRefExpr, NamedDecl, CXXCtorInitializer, TypeLoc");
242  };
243 }
244 
245 namespace {
246 // FIXME: make this available in the public API for users to easily create their
247 // own selectors.
248 
249 // Creates a selector from a range-selection function \p Func, which selects a
250 // range that is relative to a bound node id. \c T is the node type expected by
251 // \p Func.
252 template <typename T, CharSourceRange (*Func)(const MatchResult &, const T &)>
253 class RelativeSelector {
254  std::string ID;
255 
256 public:
257  RelativeSelector(std::string ID) : ID(std::move(ID)) {}
258 
259  Expected<CharSourceRange> operator()(const MatchResult &Result) {
260  Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
261  if (!N)
262  return N.takeError();
263  if (const auto *Arg = N->get<T>())
264  return Func(Result, *Arg);
265  return typeError(ID, N->getNodeKind());
266  }
267 };
268 } // namespace
269 
270 // FIXME: Change the following functions from being in an anonymous namespace
271 // to static functions, after the minimum Visual C++ has _MSC_VER >= 1915
272 // (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous
273 // namespace works around a bug in earlier versions.
274 namespace {
275 // Returns the range of the statements (all source between the braces).
276 CharSourceRange getStatementsRange(const MatchResult &,
277  const CompoundStmt &CS) {
279  CS.getRBracLoc());
280 }
281 } // namespace
282 
284  return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID));
285 }
286 
287 namespace {
288 // Returns the range of the source between the call's parentheses.
289 CharSourceRange getCallArgumentsRange(const MatchResult &Result,
290  const CallExpr &CE) {
292  findOpenParen(CE, *Result.SourceManager, Result.Context->getLangOpts())
293  .getLocWithOffset(1),
294  CE.getRParenLoc());
295 }
296 } // namespace
297 
299  return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
300 }
301 
302 namespace {
303 // Returns the range of the elements of the initializer list. Includes all
304 // source between the braces.
305 CharSourceRange getElementsRange(const MatchResult &,
306  const InitListExpr &E) {
308  E.getRBraceLoc());
309 }
310 } // namespace
311 
313  return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID));
314 }
315 
316 namespace {
317 // Returns the range of the else branch, including the `else` keyword.
318 CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) {
320  CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()),
321  tok::TokenKind::semi, *Result.Context);
322 }
323 } // namespace
324 
326  return RelativeSelector<IfStmt, getElseRange>(std::move(ID));
327 }
328 
330  return [S](const MatchResult &Result) -> Expected<CharSourceRange> {
331  Expected<CharSourceRange> SRange = S(Result);
332  if (!SRange)
333  return SRange.takeError();
334  return Result.SourceManager->getExpansionRange(*SRange);
335  };
336 }
clang::Lexer::makeFileCharRange
static CharSourceRange makeFileCharRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
Accepts a range and returns a character range with file locations.
Definition: Lexer.cpp:888
Nodes
BoundNodesTreeBuilder Nodes
Definition: ASTMatchFinder.cpp:82
Error
llvm::Error Error
Definition: ByteCodeEmitter.cpp:20
clang::SourceRange
A trivial tuple used to represent a source range.
Definition: SourceLocation.h:212
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
clang::Lexer::getRawToken
static bool getRawToken(SourceLocation Loc, Token &Result, const SourceManager &SM, const LangOptions &LangOpts, bool IgnoreWhiteSpace=false)
Relex the token at the specified location.
Definition: Lexer.cpp:458
clang::InitListExpr::getRBraceLoc
SourceLocation getRBraceLoc() const
Definition: Expr.h:4937
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:88
clang::ast_matchers::MatchFinder
A class to allow finding matches over the Clang AST.
Definition: ASTMatchFinder.h:68
clang::SourceLocation::getLocWithOffset
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
Definition: SourceLocation.h:136
clang::NamedDecl
This represents a decl that may have a name.
Definition: Decl.h:249
clang::DynTypedNode::getSourceRange
SourceRange getSourceRange() const
For nodes which represent textual entities in the source code, return their SourceRange.
Definition: ASTTypeTraits.cpp:197
RangeSelector.h
findOpenParen
static SourceLocation findOpenParen(const CallExpr &E, const SourceManager &SM, const LangOptions &LangOpts)
Definition: RangeSelector.cpp:99
findPreviousTokenKind
static SourceLocation findPreviousTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, tok::TokenKind TK)
Definition: RangeSelector.cpp:79
clang::transformer::initListElements
RangeSelector initListElements(std::string ID)
Definition: RangeSelector.cpp:312
clang::IfStmt
IfStmt - This represents an if/then/else.
Definition: Stmt.h:1903
clang::InitListExpr
Describes an C or C++ initializer list.
Definition: Expr.h:4786
clang::Decl::getLangOpts
const LangOptions & getLangOpts() const LLVM_READONLY
Helper to get the language options from the ASTContext.
Definition: DeclBase.cpp:420
clang::CallExpr::getRParenLoc
SourceLocation getRParenLoc() const
Definition: Expr.h:3109
llvm::Expected
Definition: LLVM.h:41
ASTMatchFinder.h
clang::CompoundStmt::getLBracLoc
SourceLocation getLBracLoc() const
Definition: Stmt.h:1505
clang::transformer::elseBranch
RangeSelector elseBranch(std::string ID)
Given an \IfStmt (bound to ID), selects the range of the else branch, starting from the else keyword.
Definition: RangeSelector.cpp:325
getNode
static Expected< DynTypedNode > getNode(const ast_matchers::BoundNodes &Nodes, StringRef ID)
Definition: RangeSelector.cpp:54
clang::Token
Token - This structure provides full information about a lexed token.
Definition: Token.h:34
End
SourceLocation End
Definition: USRLocFinder.cpp:167
clang::transformer::RangeSelector
MatchConsumer< CharSourceRange > RangeSelector
Definition: RangeSelector.h:27
clang::DynTypedNode::getNodeKind
ASTNodeKind getNodeKind() const
Definition: ASTTypeTraits.h:270
clang::SourceManager
This class handles loading and caching of source files into memory.
Definition: SourceManager.h:626
clang::CharSourceRange::getCharRange
static CharSourceRange getCharRange(SourceRange R)
Definition: SourceLocation.h:267
clang::CompoundStmt
CompoundStmt - This represents a group of statements like { stmt stmt }.
Definition: Stmt.h:1398
Node
DynTypedNode Node
Definition: ASTMatchFinder.cpp:67
MatchResult
MatchFinder::MatchResult MatchResult
Definition: RangeSelector.cpp:30
clang::transformer::statement
RangeSelector statement(std::string ID)
Selects a node, including trailing semicolon (always).
Definition: RangeSelector.cpp:154
clang::CallExpr::getArg
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2986
typeError
static Error typeError(StringRef ID, const ASTNodeKind &Kind)
Definition: RangeSelector.cpp:36
clang::Token::is
bool is(tok::TokenKind K) const
is/isNot - Predicates to check if this token is a specific kind, as in "if (Tok.is(tok::l_brace)) {....
Definition: Token.h:97
clang::tooling::maybeExtendRange
CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Terminator, ASTContext &Context)
Extends Range to include the token Terminator, if it immediately follows the end of the range.
Definition: SourceCode.cpp:37
Expr.h
clang::tooling::getExtendedRange
CharSourceRange getExtendedRange(const T &Node, tok::TokenKind Next, ASTContext &Context)
Returns the source range spanning the node, extended to include Next, if it immediately follows Node.
Definition: SourceCode.h:33
invalidArgumentError
static Error invalidArgumentError(Twine Message)
Definition: RangeSelector.cpp:32
clang::transformer::EditKind::Range
@ Range
missingPropertyError
static Error missingPropertyError(StringRef ID, Twine Description, StringRef Property)
Definition: RangeSelector.cpp:48
SourceLocation.h
clang::tok::TokenKind
TokenKind
Provides a simple uniform namespace for tokens from all C languages.
Definition: TokenKinds.h:25
clang::transformer::expansion
RangeSelector expansion(RangeSelector S)
Selects the range from which S was expanded (possibly along with other source), if S is an expansion,...
Definition: RangeSelector.cpp:329
clang::CompoundStmt::getRBracLoc
SourceLocation getRBracLoc() const
Definition: Stmt.h:1506
clang::CharSourceRange::getTokenRange
static CharSourceRange getTokenRange(SourceRange R)
Definition: SourceLocation.h:263
clang::CallExpr::getNumArgs
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2973
clang::TypeLoc::getAs
T getAs() const
Convert to the specified TypeLoc type, returning a null TypeLoc if this TypeLoc is not of the desired...
Definition: TypeLoc.h:88
SourceCode.h
Begin
SourceLocation Begin
Definition: USRLocFinder.cpp:165
Lexer.h
clang::Decl
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:89
clang::TypeLoc
Base wrapper for a particular "section" of type source info.
Definition: TypeLoc.h:58
clang::CharSourceRange
Represents a character-granular source range.
Definition: SourceLocation.h:255
clang::transformer::after
RangeSelector after(RangeSelector Selector)
Selects the point immediately following Selector.
Definition: RangeSelector.cpp:115
clang::transformer::member
RangeSelector member(std::string ID)
Given a MemberExpr, selects the member token.
Definition: RangeSelector.cpp:188
clang::Token::getLocation
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
Definition: Token.h:126
clang::ast_matchers::MatchFinder::MatchResult
Contains all information for a given match.
Definition: ASTMatchFinder.h:74
clang::SourceLocation::isMacroID
bool isMacroID() const
Definition: SourceLocation.h:105
clang::LangOptions
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:58
clang::ObjCPropertyAttribute::Kind
Kind
Definition: DeclObjCCommon.h:22
clang::ElaboratedTypeLoc
Definition: TypeLoc.h:2198
std
Definition: Format.h:3899
clang::ASTNodeKind
Kind identifier.
Definition: ASTTypeTraits.h:50
clang::Builtin::ID
ID
Definition: Builtins.h:48
clang::SourceLocation::isInvalid
bool isInvalid() const
Definition: SourceLocation.h:113
clang
Definition: CalledOnceCheck.h:17
clang::Selector
Smart pointer class that efficiently represents Objective-C method names.
Definition: IdentifierTable.h:720
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:68
clang::Lexer::GetBeginningOfToken
static SourceLocation GetBeginningOfToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Given a location any where in a source buffer, find the location that corresponds to the beginning of...
Definition: Lexer.cpp:557
clang::tooling::getText
StringRef getText(CharSourceRange Range, const ASTContext &Context)
Returns the source-code text in the specified range.
Definition: SourceCode.cpp:31
findPreviousTokenStart
static SourceLocation findPreviousTokenStart(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
Definition: RangeSelector.cpp:64
clang::transformer::callArgs
RangeSelector callArgs(std::string ID)
Definition: RangeSelector.cpp:298
clang::InitListExpr::getLBraceLoc
SourceLocation getLBraceLoc() const
Definition: Expr.h:4935
clang::transformer::enclose
RangeSelector enclose(RangeSelector Begin, RangeSelector End)
Selects from the start of Begin and to the end of End.
Definition: RangeSelector.cpp:164
clang::Stmt::getBeginLoc
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:336
clang::ObjCSubstitutionContext::Property
@ Property
The type of a property.
clang::DynTypedNode::get
const T * get() const
Retrieve the stored node as type T.
Definition: ASTTypeTraits.h:258
clang::MemberExpr
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:3162
clang::TypeLoc::getSourceRange
SourceRange getSourceRange() const LLVM_READONLY
Get the full source range.
Definition: TypeLoc.h:152
clang::ast_matchers::BoundNodes
Maps string IDs to AST nodes matched by parts of a matcher.
Definition: ASTMatchers.h:107
clang::transformer::encloseNodes
RangeSelector encloseNodes(std::string BeginID, std::string EndID)
Convenience version of range where end-points are bound nodes.
Definition: RangeSelector.cpp:183
clang::transformer::statements
RangeSelector statements(std::string ID)
Definition: RangeSelector.cpp:283
clang::transformer::node
RangeSelector node(std::string ID)
Selects a node, including trailing semicolon, if any (for declarations and non-expression statements)...
Definition: RangeSelector.cpp:141
clang::Expr
This represents one expression.
Definition: Expr.h:109
SM
#define SM(sm)
Definition: Cuda.cpp:78
clang::CXXCtorInitializer
Represents a C++ base or member initializer.
Definition: DeclCXX.h:2172
clang::transformer::name
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
Definition: RangeSelector.cpp:200
clang::transformer::before
RangeSelector before(RangeSelector Selector)
Selects the (empty) range [B,B) when Selector selects the range [B,E).
Definition: RangeSelector.cpp:106
clang::DeclRefExpr
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1217
clang::CallExpr
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2795
TypeLoc.h