Coverage Report

Created: 2022-05-17 06:19

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