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