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