Coverage Report

Created: 2023-11-11 10:31

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