xref: /llvm-project/clang/lib/Tooling/Transformer/RangeSelector.cpp (revision fbdf83521b17c4683e4f819587000bbce71d928b)
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 
9 #include "clang/Tooling/Transformer/RangeSelector.h"
10 #include "clang/AST/Expr.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>
21 
22 using namespace clang;
23 using namespace tooling;
24 
25 using ast_matchers::MatchFinder;
26 using ast_type_traits::ASTNodeKind;
27 using ast_type_traits::DynTypedNode;
28 using llvm::Error;
29 using llvm::StringError;
30 
31 using MatchResult = MatchFinder::MatchResult;
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 
55 static Expected<DynTypedNode> getNode(const ast_matchers::BoundNodes &Nodes,
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.
65 static SourceLocation findPreviousTokenStart(SourceLocation Start,
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.
80 static SourceLocation findPreviousTokenKind(SourceLocation Start,
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 
100 static SourceLocation findOpenParen(const CallExpr &E, const SourceManager &SM,
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 
107 RangeSelector tooling::before(RangeSelector Selector) {
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 
116 RangeSelector tooling::after(RangeSelector Selector) {
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());
123     return CharSourceRange::getCharRange(Lexer::getLocForEndOfToken(
124         SelectedRange->getEnd(), 0, Result.Context->getSourceManager(),
125         Result.Context->getLangOpts()));
126   };
127 }
128 
129 RangeSelector tooling::node(std::string ID) {
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 
140 RangeSelector tooling::statement(std::string ID) {
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 
149 RangeSelector tooling::range(RangeSelector Begin, RangeSelector End) {
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 
172 RangeSelector tooling::member(std::string ID) {
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>())
178       return CharSourceRange::getTokenRange(
179           M->getMemberNameInfo().getSourceRange());
180     return typeError(ID, Node->getNodeKind(), "MemberExpr");
181   };
182 }
183 
184 RangeSelector tooling::name(std::string ID) {
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 // FIXME: make this available in the public API for users to easily create their
223 // own selectors.
224 
225 // Creates a selector from a range-selection function \p Func, which selects a
226 // range that is relative to a bound node id.  \c T is the node type expected by
227 // \p Func.
228 template <typename T, CharSourceRange (*Func)(const MatchResult &, const T &)>
229 class RelativeSelector {
230   std::string ID;
231 
232 public:
233   RelativeSelector(std::string ID) : ID(std::move(ID)) {}
234 
235   Expected<CharSourceRange> operator()(const MatchResult &Result) {
236     Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
237     if (!N)
238       return N.takeError();
239     if (const auto *Arg = N->get<T>())
240       return Func(Result, *Arg);
241     return typeError(ID, N->getNodeKind());
242   }
243 };
244 } // namespace
245 
246 // FIXME: Change the following functions from being in an anonymous namespace
247 // to static functions, after the minimum Visual C++ has _MSC_VER >= 1915
248 // (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous
249 // namespace works around a bug in earlier versions.
250 namespace {
251 // Returns the range of the statements (all source between the braces).
252 CharSourceRange getStatementsRange(const MatchResult &,
253                                    const CompoundStmt &CS) {
254   return CharSourceRange::getCharRange(CS.getLBracLoc().getLocWithOffset(1),
255                                        CS.getRBracLoc());
256 }
257 } // namespace
258 
259 RangeSelector tooling::statements(std::string ID) {
260   return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID));
261 }
262 
263 namespace {
264 // Returns the range of the source between the call's parentheses.
265 CharSourceRange getCallArgumentsRange(const MatchResult &Result,
266                                       const CallExpr &CE) {
267   return CharSourceRange::getCharRange(
268       findOpenParen(CE, *Result.SourceManager, Result.Context->getLangOpts())
269           .getLocWithOffset(1),
270       CE.getRParenLoc());
271 }
272 } // namespace
273 
274 RangeSelector tooling::callArgs(std::string ID) {
275   return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
276 }
277 
278 namespace {
279 // Returns the range of the elements of the initializer list. Includes all
280 // source between the braces.
281 CharSourceRange getElementsRange(const MatchResult &,
282                                  const InitListExpr &E) {
283   return CharSourceRange::getCharRange(E.getLBraceLoc().getLocWithOffset(1),
284                                        E.getRBraceLoc());
285 }
286 } // namespace
287 
288 RangeSelector tooling::initListElements(std::string ID) {
289   return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID));
290 }
291 
292 namespace {
293 // Returns the range of the else branch, including the `else` keyword.
294 CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) {
295   return maybeExtendRange(
296       CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()),
297       tok::TokenKind::semi, *Result.Context);
298 }
299 } // namespace
300 
301 RangeSelector tooling::elseBranch(std::string ID) {
302   return RelativeSelector<IfStmt, getElseRange>(std::move(ID));
303 }
304 
305 RangeSelector tooling::expansion(RangeSelector S) {
306   return [S](const MatchResult &Result) -> Expected<CharSourceRange> {
307     Expected<CharSourceRange> SRange = S(Result);
308     if (!SRange)
309       return SRange.takeError();
310     return Result.SourceManager->getExpansionRange(*SRange);
311   };
312 }
313