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 llvm::Error;
27using llvm::StringError;
28
29using MatchResult = MatchFinder::MatchResult;
30
31static Error invalidArgumentError(Twine Message) {
32  return llvm::make_error<StringError>(llvm::errc::invalid_argument, Message);
33}
34
35static Error typeError(StringRef ID, const ASTNodeKind &Kind) {
36  return invalidArgumentError("mismatched type (node id=" + ID +
37                              " kind=" + Kind.asStringRef() + ")");
38}
39
40static Error typeError(StringRef ID, const ASTNodeKind &Kind,
41                       Twine ExpectedType) {
42  return invalidArgumentError("mismatched type: expected one of " +
43                              ExpectedType + " (node id=" + ID +
44                              " kind=" + Kind.asStringRef() + ")");
45}
46
47static Error missingPropertyError(StringRef ID, Twine Description,
48                                  StringRef Property) {
49  return invalidArgumentError(Description + " requires property '" + Property +
50                              "' (node id=" + ID + ")");
51}
52
53static Expected<DynTypedNode> getNode(const ast_matchers::BoundNodes &Nodes,
54                                      StringRef ID) {
55  auto &NodesMap = Nodes.getMap();
56  auto It = NodesMap.find(ID);
57  if (It == NodesMap.end())
58    return invalidArgumentError("ID not bound: " + ID);
59  return It->second;
60}
61
62// FIXME: handling of macros should be configurable.
63static SourceLocation findPreviousTokenStart(SourceLocation Start,
64                                             const SourceManager &SM,
65                                             const LangOptions &LangOpts) {
66  if (Start.isInvalid() || Start.isMacroID())
67    return SourceLocation();
68
69  SourceLocation BeforeStart = Start.getLocWithOffset(-1);
70  if (BeforeStart.isInvalid() || BeforeStart.isMacroID())
71    return SourceLocation();
72
73  return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts);
74}
75
76// Finds the start location of the previous token of kind \p TK.
77// FIXME: handling of macros should be configurable.
78static SourceLocation findPreviousTokenKind(SourceLocation Start,
79                                            const SourceManager &SM,
80                                            const LangOptions &LangOpts,
81                                            tok::TokenKind TK) {
82  while (true) {
83    SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts);
84    if (L.isInvalid() || L.isMacroID())
85      return SourceLocation();
86
87    Token T;
88    if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true))
89      return SourceLocation();
90
91    if (T.is(TK))
92      return T.getLocation();
93
94    Start = L;
95  }
96}
97
98static SourceLocation findOpenParen(const CallExpr &E, const SourceManager &SM,
99                                    const LangOptions &LangOpts) {
100  SourceLocation EndLoc =
101      E.getNumArgs() == 0 ? E.getRParenLoc() : E.getArg(0)->getBeginLoc();
102  return findPreviousTokenKind(EndLoc, SM, LangOpts, tok::TokenKind::l_paren);
103}
104
105RangeSelector transformer::before(RangeSelector Selector) {
106  return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
107    Expected<CharSourceRange> SelectedRange = Selector(Result);
108    if (!SelectedRange)
109      return SelectedRange.takeError();
110    return CharSourceRange::getCharRange(SelectedRange->getBegin());
111  };
112}
113
114RangeSelector transformer::after(RangeSelector Selector) {
115  return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
116    Expected<CharSourceRange> SelectedRange = Selector(Result);
117    if (!SelectedRange)
118      return SelectedRange.takeError();
119    if (SelectedRange->isCharRange())
120      return CharSourceRange::getCharRange(SelectedRange->getEnd());
121    return CharSourceRange::getCharRange(Lexer::getLocForEndOfToken(
122        SelectedRange->getEnd(), 0, Result.Context->getSourceManager(),
123        Result.Context->getLangOpts()));
124  };
125}
126
127RangeSelector transformer::node(std::string ID) {
128  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
129    Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
130    if (!Node)
131      return Node.takeError();
132    return Node->get<Stmt>() != nullptr && Node->get<Expr>() == nullptr
133               ? tooling::getExtendedRange(*Node, tok::TokenKind::semi,
134                                           *Result.Context)
135               : CharSourceRange::getTokenRange(Node->getSourceRange());
136  };
137}
138
139RangeSelector transformer::statement(std::string ID) {
140  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
141    Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
142    if (!Node)
143      return Node.takeError();
144    return tooling::getExtendedRange(*Node, tok::TokenKind::semi,
145                                     *Result.Context);
146  };
147}
148
149RangeSelector transformer::enclose(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
168RangeSelector transformer::encloseNodes(std::string BeginID,
169                                        std::string EndID) {
170  return transformer::enclose(node(std::move(BeginID)), node(std::move(EndID)));
171}
172
173RangeSelector transformer::member(std::string ID) {
174  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
175    Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);
176    if (!Node)
177      return Node.takeError();
178    if (auto *M = Node->get<clang::MemberExpr>())
179      return CharSourceRange::getTokenRange(
180          M->getMemberNameInfo().getSourceRange());
181    return typeError(ID, Node->getNodeKind(), "MemberExpr");
182  };
183}
184
185RangeSelector transformer::name(std::string ID) {
186  return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
187    Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
188    if (!N)
189      return N.takeError();
190    auto &Node = *N;
191    if (const auto *D = Node.get<NamedDecl>()) {
192      if (!D->getDeclName().isIdentifier())
193        return missingPropertyError(ID, "name", "identifier");
194      SourceLocation L = D->getLocation();
195      auto R = CharSourceRange::getTokenRange(L, L);
196      // Verify that the range covers exactly the name.
197      // FIXME: extend this code to support cases like `operator +` or
198      // `foo<int>` for which this range will be too short.  Doing so will
199      // require subcasing `NamedDecl`, because it doesn't provide virtual
200      // access to the \c DeclarationNameInfo.
201      if (tooling::getText(R, *Result.Context) != D->getName())
202        return CharSourceRange();
203      return R;
204    }
205    if (const auto *E = Node.get<DeclRefExpr>()) {
206      if (!E->getNameInfo().getName().isIdentifier())
207        return missingPropertyError(ID, "name", "identifier");
208      SourceLocation L = E->getLocation();
209      return CharSourceRange::getTokenRange(L, L);
210    }
211    if (const auto *I = Node.get<CXXCtorInitializer>()) {
212      if (!I->isMemberInitializer() && I->isWritten())
213        return missingPropertyError(ID, "name", "explicit member initializer");
214      SourceLocation L = I->getMemberLocation();
215      return CharSourceRange::getTokenRange(L, L);
216    }
217    return typeError(ID, Node.getNodeKind(),
218                     "DeclRefExpr, NamedDecl, CXXCtorInitializer");
219  };
220}
221
222namespace {
223// FIXME: make this available in the public API for users to easily create their
224// own selectors.
225
226// Creates a selector from a range-selection function \p Func, which selects a
227// range that is relative to a bound node id.  \c T is the node type expected by
228// \p Func.
229template <typename T, CharSourceRange (*Func)(const MatchResult &, const T &)>
230class RelativeSelector {
231  std::string ID;
232
233public:
234  RelativeSelector(std::string ID) : ID(std::move(ID)) {}
235
236  Expected<CharSourceRange> operator()(const MatchResult &Result) {
237    Expected<DynTypedNode> N = getNode(Result.Nodes, ID);
238    if (!N)
239      return N.takeError();
240    if (const auto *Arg = N->get<T>())
241      return Func(Result, *Arg);
242    return typeError(ID, N->getNodeKind());
243  }
244};
245} // namespace
246
247// FIXME: Change the following functions from being in an anonymous namespace
248// to static functions, after the minimum Visual C++ has _MSC_VER >= 1915
249// (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous
250// namespace works around a bug in earlier versions.
251namespace {
252// Returns the range of the statements (all source between the braces).
253CharSourceRange getStatementsRange(const MatchResult &,
254                                   const CompoundStmt &CS) {
255  return CharSourceRange::getCharRange(CS.getLBracLoc().getLocWithOffset(1),
256                                       CS.getRBracLoc());
257}
258} // namespace
259
260RangeSelector transformer::statements(std::string ID) {
261  return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID));
262}
263
264namespace {
265// Returns the range of the source between the call's parentheses.
266CharSourceRange getCallArgumentsRange(const MatchResult &Result,
267                                      const CallExpr &CE) {
268  return CharSourceRange::getCharRange(
269      findOpenParen(CE, *Result.SourceManager, Result.Context->getLangOpts())
270          .getLocWithOffset(1),
271      CE.getRParenLoc());
272}
273} // namespace
274
275RangeSelector transformer::callArgs(std::string ID) {
276  return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID));
277}
278
279namespace {
280// Returns the range of the elements of the initializer list. Includes all
281// source between the braces.
282CharSourceRange getElementsRange(const MatchResult &,
283                                 const InitListExpr &E) {
284  return CharSourceRange::getCharRange(E.getLBraceLoc().getLocWithOffset(1),
285                                       E.getRBraceLoc());
286}
287} // namespace
288
289RangeSelector transformer::initListElements(std::string ID) {
290  return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID));
291}
292
293namespace {
294// Returns the range of the else branch, including the `else` keyword.
295CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) {
296  return tooling::maybeExtendRange(
297      CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()),
298      tok::TokenKind::semi, *Result.Context);
299}
300} // namespace
301
302RangeSelector transformer::elseBranch(std::string ID) {
303  return RelativeSelector<IfStmt, getElseRange>(std::move(ID));
304}
305
306RangeSelector transformer::expansion(RangeSelector S) {
307  return [S](const MatchResult &Result) -> Expected<CharSourceRange> {
308    Expected<CharSourceRange> SRange = S(Result);
309    if (!SRange)
310      return SRange.takeError();
311    return Result.SourceManager->getExpansionRange(*SRange);
312  };
313}
314