xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/Transformer/Stencil.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1a7dea167SDimitry Andric //===--- Stencil.cpp - Stencil implementation -------------------*- C++ -*-===//
2a7dea167SDimitry Andric //
3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a7dea167SDimitry Andric //
7a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
8a7dea167SDimitry Andric 
9a7dea167SDimitry Andric #include "clang/Tooling/Transformer/Stencil.h"
10a7dea167SDimitry Andric #include "clang/AST/ASTContext.h"
11a7dea167SDimitry Andric #include "clang/AST/ASTTypeTraits.h"
12a7dea167SDimitry Andric #include "clang/AST/Expr.h"
13a7dea167SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
145ffd83dbSDimitry Andric #include "clang/Basic/SourceLocation.h"
15a7dea167SDimitry Andric #include "clang/Lex/Lexer.h"
16a7dea167SDimitry Andric #include "clang/Tooling/Transformer/SourceCode.h"
17a7dea167SDimitry Andric #include "clang/Tooling/Transformer/SourceCodeBuilders.h"
18480093f4SDimitry Andric #include "llvm/ADT/SmallVector.h"
19a7dea167SDimitry Andric #include "llvm/ADT/Twine.h"
20a7dea167SDimitry Andric #include "llvm/Support/Errc.h"
215ffd83dbSDimitry Andric #include "llvm/Support/Error.h"
22a7dea167SDimitry Andric #include <atomic>
23a7dea167SDimitry Andric #include <memory>
24a7dea167SDimitry Andric #include <string>
25a7dea167SDimitry Andric 
26a7dea167SDimitry Andric using namespace clang;
27a7dea167SDimitry Andric using namespace transformer;
28a7dea167SDimitry Andric 
29349cc55cSDimitry Andric using ast_matchers::BoundNodes;
30a7dea167SDimitry Andric using ast_matchers::MatchFinder;
31a7dea167SDimitry Andric using llvm::errc;
32a7dea167SDimitry Andric using llvm::Error;
33a7dea167SDimitry Andric using llvm::Expected;
34a7dea167SDimitry Andric using llvm::StringError;
35a7dea167SDimitry Andric 
getNode(const BoundNodes & Nodes,StringRef Id)36349cc55cSDimitry Andric static llvm::Expected<DynTypedNode> getNode(const BoundNodes &Nodes,
37349cc55cSDimitry Andric                                             StringRef Id) {
38a7dea167SDimitry Andric   auto &NodesMap = Nodes.getMap();
39a7dea167SDimitry Andric   auto It = NodesMap.find(Id);
40a7dea167SDimitry Andric   if (It == NodesMap.end())
41a7dea167SDimitry Andric     return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
42a7dea167SDimitry Andric                                                "Id not bound: " + Id);
43a7dea167SDimitry Andric   return It->second;
44a7dea167SDimitry Andric }
45a7dea167SDimitry Andric 
printNode(StringRef Id,const MatchFinder::MatchResult & Match,std::string * Result)46e8d8bef9SDimitry Andric static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match,
47e8d8bef9SDimitry Andric                        std::string *Result) {
48a7dea167SDimitry Andric   std::string Output;
49a7dea167SDimitry Andric   llvm::raw_string_ostream Os(Output);
50e8d8bef9SDimitry Andric   auto NodeOrErr = getNode(Match.Nodes, Id);
51a7dea167SDimitry Andric   if (auto Err = NodeOrErr.takeError())
52a7dea167SDimitry Andric     return Err;
53a7dea167SDimitry Andric   NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts()));
54*0fca6ea1SDimitry Andric   *Result += Output;
55a7dea167SDimitry Andric   return Error::success();
56a7dea167SDimitry Andric }
57a7dea167SDimitry Andric 
58fe6060f1SDimitry Andric namespace {
59fe6060f1SDimitry Andric // An arbitrary fragment of code within a stencil.
60fe6060f1SDimitry Andric class RawTextStencil : public StencilInterface {
61fe6060f1SDimitry Andric   std::string Text;
62fe6060f1SDimitry Andric 
63fe6060f1SDimitry Andric public:
RawTextStencil(std::string T)64fe6060f1SDimitry Andric   explicit RawTextStencil(std::string T) : Text(std::move(T)) {}
65fe6060f1SDimitry Andric 
toString() const66fe6060f1SDimitry Andric   std::string toString() const override {
67fe6060f1SDimitry Andric     std::string Result;
68fe6060f1SDimitry Andric     llvm::raw_string_ostream OS(Result);
69fe6060f1SDimitry Andric     OS << "\"";
70fe6060f1SDimitry Andric     OS.write_escaped(Text);
71fe6060f1SDimitry Andric     OS << "\"";
72fe6060f1SDimitry Andric     OS.flush();
73fe6060f1SDimitry Andric     return Result;
74fe6060f1SDimitry Andric   }
75fe6060f1SDimitry Andric 
eval(const MatchFinder::MatchResult & Match,std::string * Result) const76fe6060f1SDimitry Andric   Error eval(const MatchFinder::MatchResult &Match,
77fe6060f1SDimitry Andric              std::string *Result) const override {
78fe6060f1SDimitry Andric     Result->append(Text);
79fe6060f1SDimitry Andric     return Error::success();
80fe6060f1SDimitry Andric   }
81fe6060f1SDimitry Andric };
82fe6060f1SDimitry Andric 
83fe6060f1SDimitry Andric // A debugging operation to dump the AST for a particular (bound) AST node.
84fe6060f1SDimitry Andric class DebugPrintNodeStencil : public StencilInterface {
85fe6060f1SDimitry Andric   std::string Id;
86fe6060f1SDimitry Andric 
87fe6060f1SDimitry Andric public:
DebugPrintNodeStencil(std::string S)88fe6060f1SDimitry Andric   explicit DebugPrintNodeStencil(std::string S) : Id(std::move(S)) {}
89fe6060f1SDimitry Andric 
toString() const90fe6060f1SDimitry Andric   std::string toString() const override {
91fe6060f1SDimitry Andric     return (llvm::Twine("dPrint(\"") + Id + "\")").str();
92fe6060f1SDimitry Andric   }
93fe6060f1SDimitry Andric 
eval(const MatchFinder::MatchResult & Match,std::string * Result) const94fe6060f1SDimitry Andric   Error eval(const MatchFinder::MatchResult &Match,
95fe6060f1SDimitry Andric              std::string *Result) const override {
96fe6060f1SDimitry Andric     return printNode(Id, Match, Result);
97fe6060f1SDimitry Andric   }
98fe6060f1SDimitry Andric };
99fe6060f1SDimitry Andric 
100fe6060f1SDimitry Andric // Operators that take a single node Id as an argument.
101fe6060f1SDimitry Andric enum class UnaryNodeOperator {
102fe6060f1SDimitry Andric   Parens,
103fe6060f1SDimitry Andric   Deref,
104fe6060f1SDimitry Andric   MaybeDeref,
105fe6060f1SDimitry Andric   AddressOf,
106fe6060f1SDimitry Andric   MaybeAddressOf,
107fe6060f1SDimitry Andric   Describe,
108fe6060f1SDimitry Andric };
109fe6060f1SDimitry Andric 
110fe6060f1SDimitry Andric // Generic container for stencil operations with a (single) node-id argument.
111fe6060f1SDimitry Andric class UnaryOperationStencil : public StencilInterface {
112fe6060f1SDimitry Andric   UnaryNodeOperator Op;
113fe6060f1SDimitry Andric   std::string Id;
114fe6060f1SDimitry Andric 
115fe6060f1SDimitry Andric public:
UnaryOperationStencil(UnaryNodeOperator Op,std::string Id)116fe6060f1SDimitry Andric   UnaryOperationStencil(UnaryNodeOperator Op, std::string Id)
117fe6060f1SDimitry Andric       : Op(Op), Id(std::move(Id)) {}
118fe6060f1SDimitry Andric 
toString() const119fe6060f1SDimitry Andric   std::string toString() const override {
120fe6060f1SDimitry Andric     StringRef OpName;
121fe6060f1SDimitry Andric     switch (Op) {
122fe6060f1SDimitry Andric     case UnaryNodeOperator::Parens:
123fe6060f1SDimitry Andric       OpName = "expression";
124fe6060f1SDimitry Andric       break;
125fe6060f1SDimitry Andric     case UnaryNodeOperator::Deref:
126fe6060f1SDimitry Andric       OpName = "deref";
127fe6060f1SDimitry Andric       break;
128fe6060f1SDimitry Andric     case UnaryNodeOperator::MaybeDeref:
129fe6060f1SDimitry Andric       OpName = "maybeDeref";
130fe6060f1SDimitry Andric       break;
131fe6060f1SDimitry Andric     case UnaryNodeOperator::AddressOf:
132fe6060f1SDimitry Andric       OpName = "addressOf";
133fe6060f1SDimitry Andric       break;
134fe6060f1SDimitry Andric     case UnaryNodeOperator::MaybeAddressOf:
135fe6060f1SDimitry Andric       OpName = "maybeAddressOf";
136fe6060f1SDimitry Andric       break;
137fe6060f1SDimitry Andric     case UnaryNodeOperator::Describe:
138fe6060f1SDimitry Andric       OpName = "describe";
139fe6060f1SDimitry Andric       break;
140fe6060f1SDimitry Andric     }
141fe6060f1SDimitry Andric     return (OpName + "(\"" + Id + "\")").str();
142fe6060f1SDimitry Andric   }
143fe6060f1SDimitry Andric 
eval(const MatchFinder::MatchResult & Match,std::string * Result) const144fe6060f1SDimitry Andric   Error eval(const MatchFinder::MatchResult &Match,
145fe6060f1SDimitry Andric              std::string *Result) const override {
146fe6060f1SDimitry Andric     // The `Describe` operation can be applied to any node, not just
147fe6060f1SDimitry Andric     // expressions, so it is handled here, separately.
148fe6060f1SDimitry Andric     if (Op == UnaryNodeOperator::Describe)
149fe6060f1SDimitry Andric       return printNode(Id, Match, Result);
150fe6060f1SDimitry Andric 
151fe6060f1SDimitry Andric     const auto *E = Match.Nodes.getNodeAs<Expr>(Id);
152a7dea167SDimitry Andric     if (E == nullptr)
153fe6060f1SDimitry Andric       return llvm::make_error<StringError>(errc::invalid_argument,
154fe6060f1SDimitry Andric                                            "Id not bound or not Expr: " + Id);
155bdd1243dSDimitry Andric     std::optional<std::string> Source;
156fe6060f1SDimitry Andric     switch (Op) {
157a7dea167SDimitry Andric     case UnaryNodeOperator::Parens:
158a7dea167SDimitry Andric       Source = tooling::buildParens(*E, *Match.Context);
159a7dea167SDimitry Andric       break;
160a7dea167SDimitry Andric     case UnaryNodeOperator::Deref:
161a7dea167SDimitry Andric       Source = tooling::buildDereference(*E, *Match.Context);
162a7dea167SDimitry Andric       break;
163480093f4SDimitry Andric     case UnaryNodeOperator::MaybeDeref:
164e8d8bef9SDimitry Andric       if (E->getType()->isAnyPointerType() ||
16504eeddc0SDimitry Andric           tooling::isKnownPointerLikeType(E->getType(), *Match.Context)) {
166e8d8bef9SDimitry Andric         // Strip off any operator->. This can only occur inside an actual arrow
167e8d8bef9SDimitry Andric         // member access, so we treat it as equivalent to an actual object
168e8d8bef9SDimitry Andric         // expression.
169e8d8bef9SDimitry Andric         if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {
170e8d8bef9SDimitry Andric           if (OpCall->getOperator() == clang::OO_Arrow &&
171e8d8bef9SDimitry Andric               OpCall->getNumArgs() == 1) {
172e8d8bef9SDimitry Andric             E = OpCall->getArg(0);
173e8d8bef9SDimitry Andric           }
174480093f4SDimitry Andric         }
175480093f4SDimitry Andric         Source = tooling::buildDereference(*E, *Match.Context);
176480093f4SDimitry Andric         break;
177e8d8bef9SDimitry Andric       }
178e8d8bef9SDimitry Andric       *Result += tooling::getText(*E, *Match.Context);
179e8d8bef9SDimitry Andric       return Error::success();
180480093f4SDimitry Andric     case UnaryNodeOperator::AddressOf:
181480093f4SDimitry Andric       Source = tooling::buildAddressOf(*E, *Match.Context);
182480093f4SDimitry Andric       break;
183480093f4SDimitry Andric     case UnaryNodeOperator::MaybeAddressOf:
184e8d8bef9SDimitry Andric       if (E->getType()->isAnyPointerType() ||
18504eeddc0SDimitry Andric           tooling::isKnownPointerLikeType(E->getType(), *Match.Context)) {
186e8d8bef9SDimitry Andric         // Strip off any operator->. This can only occur inside an actual arrow
187e8d8bef9SDimitry Andric         // member access, so we treat it as equivalent to an actual object
188e8d8bef9SDimitry Andric         // expression.
189e8d8bef9SDimitry Andric         if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {
190e8d8bef9SDimitry Andric           if (OpCall->getOperator() == clang::OO_Arrow &&
191e8d8bef9SDimitry Andric               OpCall->getNumArgs() == 1) {
192e8d8bef9SDimitry Andric             E = OpCall->getArg(0);
193e8d8bef9SDimitry Andric           }
194e8d8bef9SDimitry Andric         }
195480093f4SDimitry Andric         *Result += tooling::getText(*E, *Match.Context);
196480093f4SDimitry Andric         return Error::success();
197480093f4SDimitry Andric       }
198a7dea167SDimitry Andric       Source = tooling::buildAddressOf(*E, *Match.Context);
199a7dea167SDimitry Andric       break;
200e8d8bef9SDimitry Andric     case UnaryNodeOperator::Describe:
201e8d8bef9SDimitry Andric       llvm_unreachable("This case is handled at the start of the function");
202a7dea167SDimitry Andric     }
203a7dea167SDimitry Andric     if (!Source)
204a7dea167SDimitry Andric       return llvm::make_error<StringError>(
205a7dea167SDimitry Andric           errc::invalid_argument,
206fe6060f1SDimitry Andric           "Could not construct expression source from ID: " + Id);
207a7dea167SDimitry Andric     *Result += *Source;
208a7dea167SDimitry Andric     return Error::success();
209a7dea167SDimitry Andric   }
210fe6060f1SDimitry Andric };
211a7dea167SDimitry Andric 
212fe6060f1SDimitry Andric // The fragment of code corresponding to the selected range.
213fe6060f1SDimitry Andric class SelectorStencil : public StencilInterface {
214fe6060f1SDimitry Andric   RangeSelector Selector;
215fe6060f1SDimitry Andric 
216fe6060f1SDimitry Andric public:
SelectorStencil(RangeSelector S)217fe6060f1SDimitry Andric   explicit SelectorStencil(RangeSelector S) : Selector(std::move(S)) {}
218fe6060f1SDimitry Andric 
toString() const219fe6060f1SDimitry Andric   std::string toString() const override { return "selection(...)"; }
220fe6060f1SDimitry Andric 
eval(const MatchFinder::MatchResult & Match,std::string * Result) const221fe6060f1SDimitry Andric   Error eval(const MatchFinder::MatchResult &Match,
222fe6060f1SDimitry Andric              std::string *Result) const override {
223fe6060f1SDimitry Andric     auto RawRange = Selector(Match);
2245ffd83dbSDimitry Andric     if (!RawRange)
2255ffd83dbSDimitry Andric       return RawRange.takeError();
2265ffd83dbSDimitry Andric     CharSourceRange Range = Lexer::makeFileCharRange(
2275ffd83dbSDimitry Andric         *RawRange, *Match.SourceManager, Match.Context->getLangOpts());
2285ffd83dbSDimitry Andric     if (Range.isInvalid()) {
229fe6060f1SDimitry Andric       // Validate the original range to attempt to get a meaningful error
230fe6060f1SDimitry Andric       // message. If it's valid, then something else is the cause and we just
231fe6060f1SDimitry Andric       // return the generic failure message.
2325f757f3fSDimitry Andric       if (auto Err = tooling::validateRange(*RawRange, *Match.SourceManager,
2335f757f3fSDimitry Andric                                             /*AllowSystemHeaders=*/true))
2345ffd83dbSDimitry Andric         return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) {
2355ffd83dbSDimitry Andric           assert(E->convertToErrorCode() ==
2365ffd83dbSDimitry Andric                      llvm::make_error_code(errc::invalid_argument) &&
2375ffd83dbSDimitry Andric                  "Validation errors must carry the invalid_argument code");
2385ffd83dbSDimitry Andric           return llvm::createStringError(
2395ffd83dbSDimitry Andric               errc::invalid_argument,
2405ffd83dbSDimitry Andric               "selected range could not be resolved to a valid source range; " +
2415ffd83dbSDimitry Andric                   E->getMessage());
2425ffd83dbSDimitry Andric         });
2435ffd83dbSDimitry Andric       return llvm::createStringError(
2445ffd83dbSDimitry Andric           errc::invalid_argument,
2455ffd83dbSDimitry Andric           "selected range could not be resolved to a valid source range");
2465ffd83dbSDimitry Andric     }
2475ffd83dbSDimitry Andric     // Validate `Range`, because `makeFileCharRange` accepts some ranges that
2485f757f3fSDimitry Andric     // `validateRange` rejects.
2495f757f3fSDimitry Andric     if (auto Err = tooling::validateRange(Range, *Match.SourceManager,
2505f757f3fSDimitry Andric                                           /*AllowSystemHeaders=*/true))
2515ffd83dbSDimitry Andric       return joinErrors(
2525ffd83dbSDimitry Andric           llvm::createStringError(errc::invalid_argument,
2535ffd83dbSDimitry Andric                                   "selected range is not valid for editing"),
2545ffd83dbSDimitry Andric           std::move(Err));
2555ffd83dbSDimitry Andric     *Result += tooling::getText(Range, *Match.Context);
256a7dea167SDimitry Andric     return Error::success();
257a7dea167SDimitry Andric   }
258fe6060f1SDimitry Andric };
259a7dea167SDimitry Andric 
260fe6060f1SDimitry Andric // A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
261fe6060f1SDimitry Andric class AccessStencil : public StencilInterface {
262fe6060f1SDimitry Andric   std::string BaseId;
263fe6060f1SDimitry Andric   Stencil Member;
264fe6060f1SDimitry Andric 
265fe6060f1SDimitry Andric public:
AccessStencil(StringRef BaseId,Stencil Member)266fe6060f1SDimitry Andric   AccessStencil(StringRef BaseId, Stencil Member)
267fe6060f1SDimitry Andric       : BaseId(std::string(BaseId)), Member(std::move(Member)) {}
268fe6060f1SDimitry Andric 
toString() const269fe6060f1SDimitry Andric   std::string toString() const override {
270fe6060f1SDimitry Andric     return (llvm::Twine("access(\"") + BaseId + "\", " + Member->toString() +
271fe6060f1SDimitry Andric             ")")
272fe6060f1SDimitry Andric         .str();
273fe6060f1SDimitry Andric   }
274fe6060f1SDimitry Andric 
eval(const MatchFinder::MatchResult & Match,std::string * Result) const275fe6060f1SDimitry Andric   Error eval(const MatchFinder::MatchResult &Match,
276fe6060f1SDimitry Andric              std::string *Result) const override {
277fe6060f1SDimitry Andric     const auto *E = Match.Nodes.getNodeAs<Expr>(BaseId);
278a7dea167SDimitry Andric     if (E == nullptr)
279a7dea167SDimitry Andric       return llvm::make_error<StringError>(errc::invalid_argument,
280fe6060f1SDimitry Andric                                            "Id not bound: " + BaseId);
281bdd1243dSDimitry Andric     std::optional<std::string> S = tooling::buildAccess(*E, *Match.Context);
28281ad6265SDimitry Andric     if (!S)
283a7dea167SDimitry Andric       return llvm::make_error<StringError>(
284a7dea167SDimitry Andric           errc::invalid_argument,
285fe6060f1SDimitry Andric           "Could not construct object text from ID: " + BaseId);
28604eeddc0SDimitry Andric     *Result += *S;
287fe6060f1SDimitry Andric     return Member->eval(Match, Result);
288fe6060f1SDimitry Andric   }
289fe6060f1SDimitry Andric };
290fe6060f1SDimitry Andric 
291fe6060f1SDimitry Andric class IfBoundStencil : public StencilInterface {
292fe6060f1SDimitry Andric   std::string Id;
293fe6060f1SDimitry Andric   Stencil TrueStencil;
294fe6060f1SDimitry Andric   Stencil FalseStencil;
295fe6060f1SDimitry Andric 
296fe6060f1SDimitry Andric public:
IfBoundStencil(StringRef Id,Stencil TrueStencil,Stencil FalseStencil)297fe6060f1SDimitry Andric   IfBoundStencil(StringRef Id, Stencil TrueStencil, Stencil FalseStencil)
298fe6060f1SDimitry Andric       : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)),
299fe6060f1SDimitry Andric         FalseStencil(std::move(FalseStencil)) {}
300fe6060f1SDimitry Andric 
toString() const301fe6060f1SDimitry Andric   std::string toString() const override {
302fe6060f1SDimitry Andric     return (llvm::Twine("ifBound(\"") + Id + "\", " + TrueStencil->toString() +
303fe6060f1SDimitry Andric             ", " + FalseStencil->toString() + ")")
304fe6060f1SDimitry Andric         .str();
305a7dea167SDimitry Andric   }
306a7dea167SDimitry Andric 
eval(const MatchFinder::MatchResult & Match,std::string * Result) const307fe6060f1SDimitry Andric   Error eval(const MatchFinder::MatchResult &Match,
308fe6060f1SDimitry Andric              std::string *Result) const override {
309a7dea167SDimitry Andric     auto &M = Match.Nodes.getMap();
310fe6060f1SDimitry Andric     return (M.find(Id) != M.end() ? TrueStencil : FalseStencil)
311480093f4SDimitry Andric         ->eval(Match, Result);
312a7dea167SDimitry Andric   }
313fe6060f1SDimitry Andric };
314a7dea167SDimitry Andric 
315349cc55cSDimitry Andric class SelectBoundStencil : public clang::transformer::StencilInterface {
containsNoNullStencils(const std::vector<std::pair<std::string,Stencil>> & Cases)316349cc55cSDimitry Andric   static bool containsNoNullStencils(
317349cc55cSDimitry Andric       const std::vector<std::pair<std::string, Stencil>> &Cases) {
318349cc55cSDimitry Andric     for (const auto &S : Cases)
319349cc55cSDimitry Andric       if (S.second == nullptr)
320349cc55cSDimitry Andric         return false;
321349cc55cSDimitry Andric     return true;
322349cc55cSDimitry Andric   }
323349cc55cSDimitry Andric 
324349cc55cSDimitry Andric public:
SelectBoundStencil(std::vector<std::pair<std::string,Stencil>> Cases,Stencil Default)325349cc55cSDimitry Andric   SelectBoundStencil(std::vector<std::pair<std::string, Stencil>> Cases,
326349cc55cSDimitry Andric                      Stencil Default)
327349cc55cSDimitry Andric       : CaseStencils(std::move(Cases)), DefaultStencil(std::move(Default)) {
328349cc55cSDimitry Andric     assert(containsNoNullStencils(CaseStencils) &&
329349cc55cSDimitry Andric            "cases of selectBound may not be null");
330349cc55cSDimitry Andric   }
~SelectBoundStencil()33106c3fb27SDimitry Andric   ~SelectBoundStencil() override {}
332349cc55cSDimitry Andric 
eval(const MatchFinder::MatchResult & match,std::string * result) const333349cc55cSDimitry Andric   llvm::Error eval(const MatchFinder::MatchResult &match,
334349cc55cSDimitry Andric                    std::string *result) const override {
335349cc55cSDimitry Andric     const BoundNodes::IDToNodeMap &NodeMap = match.Nodes.getMap();
336349cc55cSDimitry Andric     for (const auto &S : CaseStencils) {
337349cc55cSDimitry Andric       if (NodeMap.count(S.first) > 0) {
338349cc55cSDimitry Andric         return S.second->eval(match, result);
339349cc55cSDimitry Andric       }
340349cc55cSDimitry Andric     }
341349cc55cSDimitry Andric 
342349cc55cSDimitry Andric     if (DefaultStencil != nullptr) {
343349cc55cSDimitry Andric       return DefaultStencil->eval(match, result);
344349cc55cSDimitry Andric     }
345349cc55cSDimitry Andric 
346349cc55cSDimitry Andric     llvm::SmallVector<llvm::StringRef, 2> CaseIDs;
347349cc55cSDimitry Andric     CaseIDs.reserve(CaseStencils.size());
348349cc55cSDimitry Andric     for (const auto &S : CaseStencils)
349349cc55cSDimitry Andric       CaseIDs.emplace_back(S.first);
350349cc55cSDimitry Andric 
351349cc55cSDimitry Andric     return llvm::createStringError(
352349cc55cSDimitry Andric         errc::result_out_of_range,
353349cc55cSDimitry Andric         llvm::Twine("selectBound failed: no cases bound and no default: {") +
354349cc55cSDimitry Andric             llvm::join(CaseIDs, ", ") + "}");
355349cc55cSDimitry Andric   }
356349cc55cSDimitry Andric 
toString() const357349cc55cSDimitry Andric   std::string toString() const override {
358349cc55cSDimitry Andric     std::string Buffer;
359349cc55cSDimitry Andric     llvm::raw_string_ostream Stream(Buffer);
360349cc55cSDimitry Andric     Stream << "selectBound({";
361349cc55cSDimitry Andric     bool First = true;
362349cc55cSDimitry Andric     for (const auto &S : CaseStencils) {
363349cc55cSDimitry Andric       if (First)
364349cc55cSDimitry Andric         First = false;
365349cc55cSDimitry Andric       else
366349cc55cSDimitry Andric         Stream << "}, ";
367349cc55cSDimitry Andric       Stream << "{\"" << S.first << "\", " << S.second->toString();
368349cc55cSDimitry Andric     }
369349cc55cSDimitry Andric     Stream << "}}";
370349cc55cSDimitry Andric     if (DefaultStencil != nullptr) {
371349cc55cSDimitry Andric       Stream << ", " << DefaultStencil->toString();
372349cc55cSDimitry Andric     }
373349cc55cSDimitry Andric     Stream << ")";
374*0fca6ea1SDimitry Andric     return Buffer;
375349cc55cSDimitry Andric   }
376349cc55cSDimitry Andric 
377349cc55cSDimitry Andric private:
378349cc55cSDimitry Andric   std::vector<std::pair<std::string, Stencil>> CaseStencils;
379349cc55cSDimitry Andric   Stencil DefaultStencil;
380349cc55cSDimitry Andric };
381349cc55cSDimitry Andric 
382fe6060f1SDimitry Andric class SequenceStencil : public StencilInterface {
383fe6060f1SDimitry Andric   std::vector<Stencil> Stencils;
384fe6060f1SDimitry Andric 
385fe6060f1SDimitry Andric public:
SequenceStencil(std::vector<Stencil> Stencils)386fe6060f1SDimitry Andric   SequenceStencil(std::vector<Stencil> Stencils)
387fe6060f1SDimitry Andric       : Stencils(std::move(Stencils)) {}
388fe6060f1SDimitry Andric 
toString() const389fe6060f1SDimitry Andric   std::string toString() const override {
390fe6060f1SDimitry Andric     llvm::SmallVector<std::string, 2> Parts;
391fe6060f1SDimitry Andric     Parts.reserve(Stencils.size());
392fe6060f1SDimitry Andric     for (const auto &S : Stencils)
393fe6060f1SDimitry Andric       Parts.push_back(S->toString());
394fe6060f1SDimitry Andric     return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str();
395fe6060f1SDimitry Andric   }
396fe6060f1SDimitry Andric 
eval(const MatchFinder::MatchResult & Match,std::string * Result) const397fe6060f1SDimitry Andric   Error eval(const MatchFinder::MatchResult &Match,
398fe6060f1SDimitry Andric              std::string *Result) const override {
399fe6060f1SDimitry Andric     for (const auto &S : Stencils)
400fe6060f1SDimitry Andric       if (auto Err = S->eval(Match, Result))
401fe6060f1SDimitry Andric         return Err;
402fe6060f1SDimitry Andric     return Error::success();
403fe6060f1SDimitry Andric   }
404fe6060f1SDimitry Andric };
405fe6060f1SDimitry Andric 
406fe6060f1SDimitry Andric class RunStencil : public StencilInterface {
407fe6060f1SDimitry Andric   MatchConsumer<std::string> Consumer;
408fe6060f1SDimitry Andric 
409fe6060f1SDimitry Andric public:
RunStencil(MatchConsumer<std::string> C)410fe6060f1SDimitry Andric   explicit RunStencil(MatchConsumer<std::string> C) : Consumer(std::move(C)) {}
411fe6060f1SDimitry Andric 
toString() const412fe6060f1SDimitry Andric   std::string toString() const override { return "run(...)"; }
413fe6060f1SDimitry Andric 
eval(const MatchFinder::MatchResult & Match,std::string * Result) const414fe6060f1SDimitry Andric   Error eval(const MatchFinder::MatchResult &Match,
415fe6060f1SDimitry Andric              std::string *Result) const override {
416fe6060f1SDimitry Andric 
417fe6060f1SDimitry Andric     Expected<std::string> Value = Consumer(Match);
418a7dea167SDimitry Andric     if (!Value)
419a7dea167SDimitry Andric       return Value.takeError();
420a7dea167SDimitry Andric     *Result += *Value;
421a7dea167SDimitry Andric     return Error::success();
422a7dea167SDimitry Andric   }
423a7dea167SDimitry Andric };
424a7dea167SDimitry Andric } // namespace
425a7dea167SDimitry Andric 
makeStencil(StringRef Text)4265ffd83dbSDimitry Andric Stencil transformer::detail::makeStencil(StringRef Text) {
427fe6060f1SDimitry Andric   return std::make_shared<RawTextStencil>(std::string(Text));
4285ffd83dbSDimitry Andric }
429480093f4SDimitry Andric 
makeStencil(RangeSelector Selector)430480093f4SDimitry Andric Stencil transformer::detail::makeStencil(RangeSelector Selector) {
431fe6060f1SDimitry Andric   return std::make_shared<SelectorStencil>(std::move(Selector));
432a7dea167SDimitry Andric }
433a7dea167SDimitry Andric 
dPrint(StringRef Id)434480093f4SDimitry Andric Stencil transformer::dPrint(StringRef Id) {
435fe6060f1SDimitry Andric   return std::make_shared<DebugPrintNodeStencil>(std::string(Id));
436a7dea167SDimitry Andric }
437a7dea167SDimitry Andric 
expression(llvm::StringRef Id)438480093f4SDimitry Andric Stencil transformer::expression(llvm::StringRef Id) {
439fe6060f1SDimitry Andric   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Parens,
440fe6060f1SDimitry Andric                                                  std::string(Id));
441a7dea167SDimitry Andric }
442a7dea167SDimitry Andric 
deref(llvm::StringRef ExprId)443480093f4SDimitry Andric Stencil transformer::deref(llvm::StringRef ExprId) {
444fe6060f1SDimitry Andric   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Deref,
445fe6060f1SDimitry Andric                                                  std::string(ExprId));
446a7dea167SDimitry Andric }
447a7dea167SDimitry Andric 
maybeDeref(llvm::StringRef ExprId)448480093f4SDimitry Andric Stencil transformer::maybeDeref(llvm::StringRef ExprId) {
449fe6060f1SDimitry Andric   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::MaybeDeref,
450fe6060f1SDimitry Andric                                                  std::string(ExprId));
451a7dea167SDimitry Andric }
452a7dea167SDimitry Andric 
addressOf(llvm::StringRef ExprId)453480093f4SDimitry Andric Stencil transformer::addressOf(llvm::StringRef ExprId) {
454fe6060f1SDimitry Andric   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::AddressOf,
455fe6060f1SDimitry Andric                                                  std::string(ExprId));
456a7dea167SDimitry Andric }
457a7dea167SDimitry Andric 
maybeAddressOf(llvm::StringRef ExprId)458480093f4SDimitry Andric Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) {
459fe6060f1SDimitry Andric   return std::make_shared<UnaryOperationStencil>(
4605ffd83dbSDimitry Andric       UnaryNodeOperator::MaybeAddressOf, std::string(ExprId));
461a7dea167SDimitry Andric }
462a7dea167SDimitry Andric 
describe(StringRef Id)463e8d8bef9SDimitry Andric Stencil transformer::describe(StringRef Id) {
464fe6060f1SDimitry Andric   return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Describe,
465fe6060f1SDimitry Andric                                                  std::string(Id));
466e8d8bef9SDimitry Andric }
467e8d8bef9SDimitry Andric 
access(StringRef BaseId,Stencil Member)468480093f4SDimitry Andric Stencil transformer::access(StringRef BaseId, Stencil Member) {
469fe6060f1SDimitry Andric   return std::make_shared<AccessStencil>(BaseId, std::move(Member));
470a7dea167SDimitry Andric }
471a7dea167SDimitry Andric 
ifBound(StringRef Id,Stencil TrueStencil,Stencil FalseStencil)472480093f4SDimitry Andric Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil,
473480093f4SDimitry Andric                              Stencil FalseStencil) {
474fe6060f1SDimitry Andric   return std::make_shared<IfBoundStencil>(Id, std::move(TrueStencil),
475480093f4SDimitry Andric                                           std::move(FalseStencil));
476a7dea167SDimitry Andric }
477a7dea167SDimitry Andric 
selectBound(std::vector<std::pair<std::string,Stencil>> CaseStencils,Stencil DefaultStencil)478349cc55cSDimitry Andric Stencil transformer::selectBound(
479349cc55cSDimitry Andric     std::vector<std::pair<std::string, Stencil>> CaseStencils,
480349cc55cSDimitry Andric     Stencil DefaultStencil) {
481349cc55cSDimitry Andric   return std::make_shared<SelectBoundStencil>(std::move(CaseStencils),
482349cc55cSDimitry Andric                                               std::move(DefaultStencil));
483349cc55cSDimitry Andric }
484349cc55cSDimitry Andric 
run(MatchConsumer<std::string> Fn)485480093f4SDimitry Andric Stencil transformer::run(MatchConsumer<std::string> Fn) {
486fe6060f1SDimitry Andric   return std::make_shared<RunStencil>(std::move(Fn));
487a7dea167SDimitry Andric }
488a7dea167SDimitry Andric 
catVector(std::vector<Stencil> Parts)489480093f4SDimitry Andric Stencil transformer::catVector(std::vector<Stencil> Parts) {
490480093f4SDimitry Andric   // Only one argument, so don't wrap in sequence.
491480093f4SDimitry Andric   if (Parts.size() == 1)
492480093f4SDimitry Andric     return std::move(Parts[0]);
493fe6060f1SDimitry Andric   return std::make_shared<SequenceStencil>(std::move(Parts));
494a7dea167SDimitry Andric }
495