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