clang  10.0.0svn
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"
13 #include "clang/Lex/Lexer.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>
21 
22 using namespace clang;
23 using namespace tooling;
24 
28 using llvm::Error;
29 using llvm::StringError;
30 
32 
33 static Error invalidArgumentError(Twine Message) {
34  return llvm::make_error<StringError>(llvm::errc::invalid_argument, Message);
35 }
36 
37 static Error typeError(StringRef ID, const ASTNodeKind &Kind) {
38  return invalidArgumentError("mismatched type (node id=" + ID +
39  " kind=" + Kind.asStringRef() + ")");
40 }
41 
42 static Error typeError(StringRef ID, const ASTNodeKind &Kind,
43  Twine ExpectedType) {
44  return invalidArgumentError("mismatched type: expected one of " +
45  ExpectedType + " (node id=" + ID +
46  " kind=" + Kind.asStringRef() + ")");
47 }
48 
49 static Error missingPropertyError(StringRef ID, Twine Description,
50  StringRef Property) {
51  return invalidArgumentError(Description + " requires property '" + Property +
52  "' (node id=" + ID + ")");
53 }
54 
56  StringRef ID) {
57  auto &NodesMap = Nodes.getMap();
58  auto It = NodesMap.find(ID);
59  if (It == NodesMap.end())
60  return invalidArgumentError("ID not bound: " + ID);
61  return It->second;
62 }
63 
64 // FIXME: handling of macros should be configurable.
66  const SourceManager &SM,
67  const LangOptions &LangOpts) {
68  if (Start.isInvalid() || Start.isMacroID())
69  return SourceLocation();
70 
71  SourceLocation BeforeStart = Start.getLocWithOffset(-1);
72  if (BeforeStart.isInvalid() || BeforeStart.isMacroID())
73  return SourceLocation();
74 
75  return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts);
76 }
77 
78 // Finds the start location of the previous token of kind \p TK.
79 // FIXME: handling of macros should be configurable.
81  const SourceManager &SM,
82  const LangOptions &LangOpts,
83  tok::TokenKind TK) {
84  while (true) {
85  SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);
86  if (L.isInvalid() || L.isMacroID())
87  return SourceLocation();
88 
89  Token T;
90  if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true))
91  return SourceLocation();
92 
93  if (T.is(TK))
94  return T.getLocation();
95 
96  Start = L;
97  }
98 }
99 
101  const LangOptions &LangOpts) {
102  SourceLocation EndLoc =
103  E.getNumArgs() == 0 ? E.getRParenLoc() : E.getArg(0)->getBeginLoc();
104  return findPreviousTokenKind(EndLoc, SM, LangOpts, tok::TokenKind::l_paren);
105 }
106 
108  return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
109  Expected<CharSourceRange> SelectedRange = Selector(Result);
110  if (!SelectedRange)
111  return SelectedRange.takeError();
112  return CharSourceRange::getCharRange(SelectedRange->getBegin());
113  };
114 }
115 
117  return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
118  Expected<CharSourceRange> SelectedRange = Selector(Result);
119  if (!SelectedRange)
120  return SelectedRange.takeError();
121  if (SelectedRange->isCharRange())
122  return CharSourceRange::getCharRange(SelectedRange->getEnd());
124  SelectedRange->getEnd(), 0, Result.Context->getSourceManager(),
125  Result.Context->getLangOpts()));
126  };
127 }
128 
130  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
131  Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
132  if (!Node)
133  return Node.takeError();
134  return Node->get<Stmt>() != nullptr && Node->get<Expr>() == nullptr
135  ? getExtendedRange(*Node, tok::TokenKind::semi, *Result.Context)
136  : CharSourceRange::getTokenRange(Node->getSourceRange());
137  };
138 }
139 
141  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
142  Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
143  if (!Node)
144  return Node.takeError();
145  return getExtendedRange(*Node, tok::TokenKind::semi, *Result.Context);
146  };
147 }
148 
150  return [Begin, End](const MatchResult &Result) -> Expected<CharSourceRange> {
151  Expected<CharSourceRange> BeginRange = Begin(Result);
152  if (!BeginRange)
153  return BeginRange.takeError();
154  Expected<CharSourceRange> EndRange = End(Result);
155  if (!EndRange)
156  return EndRange.takeError();
157  SourceLocation B = BeginRange->getBegin();
158  SourceLocation E = EndRange->getEnd();
159  // Note: we are precluding the possibility of sub-token ranges in the case
160  // that EndRange is a token range.
161  if (Result.SourceManager->isBeforeInTranslationUnit(E, B)) {
162  return invalidArgumentError("Bad range: out of order");
163  }
164  return CharSourceRange(SourceRange(B, E), EndRange->isTokenRange());
165  };
166 }
167 
168 RangeSelector tooling::range(std::string BeginID, std::string EndID) {
169  return tooling::range(node(std::move(BeginID)), node(std::move(EndID)));
170 }
171 
173  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
174  Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
175  if (!Node)
176  return Node.takeError();
177  if (auto *M = Node->get<clang::MemberExpr>())
179  M->getMemberNameInfo().getSourceRange());
180  return typeError(ID, Node->getNodeKind(), "MemberExpr");
181  };
182 }
183 
185  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
186  Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
187  if (!N)
188  return N.takeError();
189  auto &Node = *N;
190  if (const auto *D = Node.get<NamedDecl>()) {
191  if (!D->getDeclName().isIdentifier())
192  return missingPropertyError(ID, "name", "identifier");
193  SourceLocation L = D->getLocation();
194  auto R = CharSourceRange::getTokenRange(L, L);
195  // Verify that the range covers exactly the name.
196  // FIXME: extend this code to support cases like `operator +` or
197  // `foo<int>` for which this range will be too short. Doing so will
198  // require subcasing `NamedDecl`, because it doesn't provide virtual
199  // access to the \c DeclarationNameInfo.
200  if (getText(R, *Result.Context) != D->getName())
201  return CharSourceRange();
202  return R;
203  }
204  if (const auto *E = Node.get<DeclRefExpr>()) {
205  if (!E->getNameInfo().getName().isIdentifier())
206  return missingPropertyError(ID, "name", "identifier");
207  SourceLocation L = E->getLocation();
208  return CharSourceRange::getTokenRange(L, L);
209  }
210  if (const auto *I = Node.get<CXXCtorInitializer>()) {
211  if (!I->isMemberInitializer() && I->isWritten())
212  return missingPropertyError(ID, "name", "explicit member initializer");
213  SourceLocation L = I->getMemberLocation();
214  return CharSourceRange::getTokenRange(L, L);
215  }
216  return typeError(ID, Node.getNodeKind(),
217  "DeclRefExpr, NamedDecl, CXXCtorInitializer");
218  };
219 }
220 
221 namespace {
222 // Creates a selector from a range-selection function \p Func, which selects a
223 // range that is relative to a bound node id. \c T is the node type expected by
224 // \p Func.
225 template <typename T, CharSourceRange (*Func)(const MatchResult &, const T &)>
226 class RelativeSelector {
227  std::string ID;
228 
229 public:
230  RelativeSelector(std::string ID) : ID(std::move(ID)) {}
231 
232  Expected<CharSourceRange> operator()(const MatchResult &Result) {
233  Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
234  if (!N)
235  return N.takeError();
236  if (const auto *Arg = N->get<T>())
237  return Func(Result, *Arg);
238  return typeError(ID, N->getNodeKind());
239  }
240 };
241 } // namespace
242 
243 // FIXME: Change the following functions from being in an anonymous namespace
244 // to static functions, after the minimum Visual C++ has _MSC_VER >= 1915
245 // (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous
246 // namespace works around a bug in earlier versions.
247 namespace {
248 // Returns the range of the statements (all source between the braces).
249 CharSourceRange getStatementsRange(const MatchResult &,
250  const CompoundStmt &CS) {
252  CS.getRBracLoc());
253 }
254 } // namespace
255 
257  return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID));
258 }
259 
260 namespace {
261 // Returns the range of the source between the call's parentheses.
262 CharSourceRange getCallArgumentsRange(const MatchResult &Result,
263  const CallExpr &CE) {
265  findOpenParen(CE, *Result.SourceManager, Result.Context->getLangOpts())
266  .getLocWithOffset(1),
267  CE.getRParenLoc());
268 }
269 } // namespace
270 
272  return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
273 }
274 
275 namespace {
276 // Returns the range of the elements of the initializer list. Includes all
277 // source between the braces.
278 CharSourceRange getElementsRange(const MatchResult &,
279  const InitListExpr &E) {
281  E.getRBraceLoc());
282 }
283 } // namespace
284 
286  return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID));
287 }
288 
290  return [S](const MatchResult &Result) -> Expected<CharSourceRange> {
291  Expected<CharSourceRange> SRange = S(Result);
292  if (!SRange)
293  return SRange.takeError();
294  return Result.SourceManager->getExpansionRange(*SRange);
295  };
296 }
A class to allow finding matches over the Clang AST.
SourceLocation getRBracLoc() const
Definition: Stmt.h:1418
SourceLocation getRParenLoc() const
Definition: Expr.h:2759
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
Smart pointer class that efficiently represents Objective-C method names.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2672
Stmt - This represents one statement.
Definition: Stmt.h:66
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:2659
StringRef asStringRef() const
String representation of the kind.
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
static CharSourceRange getTokenRange(SourceRange R)
const T * get() const
Retrieve the stored node as type T.
RangeSelector before(RangeSelector Selector)
Selects the (empty) range [B,B) when Selector selects the range [B,E).
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr or CxxCtorInitializer) selects the name&#39;s to...
RangeSelector statement(std::string ID)
Selects a node, including trailing semicolon (always).
StringRef getText(CharSourceRange Range, const ASTContext &Context)
Returns the source-code text in the specified range.
Definition: SourceCode.cpp:17
RangeSelector initListElements(std::string ID)
static Error missingPropertyError(StringRef ID, Twine Description, StringRef Property)
RangeSelector member(std::string ID)
Given a MemberExpr, selects the member token.
MatchFinder::MatchResult MatchResult
static SourceLocation findOpenParen(const CallExpr &E, const SourceManager &SM, const LangOptions &LangOpts)
RangeSelector range(RangeSelector Begin, RangeSelector End)
Selects from the start of Begin and to the end of End.
RangeSelector expansion(RangeSelector S)
Selects the range from which S was expanded (possibly along with other source), if S is an expansion...
BoundNodesTreeBuilder Nodes
const IDToNodeMap & getMap() const
Retrieve mapping from binding identifiers to bound nodes.
Definition: ASTMatchers.h:120
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:274
SourceLocation getRBraceLoc() const
Definition: Expr.h:4519
Token - This structure provides full information about a lexed token.
Definition: Token.h:34
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
std::function< Expected< CharSourceRange >(const ast_matchers::MatchFinder::MatchResult &)> RangeSelector
Definition: RangeSelector.h:27
Describes an C or C++ initializer list.
Definition: Expr.h:4370
SourceLocation getLBracLoc() const
Definition: Stmt.h:1417
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:455
llvm::Error Error
RangeSelector callArgs(std::string ID)
RangeSelector node(std::string ID)
Selects a node, including trailing semicolon (for non-expression statements).
CompoundStmt - This represents a group of statements like { stmt stmt }.
Definition: Stmt.h:1310
This represents one expression.
Definition: Expr.h:108
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Definition: Lexer.cpp:778
SourceLocation End
Represents a character-granular source range.
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file. ...
Definition: Token.h:126
SourceLocation Begin
static SourceLocation findPreviousTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, tok::TokenKind TK)
Contains all information for a given match.
static Error invalidArgumentError(Twine Message)
const SourceManager & SM
Definition: Format.cpp:1609
static CharSourceRange getCharRange(SourceRange R)
static Error typeError(StringRef ID, const ASTNodeKind &Kind)
Maps string IDs to AST nodes matched by parts of a matcher.
Definition: ASTMatchers.h:103
Kind
Encodes a location in the source.
clang::ASTContext *const Context
Utilities for interpreting the matched AST structures.
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:554
TokenKind
Provides a simple uniform namespace for tokens from all C languages.
Definition: TokenKinds.h:24
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:32
RangeSelector after(RangeSelector Selector)
Selects the the point immediately following Selector.
ast_type_traits::DynTypedNode DynTypedNode
const BoundNodes Nodes
Contains the nodes bound on the current match.
ast_type_traits::DynTypedNode Node
Dataflow Directional Tag Classes.
SourceLocation getLBraceLoc() const
Definition: Expr.h:4517
bool isMacroID() const
Represents a C++ base or member initializer.
Definition: DeclCXX.h:2300
static Expected< DynTypedNode > getNode(const ast_matchers::BoundNodes &Nodes, StringRef ID)
Defines a combinator library supporting the definition of selectors, which select source ranges based...
static SourceLocation findPreviousTokenStart(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:2806
Defines the clang::SourceLocation class and associated facilities.
RangeSelector statements(std::string ID)
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2516
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1141
clang::SourceManager *const SourceManager
A trivial tuple used to represent a source range.
This represents a decl that may have a name.
Definition: Decl.h:248
const LangOptions & getLangOpts() const
Definition: ASTContext.h:720
This class handles loading and caching of source files into memory.