Coverage Report

Created: 2021-08-24 07:12

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Transformer/Stencil.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- Stencil.cpp - Stencil implementation -------------------*- 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/Stencil.h"
10
#include "clang/AST/ASTContext.h"
11
#include "clang/AST/ASTTypeTraits.h"
12
#include "clang/AST/Expr.h"
13
#include "clang/ASTMatchers/ASTMatchFinder.h"
14
#include "clang/ASTMatchers/ASTMatchers.h"
15
#include "clang/Basic/SourceLocation.h"
16
#include "clang/Lex/Lexer.h"
17
#include "clang/Tooling/Transformer/SourceCode.h"
18
#include "clang/Tooling/Transformer/SourceCodeBuilders.h"
19
#include "llvm/ADT/SmallVector.h"
20
#include "llvm/ADT/Twine.h"
21
#include "llvm/Support/Errc.h"
22
#include "llvm/Support/Error.h"
23
#include <atomic>
24
#include <memory>
25
#include <string>
26
27
using namespace clang;
28
using namespace transformer;
29
30
using ast_matchers::MatchFinder;
31
using llvm::errc;
32
using llvm::Error;
33
using llvm::Expected;
34
using llvm::StringError;
35
36
static llvm::Expected<DynTypedNode>
37
6
getNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) {
38
6
  auto &NodesMap = Nodes.getMap();
39
6
  auto It = NodesMap.find(Id);
40
6
  if (It == NodesMap.end())
41
0
    return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
42
0
                                               "Id not bound: " + Id);
43
6
  return It->second;
44
6
}
45
46
static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match,
47
6
                       std::string *Result) {
48
6
  std::string Output;
49
6
  llvm::raw_string_ostream Os(Output);
50
6
  auto NodeOrErr = getNode(Match.Nodes, Id);
51
6
  if (auto Err = NodeOrErr.takeError())
52
0
    return Err;
53
6
  NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts()));
54
6
  *Result += Os.str();
55
6
  return Error::success();
56
6
}
57
58
// FIXME: Consider memoizing this function using the `ASTContext`.
59
11
static bool isSmartPointerType(QualType Ty, ASTContext &Context) {
60
11
  using namespace ::clang::ast_matchers;
61
62
  // Optimization: hard-code common smart-pointer types. This can/should be
63
  // removed if we start caching the results of this function.
64
11
  auto KnownSmartPointer =
65
11
      cxxRecordDecl(hasAnyName("::std::unique_ptr", "::std::shared_ptr"));
66
11
  const auto QuacksLikeASmartPointer = cxxRecordDecl(
67
11
      hasMethod(cxxMethodDecl(hasOverloadedOperatorName("->"),
68
11
                              returns(qualType(pointsTo(type()))))),
69
11
      hasMethod(cxxMethodDecl(hasOverloadedOperatorName("*"),
70
11
                              returns(qualType(references(type()))))));
71
11
  const auto SmartPointer = qualType(hasDeclaration(
72
11
      cxxRecordDecl(anyOf(KnownSmartPointer, QuacksLikeASmartPointer))));
73
11
  return match(SmartPointer, Ty, Context).size() > 0;
74
11
}
75
76
// Identifies use of `operator*` on smart pointers, and returns the underlying
77
// smart-pointer expression; otherwise, returns null.
78
4
static const Expr *isSmartDereference(const Expr &E, ASTContext &Context) {
79
4
  using namespace ::clang::ast_matchers;
80
81
4
  const auto HasOverloadedArrow = cxxRecordDecl(hasMethod(cxxMethodDecl(
82
4
      hasOverloadedOperatorName("->"), returns(qualType(pointsTo(type()))))));
83
  // Verify it is a smart pointer by finding `operator->` in the class
84
  // declaration.
85
4
  auto Deref = cxxOperatorCallExpr(
86
4
      hasOverloadedOperatorName("*"), hasUnaryOperand(expr().bind("arg")),
87
4
      callee(cxxMethodDecl(ofClass(HasOverloadedArrow))));
88
4
  return selectFirst<Expr>("arg", match(Deref, E, Context));
89
4
}
90
91
namespace {
92
// An arbitrary fragment of code within a stencil.
93
class RawTextStencil : public StencilInterface {
94
  std::string Text;
95
96
public:
97
129
  explicit RawTextStencil(std::string T) : Text(std::move(T)) {}
98
99
18
  std::string toString() const override {
100
18
    std::string Result;
101
18
    llvm::raw_string_ostream OS(Result);
102
18
    OS << "\"";
103
18
    OS.write_escaped(Text);
104
18
    OS << "\"";
105
18
    OS.flush();
106
18
    return Result;
107
18
  }
108
109
  Error eval(const MatchFinder::MatchResult &Match,
110
159
             std::string *Result) const override {
111
159
    Result->append(Text);
112
159
    return Error::success();
113
159
  }
114
};
115
116
// A debugging operation to dump the AST for a particular (bound) AST node.
117
class DebugPrintNodeStencil : public StencilInterface {
118
  std::string Id;
119
120
public:
121
1
  explicit DebugPrintNodeStencil(std::string S) : Id(std::move(S)) {}
122
123
1
  std::string toString() const override {
124
1
    return (llvm::Twine("dPrint(\"") + Id + "\")").str();
125
1
  }
126
127
  Error eval(const MatchFinder::MatchResult &Match,
128
0
             std::string *Result) const override {
129
0
    return printNode(Id, Match, Result);
130
0
  }
131
};
132
133
// Operators that take a single node Id as an argument.
134
enum class UnaryNodeOperator {
135
  Parens,
136
  Deref,
137
  MaybeDeref,
138
  AddressOf,
139
  MaybeAddressOf,
140
  Describe,
141
};
142
143
// Generic container for stencil operations with a (single) node-id argument.
144
class UnaryOperationStencil : public StencilInterface {
145
  UnaryNodeOperator Op;
146
  std::string Id;
147
148
public:
149
  UnaryOperationStencil(UnaryNodeOperator Op, std::string Id)
150
33
      : Op(Op), Id(std::move(Id)) {}
151
152
4
  std::string toString() const override {
153
4
    StringRef OpName;
154
4
    switch (Op) {
155
1
    case UnaryNodeOperator::Parens:
156
1
      OpName = "expression";
157
1
      break;
158
1
    case UnaryNodeOperator::Deref:
159
1
      OpName = "deref";
160
1
      break;
161
0
    case UnaryNodeOperator::MaybeDeref:
162
0
      OpName = "maybeDeref";
163
0
      break;
164
1
    case UnaryNodeOperator::AddressOf:
165
1
      OpName = "addressOf";
166
1
      break;
167
0
    case UnaryNodeOperator::MaybeAddressOf:
168
0
      OpName = "maybeAddressOf";
169
0
      break;
170
1
    case UnaryNodeOperator::Describe:
171
1
      OpName = "describe";
172
1
      break;
173
4
    }
174
4
    return (OpName + "(\"" + Id + "\")").str();
175
4
  }
176
177
  Error eval(const MatchFinder::MatchResult &Match,
178
29
             std::string *Result) const override {
179
    // The `Describe` operation can be applied to any node, not just
180
    // expressions, so it is handled here, separately.
181
29
    if (Op == UnaryNodeOperator::Describe)
182
6
      return printNode(Id, Match, Result);
183
184
23
    const auto *E = Match.Nodes.getNodeAs<Expr>(Id);
185
23
    if (E == nullptr)
186
1
      return llvm::make_error<StringError>(errc::invalid_argument,
187
1
                                           "Id not bound or not Expr: " + Id);
188
22
    llvm::Optional<std::string> Source;
189
22
    switch (Op) {
190
3
    case UnaryNodeOperator::Parens:
191
3
      Source = tooling::buildParens(*E, *Match.Context);
192
3
      break;
193
3
    case UnaryNodeOperator::Deref:
194
3
      Source = tooling::buildDereference(*E, *Match.Context);
195
3
      break;
196
7
    case UnaryNodeOperator::MaybeDeref:
197
7
      if (E->getType()->isAnyPointerType() ||
198
7
          
isSmartPointerType(E->getType(), *Match.Context)3
) {
199
        // Strip off any operator->. This can only occur inside an actual arrow
200
        // member access, so we treat it as equivalent to an actual object
201
        // expression.
202
6
        if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {
203
1
          if (OpCall->getOperator() == clang::OO_Arrow &&
204
1
              OpCall->getNumArgs() == 1) {
205
1
            E = OpCall->getArg(0);
206
1
          }
207
1
        }
208
6
        Source = tooling::buildDereference(*E, *Match.Context);
209
6
        break;
210
6
      }
211
1
      *Result += tooling::getText(*E, *Match.Context);
212
1
      return Error::success();
213
4
    case UnaryNodeOperator::AddressOf:
214
4
      Source = tooling::buildAddressOf(*E, *Match.Context);
215
4
      break;
216
5
    case UnaryNodeOperator::MaybeAddressOf:
217
5
      if (E->getType()->isAnyPointerType() ||
218
5
          
isSmartPointerType(E->getType(), *Match.Context)3
) {
219
        // Strip off any operator->. This can only occur inside an actual arrow
220
        // member access, so we treat it as equivalent to an actual object
221
        // expression.
222
3
        if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {
223
1
          if (OpCall->getOperator() == clang::OO_Arrow &&
224
1
              OpCall->getNumArgs() == 1) {
225
1
            E = OpCall->getArg(0);
226
1
          }
227
1
        }
228
3
        *Result += tooling::getText(*E, *Match.Context);
229
3
        return Error::success();
230
3
      }
231
2
      Source = tooling::buildAddressOf(*E, *Match.Context);
232
2
      break;
233
0
    case UnaryNodeOperator::Describe:
234
0
      llvm_unreachable("This case is handled at the start of the function");
235
22
    }
236
18
    if (!Source)
237
0
      return llvm::make_error<StringError>(
238
0
          errc::invalid_argument,
239
0
          "Could not construct expression source from ID: " + Id);
240
18
    *Result += *Source;
241
18
    return Error::success();
242
18
  }
243
};
244
245
// The fragment of code corresponding to the selected range.
246
class SelectorStencil : public StencilInterface {
247
  RangeSelector Selector;
248
249
public:
250
19
  explicit SelectorStencil(RangeSelector S) : Selector(std::move(S)) {}
251
252
2
  std::string toString() const override { return "selection(...)"; }
253
254
  Error eval(const MatchFinder::MatchResult &Match,
255
44
             std::string *Result) const override {
256
44
    auto RawRange = Selector(Match);
257
44
    if (!RawRange)
258
1
      return RawRange.takeError();
259
43
    CharSourceRange Range = Lexer::makeFileCharRange(
260
43
        *RawRange, *Match.SourceManager, Match.Context->getLangOpts());
261
43
    if (Range.isInvalid()) {
262
      // Validate the original range to attempt to get a meaningful error
263
      // message. If it's valid, then something else is the cause and we just
264
      // return the generic failure message.
265
3
      if (auto Err =
266
3
              tooling::validateEditRange(*RawRange, *Match.SourceManager))
267
3
        return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) {
268
3
          assert(E->convertToErrorCode() ==
269
3
                     llvm::make_error_code(errc::invalid_argument) &&
270
3
                 "Validation errors must carry the invalid_argument code");
271
0
          return llvm::createStringError(
272
3
              errc::invalid_argument,
273
3
              "selected range could not be resolved to a valid source range; " +
274
3
                  E->getMessage());
275
3
        });
276
0
      return llvm::createStringError(
277
0
          errc::invalid_argument,
278
0
          "selected range could not be resolved to a valid source range");
279
3
    }
280
    // Validate `Range`, because `makeFileCharRange` accepts some ranges that
281
    // `validateEditRange` rejects.
282
40
    if (auto Err = tooling::validateEditRange(Range, *Match.SourceManager))
283
0
      return joinErrors(
284
0
          llvm::createStringError(errc::invalid_argument,
285
0
                                  "selected range is not valid for editing"),
286
0
          std::move(Err));
287
40
    *Result += tooling::getText(Range, *Match.Context);
288
40
    return Error::success();
289
40
  }
290
};
291
292
// A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
293
class AccessStencil : public StencilInterface {
294
  std::string BaseId;
295
  Stencil Member;
296
297
public:
298
  AccessStencil(StringRef BaseId, Stencil Member)
299
18
      : BaseId(std::string(BaseId)), Member(std::move(Member)) {}
300
301
8
  std::string toString() const override {
302
8
    return (llvm::Twine("access(\"") + BaseId + "\", " + Member->toString() +
303
8
            ")")
304
8
        .str();
305
8
  }
306
307
  Error eval(const MatchFinder::MatchResult &Match,
308
10
             std::string *Result) const override {
309
10
    const auto *E = Match.Nodes.getNodeAs<Expr>(BaseId);
310
10
    if (E == nullptr)
311
0
      return llvm::make_error<StringError>(errc::invalid_argument,
312
0
                                           "Id not bound: " + BaseId);
313
10
    if (!E->isImplicitCXXThis()) {
314
9
      llvm::Optional<std::string> S;
315
9
      if (E->getType()->isAnyPointerType() ||
316
9
          
isSmartPointerType(E->getType(), *Match.Context)5
) {
317
        // Strip off any operator->. This can only occur inside an actual arrow
318
        // member access, so we treat it as equivalent to an actual object
319
        // expression.
320
5
        if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {
321
1
          if (OpCall->getOperator() == clang::OO_Arrow &&
322
1
              OpCall->getNumArgs() == 1) {
323
1
            E = OpCall->getArg(0);
324
1
          }
325
1
        }
326
5
        S = tooling::buildArrow(*E, *Match.Context);
327
5
      } else 
if (const auto *4
Operand4
= isSmartDereference(*E, *Match.Context)) {
328
        // `buildDot` already handles the built-in dereference operator, so we
329
        // only need to catch overloaded `operator*`.
330
1
        S = tooling::buildArrow(*Operand, *Match.Context);
331
3
      } else {
332
3
        S = tooling::buildDot(*E, *Match.Context);
333
3
      }
334
9
      if (S.hasValue())
335
9
        *Result += *S;
336
0
      else
337
0
        return llvm::make_error<StringError>(
338
0
            errc::invalid_argument,
339
0
            "Could not construct object text from ID: " + BaseId);
340
9
    }
341
10
    return Member->eval(Match, Result);
342
10
  }
343
};
344
345
class IfBoundStencil : public StencilInterface {
346
  std::string Id;
347
  Stencil TrueStencil;
348
  Stencil FalseStencil;
349
350
public:
351
  IfBoundStencil(StringRef Id, Stencil TrueStencil, Stencil FalseStencil)
352
      : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)),
353
5
        FalseStencil(std::move(FalseStencil)) {}
354
355
3
  std::string toString() const override {
356
3
    return (llvm::Twine("ifBound(\"") + Id + "\", " + TrueStencil->toString() +
357
3
            ", " + FalseStencil->toString() + ")")
358
3
        .str();
359
3
  }
360
361
  Error eval(const MatchFinder::MatchResult &Match,
362
2
             std::string *Result) const override {
363
2
    auto &M = Match.Nodes.getMap();
364
2
    return (M.find(Id) != M.end() ? 
TrueStencil1
:
FalseStencil1
)
365
2
        ->eval(Match, Result);
366
2
  }
367
};
368
369
class SequenceStencil : public StencilInterface {
370
  std::vector<Stencil> Stencils;
371
372
public:
373
  SequenceStencil(std::vector<Stencil> Stencils)
374
11
      : Stencils(std::move(Stencils)) {}
375
376
4
  std::string toString() const override {
377
4
    llvm::SmallVector<std::string, 2> Parts;
378
4
    Parts.reserve(Stencils.size());
379
4
    for (const auto &S : Stencils)
380
10
      Parts.push_back(S->toString());
381
4
    return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str();
382
4
  }
383
384
  Error eval(const MatchFinder::MatchResult &Match,
385
24
             std::string *Result) const override {
386
24
    for (const auto &S : Stencils)
387
94
      if (auto Err = S->eval(Match, Result))
388
3
        return Err;
389
21
    return Error::success();
390
24
  }
391
};
392
393
class RunStencil : public StencilInterface {
394
  MatchConsumer<std::string> Consumer;
395
396
public:
397
2
  explicit RunStencil(MatchConsumer<std::string> C) : Consumer(std::move(C)) {}
398
399
1
  std::string toString() const override { return "run(...)"; }
400
401
  Error eval(const MatchFinder::MatchResult &Match,
402
1
             std::string *Result) const override {
403
404
1
    Expected<std::string> Value = Consumer(Match);
405
1
    if (!Value)
406
0
      return Value.takeError();
407
1
    *Result += *Value;
408
1
    return Error::success();
409
1
  }
410
};
411
} // namespace
412
413
129
Stencil transformer::detail::makeStencil(StringRef Text) {
414
129
  return std::make_shared<RawTextStencil>(std::string(Text));
415
129
}
416
417
19
Stencil transformer::detail::makeStencil(RangeSelector Selector) {
418
19
  return std::make_shared<SelectorStencil>(std::move(Selector));
419
19
}
420
421
1
Stencil transformer::dPrint(StringRef Id) {
422
1
  return std::make_shared<DebugPrintNodeStencil>(std::string(Id));
423
1
}
424
425
5
Stencil transformer::expression(llvm::StringRef Id) {
426
5
  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Parens,
427
5
                                                 std::string(Id));
428
5
}
429
430
4
Stencil transformer::deref(llvm::StringRef ExprId) {
431
4
  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Deref,
432
4
                                                 std::string(ExprId));
433
4
}
434
435
7
Stencil transformer::maybeDeref(llvm::StringRef ExprId) {
436
7
  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::MaybeDeref,
437
7
                                                 std::string(ExprId));
438
7
}
439
440
5
Stencil transformer::addressOf(llvm::StringRef ExprId) {
441
5
  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::AddressOf,
442
5
                                                 std::string(ExprId));
443
5
}
444
445
5
Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) {
446
5
  return std::make_shared<UnaryOperationStencil>(
447
5
      UnaryNodeOperator::MaybeAddressOf, std::string(ExprId));
448
5
}
449
450
7
Stencil transformer::describe(StringRef Id) {
451
7
  return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Describe,
452
7
                                                 std::string(Id));
453
7
}
454
455
18
Stencil transformer::access(StringRef BaseId, Stencil Member) {
456
18
  return std::make_shared<AccessStencil>(BaseId, std::move(Member));
457
18
}
458
459
Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil,
460
5
                             Stencil FalseStencil) {
461
5
  return std::make_shared<IfBoundStencil>(Id, std::move(TrueStencil),
462
5
                                          std::move(FalseStencil));
463
5
}
464
465
2
Stencil transformer::run(MatchConsumer<std::string> Fn) {
466
2
  return std::make_shared<RunStencil>(std::move(Fn));
467
2
}
468
469
111
Stencil transformer::catVector(std::vector<Stencil> Parts) {
470
  // Only one argument, so don't wrap in sequence.
471
111
  if (Parts.size() == 1)
472
100
    return std::move(Parts[0]);
473
11
  return std::make_shared<SequenceStencil>(std::move(Parts));
474
111
}