xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/Transformer/Stencil.cpp (revision 9c77fb6aaa366cbabc80ee1b834bcfe4df135491)
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 
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 
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:
69   explicit RawTextStencil(std::string T) : Text(std::move(T)) {}
70 
71   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 
80   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:
92   explicit DebugPrintNodeStencil(std::string S) : Id(std::move(S)) {}
93 
94   std::string toString() const override {
95     return (llvm::Twine("dPrint(\"") + Id + "\")").str();
96   }
97 
98   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:
120   UnaryOperationStencil(UnaryNodeOperator Op, std::string Id)
121       : Op(Op), Id(std::move(Id)) {}
122 
123   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 
148   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:
221   explicit SelectorStencil(RangeSelector S) : Selector(std::move(S)) {}
222 
223   std::string toString() const override { return "selection(...)"; }
224 
225   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:
270   AccessStencil(StringRef BaseId, Stencil Member)
271       : BaseId(std::string(BaseId)), Member(std::move(Member)) {}
272 
273   std::string toString() const override {
274     return (llvm::Twine("access(\"") + BaseId + "\", " + Member->toString() +
275             ")")
276         .str();
277   }
278 
279   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:
301   IfBoundStencil(StringRef Id, Stencil TrueStencil, Stencil FalseStencil)
302       : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)),
303         FalseStencil(std::move(FalseStencil)) {}
304 
305   std::string toString() const override {
306     return (llvm::Twine("ifBound(\"") + Id + "\", " + TrueStencil->toString() +
307             ", " + FalseStencil->toString() + ")")
308         .str();
309   }
310 
311   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 {
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:
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   }
335   ~SelectBoundStencil() override {}
336 
337   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 
361   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:
390   SequenceStencil(std::vector<Stencil> Stencils)
391       : Stencils(std::move(Stencils)) {}
392 
393   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 
401   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:
414   explicit RunStencil(MatchConsumer<std::string> C) : Consumer(std::move(C)) {}
415 
416   std::string toString() const override { return "run(...)"; }
417 
418   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 
430 Stencil transformer::detail::makeStencil(StringRef Text) {
431   return std::make_shared<RawTextStencil>(std::string(Text));
432 }
433 
434 Stencil transformer::detail::makeStencil(RangeSelector Selector) {
435   return std::make_shared<SelectorStencil>(std::move(Selector));
436 }
437 
438 Stencil transformer::dPrint(StringRef Id) {
439   return std::make_shared<DebugPrintNodeStencil>(std::string(Id));
440 }
441 
442 Stencil transformer::expression(llvm::StringRef Id) {
443   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Parens,
444                                                  std::string(Id));
445 }
446 
447 Stencil transformer::deref(llvm::StringRef ExprId) {
448   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Deref,
449                                                  std::string(ExprId));
450 }
451 
452 Stencil transformer::maybeDeref(llvm::StringRef ExprId) {
453   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::MaybeDeref,
454                                                  std::string(ExprId));
455 }
456 
457 Stencil transformer::addressOf(llvm::StringRef ExprId) {
458   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::AddressOf,
459                                                  std::string(ExprId));
460 }
461 
462 Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) {
463   return std::make_shared<UnaryOperationStencil>(
464       UnaryNodeOperator::MaybeAddressOf, std::string(ExprId));
465 }
466 
467 Stencil transformer::describe(StringRef Id) {
468   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Describe,
469                                                  std::string(Id));
470 }
471 
472 Stencil transformer::access(StringRef BaseId, Stencil Member) {
473   return std::make_shared<AccessStencil>(BaseId, std::move(Member));
474 }
475 
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 
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 
489 Stencil transformer::run(MatchConsumer<std::string> Fn) {
490   return std::make_shared<RunStencil>(std::move(Fn));
491 }
492 
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