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
22using namespace clang;
23using namespace transformer;
24
25using ast_matchers::MatchFinder;
26using ast_type_traits::ASTNodeKind;
27using ast_type_traits::DynTypedNode;
28using llvm::Error;
29using llvm::StringError;
30
31using MatchResult = MatchFinder::MatchResult;
32
33static Error invalidArgumentError(Twine Message) {
34  return llvm::make_error<StringError>(llvm::errc::invalid_argument, Message);
35}
36
37static Error typeError(StringRef ID, const ASTNodeKind &Kind) {
38  return invalidArgumentError("mismatched type (node id=" + ID +
39                              " kind=" + Kind.asStringRef() + ")");
40}
41
42static 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
49static Error missingPropertyError(StringRef ID, Twine Description,
50                                  StringRef Property) {
51  return invalidArgumentError(Description + " requires property '" + Property +
52                              "' (node id=" + ID + ")");
53}
54
55static 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.
65static 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.
80static 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
100static 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
107RangeSelector transformer::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
116RangeSelector transformer::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
129RangeSelector transformer::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               ? tooling::getExtendedRange(*Node, tok::TokenKind::semi,
136                                           *Result.Context)
137               : CharSourceRange::getTokenRange(Node->getSourceRange());
138  };
139}
140
141RangeSelector transformer::statement(std::string ID) {
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 tooling::getExtendedRange(*Node, tok::TokenKind::semi,
147                                     *Result.Context);
148  };
149}
150
151RangeSelector transformer::range(RangeSelector Begin, RangeSelector End) {
152  return [Begin, End](const MatchResult &Result) -> Expected<CharSourceRange> {
153    Expected<CharSourceRange> BeginRange = Begin(Result);
154    if (!BeginRange)
155      return BeginRange.takeError();
156    Expected<CharSourceRange> EndRange = End(Result);
157    if (!EndRange)
158      return EndRange.takeError();
159    SourceLocation B = BeginRange->getBegin();
160    SourceLocation E = EndRange->getEnd();
161    // Note: we are precluding the possibility of sub-token ranges in the case
162    // that EndRange is a token range.
163    if (Result.SourceManager->isBeforeInTranslationUnit(E, B)) {
164      return invalidArgumentError("Bad range: out of order");
165    }
166    return CharSourceRange(SourceRange(B, E), EndRange->isTokenRange());
167  };
168}
169
170RangeSelector transformer::range(std::string BeginID, std::string EndID) {
171  return transformer::range(node(std::move(BeginID)), node(std::move(EndID)));
172}
173
174RangeSelector transformer::member(std::string ID) {
175  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
176    Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
177    if (!Node)
178      return Node.takeError();
179    if (auto *M = Node->get<clang::MemberExpr>())
180      return CharSourceRange::getTokenRange(
181          M->getMemberNameInfo().getSourceRange());
182    return typeError(ID, Node->getNodeKind(), "MemberExpr");
183  };
184}
185
186RangeSelector transformer::name(std::string ID) {
187  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
188    Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
189    if (!N)
190      return N.takeError();
191    auto &Node = *N;
192    if (const auto *D = Node.get<NamedDecl>()) {
193      if (!D->getDeclName().isIdentifier())
194        return missingPropertyError(ID, "name", "identifier");
195      SourceLocation L = D->getLocation();
196      auto R = CharSourceRange::getTokenRange(L, L);
197      // Verify that the range covers exactly the name.
198      // FIXME: extend this code to support cases like `operator +` or
199      // `foo<int>` for which this range will be too short.  Doing so will
200      // require subcasing `NamedDecl`, because it doesn't provide virtual
201      // access to the \c DeclarationNameInfo.
202      if (tooling::getText(R, *Result.Context) != D->getName())
203        return CharSourceRange();
204      return R;
205    }
206    if (const auto *E = Node.get<DeclRefExpr>()) {
207      if (!E->getNameInfo().getName().isIdentifier())
208        return missingPropertyError(ID, "name", "identifier");
209      SourceLocation L = E->getLocation();
210      return CharSourceRange::getTokenRange(L, L);
211    }
212    if (const auto *I = Node.get<CXXCtorInitializer>()) {
213      if (!I->isMemberInitializer() && I->isWritten())
214        return missingPropertyError(ID, "name", "explicit member initializer");
215      SourceLocation L = I->getMemberLocation();
216      return CharSourceRange::getTokenRange(L, L);
217    }
218    return typeError(ID, Node.getNodeKind(),
219                     "DeclRefExpr, NamedDecl, CXXCtorInitializer");
220  };
221}
222
223namespace {
224// FIXME: make this available in the public API for users to easily create their
225// own selectors.
226
227// Creates a selector from a range-selection function \p Func, which selects a
228// range that is relative to a bound node id.  \c T is the node type expected by
229// \p Func.
230template <typename T, CharSourceRange (*Func)(const MatchResult &, const T &)>
231class RelativeSelector {
232  std::string ID;
233
234public:
235  RelativeSelector(std::string ID) : ID(std::move(ID)) {}
236
237  Expected<CharSourceRange> operator()(const MatchResult &Result) {
238    Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
239    if (!N)
240      return N.takeError();
241    if (const auto *Arg = N->get<T>())
242      return Func(Result, *Arg);
243    return typeError(ID, N->getNodeKind());
244  }
245};
246} // namespace
247
248// FIXME: Change the following functions from being in an anonymous namespace
249// to static functions, after the minimum Visual C++ has _MSC_VER >= 1915
250// (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous
251// namespace works around a bug in earlier versions.
252namespace {
253// Returns the range of the statements (all source between the braces).
254CharSourceRange getStatementsRange(const MatchResult &,
255                                   const CompoundStmt &CS) {
256  return CharSourceRange::getCharRange(CS.getLBracLoc().getLocWithOffset(1),
257                                       CS.getRBracLoc());
258}
259} // namespace
260
261RangeSelector transformer::statements(std::string ID) {
262  return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID));
263}
264
265namespace {
266// Returns the range of the source between the call's parentheses.
267CharSourceRange getCallArgumentsRange(const MatchResult &Result,
268                                      const CallExpr &CE) {
269  return CharSourceRange::getCharRange(
270      findOpenParen(CE, *Result.SourceManager, Result.Context->getLangOpts())
271          .getLocWithOffset(1),
272      CE.getRParenLoc());
273}
274} // namespace
275
276RangeSelector transformer::callArgs(std::string ID) {
277  return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
278}
279
280namespace {
281// Returns the range of the elements of the initializer list. Includes all
282// source between the braces.
283CharSourceRange getElementsRange(const MatchResult &,
284                                 const InitListExpr &E) {
285  return CharSourceRange::getCharRange(E.getLBraceLoc().getLocWithOffset(1),
286                                       E.getRBraceLoc());
287}
288} // namespace
289
290RangeSelector transformer::initListElements(std::string ID) {
291  return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID));
292}
293
294namespace {
295// Returns the range of the else branch, including the `else` keyword.
296CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) {
297  return tooling::maybeExtendRange(
298      CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()),
299      tok::TokenKind::semi, *Result.Context);
300}
301} // namespace
302
303RangeSelector transformer::elseBranch(std::string ID) {
304  return RelativeSelector<IfStmt, getElseRange>(std::move(ID));
305}
306
307RangeSelector transformer::expansion(RangeSelector S) {
308  return [S](const MatchResult &Result) -> Expected<CharSourceRange> {
309    Expected<CharSourceRange> SRange = S(Result);
310    if (!SRange)
311      return SRange.takeError();
312    return Result.SourceManager->getExpansionRange(*SRange);
313  };
314}
315