Coverage Report

Created: 2021-01-19 06:58

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