1 //===--- RangeSelector.cpp - RangeSelector implementations ------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Tooling/Transformer/RangeSelector.h"
10 #include "clang/AST/Expr.h"
11 #include "clang/AST/TypeLoc.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Basic/SourceLocation.h"
14 #include "clang/Lex/Lexer.h"
15 #include "clang/Tooling/Transformer/SourceCode.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/Errc.h"
18 #include "llvm/Support/Error.h"
23 using namespace clang
;
24 using namespace transformer
;
26 using ast_matchers::MatchFinder
;
28 using llvm::StringError
;
30 using MatchResult
= MatchFinder::MatchResult
;
32 static Error
invalidArgumentError(Twine Message
) {
33 return llvm::make_error
<StringError
>(llvm::errc::invalid_argument
, Message
);
36 static Error
typeError(StringRef ID
, const ASTNodeKind
&Kind
) {
37 return invalidArgumentError("mismatched type (node id=" + ID
+
38 " kind=" + Kind
.asStringRef() + ")");
41 static Error
typeError(StringRef ID
, const ASTNodeKind
&Kind
,
43 return invalidArgumentError("mismatched type: expected one of " +
44 ExpectedType
+ " (node id=" + ID
+
45 " kind=" + Kind
.asStringRef() + ")");
48 static Error
missingPropertyError(StringRef ID
, Twine Description
,
50 return invalidArgumentError(Description
+ " requires property '" + Property
+
51 "' (node id=" + ID
+ ")");
54 static Expected
<DynTypedNode
> getNode(const ast_matchers::BoundNodes
&Nodes
,
56 auto &NodesMap
= Nodes
.getMap();
57 auto It
= NodesMap
.find(ID
);
58 if (It
== NodesMap
.end())
59 return invalidArgumentError("ID not bound: " + ID
);
63 // FIXME: handling of macros should be configurable.
64 static SourceLocation
findPreviousTokenStart(SourceLocation Start
,
65 const SourceManager
&SM
,
66 const LangOptions
&LangOpts
) {
67 if (Start
.isInvalid() || Start
.isMacroID())
68 return SourceLocation();
70 SourceLocation BeforeStart
= Start
.getLocWithOffset(-1);
71 if (BeforeStart
.isInvalid() || BeforeStart
.isMacroID())
72 return SourceLocation();
74 return Lexer::GetBeginningOfToken(BeforeStart
, SM
, LangOpts
);
77 // Finds the start location of the previous token of kind \p TK.
78 // FIXME: handling of macros should be configurable.
79 static SourceLocation
findPreviousTokenKind(SourceLocation Start
,
80 const SourceManager
&SM
,
81 const LangOptions
&LangOpts
,
84 SourceLocation L
= findPreviousTokenStart(Start
, SM
, LangOpts
);
85 if (L
.isInvalid() || L
.isMacroID())
86 return SourceLocation();
89 if (Lexer::getRawToken(L
, T
, SM
, LangOpts
, /*IgnoreWhiteSpace=*/true))
90 return SourceLocation();
93 return T
.getLocation();
99 RangeSelector
transformer::before(RangeSelector Selector
) {
100 return [Selector
](const MatchResult
&Result
) -> Expected
<CharSourceRange
> {
101 Expected
<CharSourceRange
> SelectedRange
= Selector(Result
);
103 return SelectedRange
.takeError();
104 return CharSourceRange::getCharRange(SelectedRange
->getBegin());
108 RangeSelector
transformer::after(RangeSelector Selector
) {
109 return [Selector
](const MatchResult
&Result
) -> Expected
<CharSourceRange
> {
110 Expected
<CharSourceRange
> SelectedRange
= Selector(Result
);
112 return SelectedRange
.takeError();
113 SourceLocation End
= SelectedRange
->getEnd();
114 if (SelectedRange
->isTokenRange()) {
115 // We need to find the actual (exclusive) end location from which to
116 // create a new source range. However, that's not guaranteed to be valid,
117 // even if the token location itself is valid. So, we create a token range
118 // consisting only of the last token, then map that range back to the
119 // source file. If that succeeds, we have a valid location for the end of
120 // the generated range.
121 CharSourceRange Range
= Lexer::makeFileCharRange(
122 CharSourceRange::getTokenRange(SelectedRange
->getEnd()),
123 *Result
.SourceManager
, Result
.Context
->getLangOpts());
124 if (Range
.isInvalid())
125 return invalidArgumentError(
126 "after: can't resolve sub-range to valid source range");
127 End
= Range
.getEnd();
130 return CharSourceRange::getCharRange(End
);
134 RangeSelector
transformer::node(std::string ID
) {
135 return [ID
](const MatchResult
&Result
) -> Expected
<CharSourceRange
> {
136 Expected
<DynTypedNode
> Node
= getNode(Result
.Nodes
, ID
);
138 return Node
.takeError();
139 return (Node
->get
<Decl
>() != nullptr ||
140 (Node
->get
<Stmt
>() != nullptr && Node
->get
<Expr
>() == nullptr))
141 ? tooling::getExtendedRange(*Node
, tok::TokenKind::semi
,
143 : CharSourceRange::getTokenRange(Node
->getSourceRange());
147 RangeSelector
transformer::statement(std::string ID
) {
148 return [ID
](const MatchResult
&Result
) -> Expected
<CharSourceRange
> {
149 Expected
<DynTypedNode
> Node
= getNode(Result
.Nodes
, ID
);
151 return Node
.takeError();
152 return tooling::getExtendedRange(*Node
, tok::TokenKind::semi
,
157 RangeSelector
transformer::enclose(RangeSelector Begin
, RangeSelector End
) {
158 return [Begin
, End
](const MatchResult
&Result
) -> Expected
<CharSourceRange
> {
159 Expected
<CharSourceRange
> BeginRange
= Begin(Result
);
161 return BeginRange
.takeError();
162 Expected
<CharSourceRange
> EndRange
= End(Result
);
164 return EndRange
.takeError();
165 SourceLocation B
= BeginRange
->getBegin();
166 SourceLocation E
= EndRange
->getEnd();
167 // Note: we are precluding the possibility of sub-token ranges in the case
168 // that EndRange is a token range.
169 if (Result
.SourceManager
->isBeforeInTranslationUnit(E
, B
)) {
170 return invalidArgumentError("Bad range: out of order");
172 return CharSourceRange(SourceRange(B
, E
), EndRange
->isTokenRange());
176 RangeSelector
transformer::encloseNodes(std::string BeginID
,
178 return transformer::enclose(node(std::move(BeginID
)), node(std::move(EndID
)));
181 RangeSelector
transformer::member(std::string ID
) {
182 return [ID
](const MatchResult
&Result
) -> Expected
<CharSourceRange
> {
183 Expected
<DynTypedNode
> Node
= getNode(Result
.Nodes
, ID
);
185 return Node
.takeError();
186 if (auto *M
= Node
->get
<clang::MemberExpr
>())
187 return CharSourceRange::getTokenRange(
188 M
->getMemberNameInfo().getSourceRange());
189 return typeError(ID
, Node
->getNodeKind(), "MemberExpr");
193 RangeSelector
transformer::name(std::string ID
) {
194 return [ID
](const MatchResult
&Result
) -> Expected
<CharSourceRange
> {
195 Expected
<DynTypedNode
> N
= getNode(Result
.Nodes
, ID
);
197 return N
.takeError();
199 if (const auto *D
= Node
.get
<NamedDecl
>()) {
200 if (!D
->getDeclName().isIdentifier())
201 return missingPropertyError(ID
, "name", "identifier");
202 SourceLocation L
= D
->getLocation();
203 auto R
= CharSourceRange::getTokenRange(L
, L
);
204 // Verify that the range covers exactly the name.
205 // FIXME: extend this code to support cases like `operator +` or
206 // `foo<int>` for which this range will be too short. Doing so will
207 // require subcasing `NamedDecl`, because it doesn't provide virtual
208 // access to the \c DeclarationNameInfo.
209 if (tooling::getText(R
, *Result
.Context
) != D
->getName())
210 return CharSourceRange();
213 if (const auto *E
= Node
.get
<DeclRefExpr
>()) {
214 if (!E
->getNameInfo().getName().isIdentifier())
215 return missingPropertyError(ID
, "name", "identifier");
216 SourceLocation L
= E
->getLocation();
217 return CharSourceRange::getTokenRange(L
, L
);
219 if (const auto *I
= Node
.get
<CXXCtorInitializer
>()) {
220 if (!I
->isMemberInitializer() && I
->isWritten())
221 return missingPropertyError(ID
, "name", "explicit member initializer");
222 SourceLocation L
= I
->getMemberLocation();
223 return CharSourceRange::getTokenRange(L
, L
);
225 if (const auto *T
= Node
.get
<TypeLoc
>()) {
227 auto ET
= Loc
.getAs
<ElaboratedTypeLoc
>();
229 Loc
= ET
.getNamedTypeLoc();
230 if (auto SpecLoc
= Loc
.getAs
<TemplateSpecializationTypeLoc
>();
232 return CharSourceRange::getTokenRange(SpecLoc
.getTemplateNameLoc());
233 return CharSourceRange::getTokenRange(Loc
.getSourceRange());
235 return typeError(ID
, Node
.getNodeKind(),
236 "DeclRefExpr, NamedDecl, CXXCtorInitializer, TypeLoc");
241 // FIXME: make this available in the public API for users to easily create their
244 // Creates a selector from a range-selection function \p Func, which selects a
245 // range that is relative to a bound node id. \c T is the node type expected by
247 template <typename T
, CharSourceRange (*Func
)(const MatchResult
&, const T
&)>
248 class RelativeSelector
{
252 RelativeSelector(std::string ID
) : ID(std::move(ID
)) {}
254 Expected
<CharSourceRange
> operator()(const MatchResult
&Result
) {
255 Expected
<DynTypedNode
> N
= getNode(Result
.Nodes
, ID
);
257 return N
.takeError();
258 if (const auto *Arg
= N
->get
<T
>())
259 return Func(Result
, *Arg
);
260 return typeError(ID
, N
->getNodeKind());
265 // FIXME: Change the following functions from being in an anonymous namespace
266 // to static functions, after the minimum Visual C++ has _MSC_VER >= 1915
267 // (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous
268 // namespace works around a bug in earlier versions.
270 // Returns the range of the statements (all source between the braces).
271 CharSourceRange
getStatementsRange(const MatchResult
&,
272 const CompoundStmt
&CS
) {
273 return CharSourceRange::getCharRange(CS
.getLBracLoc().getLocWithOffset(1),
278 RangeSelector
transformer::statements(std::string ID
) {
279 return RelativeSelector
<CompoundStmt
, getStatementsRange
>(std::move(ID
));
284 SourceLocation
getRLoc(const CallExpr
&E
) { return E
.getRParenLoc(); }
286 SourceLocation
getRLoc(const CXXConstructExpr
&E
) {
287 return E
.getParenOrBraceRange().getEnd();
290 tok::TokenKind
getStartToken(const CallExpr
&E
) {
291 return tok::TokenKind::l_paren
;
294 tok::TokenKind
getStartToken(const CXXConstructExpr
&E
) {
295 return isa
<CXXTemporaryObjectExpr
>(E
) ? tok::TokenKind::l_paren
296 : tok::TokenKind::l_brace
;
299 template <typename ExprWithArgs
>
300 SourceLocation
findArgStartDelimiter(const ExprWithArgs
&E
, SourceLocation RLoc
,
301 const SourceManager
&SM
,
302 const LangOptions
&LangOpts
) {
303 SourceLocation Loc
= E
.getNumArgs() == 0 ? RLoc
: E
.getArg(0)->getBeginLoc();
304 return findPreviousTokenKind(Loc
, SM
, LangOpts
, getStartToken(E
));
306 // Returns the range of the source between the call's or construct expr's
307 // parentheses/braces.
308 template <typename ExprWithArgs
>
309 CharSourceRange
getArgumentsRange(const MatchResult
&Result
,
310 const ExprWithArgs
&CE
) {
311 const SourceLocation RLoc
= getRLoc(CE
);
312 return CharSourceRange::getCharRange(
313 findArgStartDelimiter(CE
, RLoc
, *Result
.SourceManager
,
314 Result
.Context
->getLangOpts())
315 .getLocWithOffset(1),
320 RangeSelector
transformer::callArgs(std::string ID
) {
321 return RelativeSelector
<CallExpr
, getArgumentsRange
<CallExpr
>>(std::move(ID
));
324 RangeSelector
transformer::constructExprArgs(std::string ID
) {
325 return RelativeSelector
<CXXConstructExpr
,
326 getArgumentsRange
<CXXConstructExpr
>>(std::move(ID
));
330 // Returns the range of the elements of the initializer list. Includes all
331 // source between the braces.
332 CharSourceRange
getElementsRange(const MatchResult
&,
333 const InitListExpr
&E
) {
334 return CharSourceRange::getCharRange(E
.getLBraceLoc().getLocWithOffset(1),
339 RangeSelector
transformer::initListElements(std::string ID
) {
340 return RelativeSelector
<InitListExpr
, getElementsRange
>(std::move(ID
));
344 // Returns the range of the else branch, including the `else` keyword.
345 CharSourceRange
getElseRange(const MatchResult
&Result
, const IfStmt
&S
) {
346 return tooling::maybeExtendRange(
347 CharSourceRange::getTokenRange(S
.getElseLoc(), S
.getEndLoc()),
348 tok::TokenKind::semi
, *Result
.Context
);
352 RangeSelector
transformer::elseBranch(std::string ID
) {
353 return RelativeSelector
<IfStmt
, getElseRange
>(std::move(ID
));
356 RangeSelector
transformer::expansion(RangeSelector S
) {
357 return [S
](const MatchResult
&Result
) -> Expected
<CharSourceRange
> {
358 Expected
<CharSourceRange
> SRange
= S(Result
);
360 return SRange
.takeError();
361 return Result
.SourceManager
->getExpansionRange(*SRange
);