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/ASTMatchers/ASTMatchers.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/Twine.h"
19 #include "llvm/Support/Errc.h"
20 #include <atomic>
21 #include <memory>
22 #include <string>
23 
24 using namespace clang;
25 using namespace tooling;
26 
27 using ast_matchers::MatchFinder;
28 using ast_type_traits::DynTypedNode;
29 using llvm::errc;
30 using llvm::Error;
31 using llvm::Expected;
32 using llvm::StringError;
33 
34 static llvm::Expected<DynTypedNode>
35 getNode(const ast_matchers::BoundNodes &Nodes, StringRef Id) {
36   auto &NodesMap = Nodes.getMap();
37   auto It = NodesMap.find(Id);
38   if (It == NodesMap.end())
39     return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
40                                                "Id not bound: " + Id);
41   return It->second;
42 }
43 
44 namespace {
45 // An arbitrary fragment of code within a stencil.
46 struct RawTextData {
47   explicit RawTextData(std::string T) : Text(std::move(T)) {}
48   std::string Text;
49 };
50 
51 // A debugging operation to dump the AST for a particular (bound) AST node.
52 struct DebugPrintNodeData {
53   explicit DebugPrintNodeData(std::string S) : Id(std::move(S)) {}
54   std::string Id;
55 };
56 
57 // Operators that take a single node Id as an argument.
58 enum class UnaryNodeOperator {
59   Parens,
60   Deref,
61   Address,
62 };
63 
64 // Generic container for stencil operations with a (single) node-id argument.
65 struct UnaryOperationData {
66   UnaryOperationData(UnaryNodeOperator Op, std::string Id)
67       : Op(Op), Id(std::move(Id)) {}
68   UnaryNodeOperator Op;
69   std::string Id;
70 };
71 
72 // The fragment of code corresponding to the selected range.
73 struct SelectorData {
74   explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {}
75   RangeSelector Selector;
76 };
77 
78 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
79 struct AccessData {
80   AccessData(StringRef BaseId, StencilPart Member)
81       : BaseId(BaseId), Member(std::move(Member)) {}
82   std::string BaseId;
83   StencilPart Member;
84 };
85 
86 struct IfBoundData {
87   IfBoundData(StringRef Id, StencilPart TruePart, StencilPart FalsePart)
88       : Id(Id), TruePart(std::move(TruePart)), FalsePart(std::move(FalsePart)) {
89   }
90   std::string Id;
91   StencilPart TruePart;
92   StencilPart FalsePart;
93 };
94 
95 std::string toStringData(const RawTextData &Data) {
96   std::string Result;
97   llvm::raw_string_ostream OS(Result);
98   OS << "\"";
99   OS.write_escaped(Data.Text);
100   OS << "\"";
101   OS.flush();
102   return Result;
103 }
104 
105 std::string toStringData(const DebugPrintNodeData &Data) {
106   return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str();
107 }
108 
109 std::string toStringData(const UnaryOperationData &Data) {
110   StringRef OpName;
111   switch (Data.Op) {
112   case UnaryNodeOperator::Parens:
113     OpName = "expression";
114     break;
115   case UnaryNodeOperator::Deref:
116     OpName = "deref";
117     break;
118   case UnaryNodeOperator::Address:
119     OpName = "addressOf";
120     break;
121   }
122   return (OpName + "(\"" + Data.Id + "\")").str();
123 }
124 
125 std::string toStringData(const SelectorData &) { return "selection(...)"; }
126 
127 std::string toStringData(const AccessData &Data) {
128   return (llvm::Twine("access(\"") + Data.BaseId + "\", " +
129           Data.Member.toString() + ")")
130       .str();
131 }
132 
133 std::string toStringData(const IfBoundData &Data) {
134   return (llvm::Twine("ifBound(\"") + Data.Id + "\", " +
135           Data.TruePart.toString() + ", " + Data.FalsePart.toString() + ")")
136       .str();
137 }
138 
139 std::string toStringData(const MatchConsumer<std::string> &) {
140   return "run(...)";
141 }
142 
143 // The `evalData()` overloads evaluate the given stencil data to a string, given
144 // the match result, and append it to `Result`. We define an overload for each
145 // type of stencil data.
146 
147 Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &,
148                std::string *Result) {
149   Result->append(Data.Text);
150   return Error::success();
151 }
152 
153 Error evalData(const DebugPrintNodeData &Data,
154                const MatchFinder::MatchResult &Match, std::string *Result) {
155   std::string Output;
156   llvm::raw_string_ostream Os(Output);
157   auto NodeOrErr = getNode(Match.Nodes, Data.Id);
158   if (auto Err = NodeOrErr.takeError())
159     return Err;
160   NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts()));
161   *Result += Os.str();
162   return Error::success();
163 }
164 
165 Error evalData(const UnaryOperationData &Data,
166                const MatchFinder::MatchResult &Match, std::string *Result) {
167   const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id);
168   if (E == nullptr)
169     return llvm::make_error<StringError>(
170         errc::invalid_argument, "Id not bound or not Expr: " + Data.Id);
171   llvm::Optional<std::string> Source;
172   switch (Data.Op) {
173   case UnaryNodeOperator::Parens:
174     Source = buildParens(*E, *Match.Context);
175     break;
176   case UnaryNodeOperator::Deref:
177     Source = buildDereference(*E, *Match.Context);
178     break;
179   case UnaryNodeOperator::Address:
180     Source = buildAddressOf(*E, *Match.Context);
181     break;
182   }
183   if (!Source)
184     return llvm::make_error<StringError>(
185         errc::invalid_argument,
186         "Could not construct expression source from ID: " + Data.Id);
187   *Result += *Source;
188   return Error::success();
189 }
190 
191 Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match,
192                std::string *Result) {
193   auto Range = Data.Selector(Match);
194   if (!Range)
195     return Range.takeError();
196   *Result += getText(*Range, *Match.Context);
197   return Error::success();
198 }
199 
200 Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match,
201                std::string *Result) {
202   const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId);
203   if (E == nullptr)
204     return llvm::make_error<StringError>(errc::invalid_argument,
205                                          "Id not bound: " + Data.BaseId);
206   if (!E->isImplicitCXXThis()) {
207     if (llvm::Optional<std::string> S = E->getType()->isAnyPointerType()
208                                             ? buildArrow(*E, *Match.Context)
209                                             : buildDot(*E, *Match.Context))
210       *Result += *S;
211     else
212       return llvm::make_error<StringError>(
213           errc::invalid_argument,
214           "Could not construct object text from ID: " + Data.BaseId);
215   }
216   return Data.Member.eval(Match, Result);
217 }
218 
219 Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match,
220                std::string *Result) {
221   auto &M = Match.Nodes.getMap();
222   return (M.find(Data.Id) != M.end() ? Data.TruePart : Data.FalsePart)
223       .eval(Match, Result);
224 }
225 
226 Error evalData(const MatchConsumer<std::string> &Fn,
227                const MatchFinder::MatchResult &Match, std::string *Result) {
228   Expected<std::string> Value = Fn(Match);
229   if (!Value)
230     return Value.takeError();
231   *Result += *Value;
232   return Error::success();
233 }
234 
235 template <typename T>
236 class StencilPartImpl : public StencilPartInterface {
237   T Data;
238 
239 public:
240   template <typename... Ps>
241   explicit StencilPartImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {}
242 
243   Error eval(const MatchFinder::MatchResult &Match,
244              std::string *Result) const override {
245     return evalData(Data, Match, Result);
246   }
247 
248   std::string toString() const override { return toStringData(Data); }
249 };
250 } // namespace
251 
252 StencilPart Stencil::wrap(StringRef Text) {
253   return stencil::text(Text);
254 }
255 
256 StencilPart Stencil::wrap(RangeSelector Selector) {
257   return stencil::selection(std::move(Selector));
258 }
259 
260 void Stencil::append(Stencil OtherStencil) {
261   for (auto &Part : OtherStencil.Parts)
262     Parts.push_back(std::move(Part));
263 }
264 
265 llvm::Expected<std::string>
266 Stencil::eval(const MatchFinder::MatchResult &Match) const {
267   std::string Result;
268   for (const auto &Part : Parts)
269     if (auto Err = Part.eval(Match, &Result))
270       return std::move(Err);
271   return Result;
272 }
273 
274 StencilPart stencil::text(StringRef Text) {
275   return StencilPart(std::make_shared<StencilPartImpl<RawTextData>>(Text));
276 }
277 
278 StencilPart stencil::selection(RangeSelector Selector) {
279   return StencilPart(
280       std::make_shared<StencilPartImpl<SelectorData>>(std::move(Selector)));
281 }
282 
283 StencilPart stencil::dPrint(StringRef Id) {
284   return StencilPart(std::make_shared<StencilPartImpl<DebugPrintNodeData>>(Id));
285 }
286 
287 StencilPart stencil::expression(llvm::StringRef Id) {
288   return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
289       UnaryNodeOperator::Parens, Id));
290 }
291 
292 StencilPart stencil::deref(llvm::StringRef ExprId) {
293   return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
294       UnaryNodeOperator::Deref, ExprId));
295 }
296 
297 StencilPart stencil::addressOf(llvm::StringRef ExprId) {
298   return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
299       UnaryNodeOperator::Address, ExprId));
300 }
301 
302 StencilPart stencil::access(StringRef BaseId, StencilPart Member) {
303   return StencilPart(
304       std::make_shared<StencilPartImpl<AccessData>>(BaseId, std::move(Member)));
305 }
306 
307 StencilPart stencil::ifBound(StringRef Id, StencilPart TruePart,
308                              StencilPart FalsePart) {
309   return StencilPart(std::make_shared<StencilPartImpl<IfBoundData>>(
310       Id, std::move(TruePart), std::move(FalsePart)));
311 }
312 
313 StencilPart stencil::run(MatchConsumer<std::string> Fn) {
314   return StencilPart(
315       std::make_shared<StencilPartImpl<MatchConsumer<std::string>>>(
316           std::move(Fn)));
317 }
318