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