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/SmallVector.h"
19 #include "llvm/ADT/Twine.h"
20 #include "llvm/Support/Errc.h"
21 #include <atomic>
22 #include <memory>
23 #include <string>
24 
25 using namespace clang;
26 using namespace transformer;
27 
28 using ast_matchers::MatchFinder;
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   MaybeDeref,
62   AddressOf,
63   MaybeAddressOf,
64 };
65 
66 // Generic container for stencil operations with a (single) node-id argument.
67 struct UnaryOperationData {
68   UnaryOperationData(UnaryNodeOperator Op, std::string Id)
69       : Op(Op), Id(std::move(Id)) {}
70   UnaryNodeOperator Op;
71   std::string Id;
72 };
73 
74 // The fragment of code corresponding to the selected range.
75 struct SelectorData {
76   explicit SelectorData(RangeSelector S) : Selector(std::move(S)) {}
77   RangeSelector Selector;
78 };
79 
80 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
81 struct AccessData {
82   AccessData(StringRef BaseId, Stencil Member)
83       : BaseId(std::string(BaseId)), Member(std::move(Member)) {}
84   std::string BaseId;
85   Stencil Member;
86 };
87 
88 struct IfBoundData {
89   IfBoundData(StringRef Id, Stencil TrueStencil, Stencil FalseStencil)
90       : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)),
91         FalseStencil(std::move(FalseStencil)) {}
92   std::string Id;
93   Stencil TrueStencil;
94   Stencil FalseStencil;
95 };
96 
97 struct SequenceData {
98   SequenceData(std::vector<Stencil> Stencils) : Stencils(std::move(Stencils)) {}
99   std::vector<Stencil> Stencils;
100 };
101 
102 std::string toStringData(const RawTextData &Data) {
103   std::string Result;
104   llvm::raw_string_ostream OS(Result);
105   OS << "\"";
106   OS.write_escaped(Data.Text);
107   OS << "\"";
108   OS.flush();
109   return Result;
110 }
111 
112 std::string toStringData(const DebugPrintNodeData &Data) {
113   return (llvm::Twine("dPrint(\"") + Data.Id + "\")").str();
114 }
115 
116 std::string toStringData(const UnaryOperationData &Data) {
117   StringRef OpName;
118   switch (Data.Op) {
119   case UnaryNodeOperator::Parens:
120     OpName = "expression";
121     break;
122   case UnaryNodeOperator::Deref:
123     OpName = "deref";
124     break;
125   case UnaryNodeOperator::MaybeDeref:
126     OpName = "maybeDeref";
127     break;
128   case UnaryNodeOperator::AddressOf:
129     OpName = "addressOf";
130     break;
131   case UnaryNodeOperator::MaybeAddressOf:
132     OpName = "maybeAddressOf";
133     break;
134   }
135   return (OpName + "(\"" + Data.Id + "\")").str();
136 }
137 
138 std::string toStringData(const SelectorData &) { return "selection(...)"; }
139 
140 std::string toStringData(const AccessData &Data) {
141   return (llvm::Twine("access(\"") + Data.BaseId + "\", " +
142           Data.Member->toString() + ")")
143       .str();
144 }
145 
146 std::string toStringData(const IfBoundData &Data) {
147   return (llvm::Twine("ifBound(\"") + Data.Id + "\", " +
148           Data.TrueStencil->toString() + ", " + Data.FalseStencil->toString() +
149           ")")
150       .str();
151 }
152 
153 std::string toStringData(const MatchConsumer<std::string> &) {
154   return "run(...)";
155 }
156 
157 std::string toStringData(const SequenceData &Data) {
158   llvm::SmallVector<std::string, 2> Parts;
159   Parts.reserve(Data.Stencils.size());
160   for (const auto &S : Data.Stencils)
161     Parts.push_back(S->toString());
162   return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str();
163 }
164 
165 // The `evalData()` overloads evaluate the given stencil data to a string, given
166 // the match result, and append it to `Result`. We define an overload for each
167 // type of stencil data.
168 
169 Error evalData(const RawTextData &Data, const MatchFinder::MatchResult &,
170                std::string *Result) {
171   Result->append(Data.Text);
172   return Error::success();
173 }
174 
175 Error evalData(const DebugPrintNodeData &Data,
176                const MatchFinder::MatchResult &Match, std::string *Result) {
177   std::string Output;
178   llvm::raw_string_ostream Os(Output);
179   auto NodeOrErr = getNode(Match.Nodes, Data.Id);
180   if (auto Err = NodeOrErr.takeError())
181     return Err;
182   NodeOrErr->print(Os, PrintingPolicy(Match.Context->getLangOpts()));
183   *Result += Os.str();
184   return Error::success();
185 }
186 
187 Error evalData(const UnaryOperationData &Data,
188                const MatchFinder::MatchResult &Match, std::string *Result) {
189   const auto *E = Match.Nodes.getNodeAs<Expr>(Data.Id);
190   if (E == nullptr)
191     return llvm::make_error<StringError>(
192         errc::invalid_argument, "Id not bound or not Expr: " + Data.Id);
193   llvm::Optional<std::string> Source;
194   switch (Data.Op) {
195   case UnaryNodeOperator::Parens:
196     Source = tooling::buildParens(*E, *Match.Context);
197     break;
198   case UnaryNodeOperator::Deref:
199     Source = tooling::buildDereference(*E, *Match.Context);
200     break;
201   case UnaryNodeOperator::MaybeDeref:
202     if (!E->getType()->isAnyPointerType()) {
203       *Result += tooling::getText(*E, *Match.Context);
204       return Error::success();
205     }
206     Source = tooling::buildDereference(*E, *Match.Context);
207     break;
208   case UnaryNodeOperator::AddressOf:
209     Source = tooling::buildAddressOf(*E, *Match.Context);
210     break;
211   case UnaryNodeOperator::MaybeAddressOf:
212     if (E->getType()->isAnyPointerType()) {
213       *Result += tooling::getText(*E, *Match.Context);
214       return Error::success();
215     }
216     Source = tooling::buildAddressOf(*E, *Match.Context);
217     break;
218   }
219   if (!Source)
220     return llvm::make_error<StringError>(
221         errc::invalid_argument,
222         "Could not construct expression source from ID: " + Data.Id);
223   *Result += *Source;
224   return Error::success();
225 }
226 
227 Error evalData(const SelectorData &Data, const MatchFinder::MatchResult &Match,
228                std::string *Result) {
229   auto Range = Data.Selector(Match);
230   if (!Range)
231     return Range.takeError();
232   if (auto Err = tooling::validateEditRange(*Range, *Match.SourceManager))
233     return Err;
234   *Result += tooling::getText(*Range, *Match.Context);
235   return Error::success();
236 }
237 
238 Error evalData(const AccessData &Data, const MatchFinder::MatchResult &Match,
239                std::string *Result) {
240   const auto *E = Match.Nodes.getNodeAs<Expr>(Data.BaseId);
241   if (E == nullptr)
242     return llvm::make_error<StringError>(errc::invalid_argument,
243                                          "Id not bound: " + Data.BaseId);
244   if (!E->isImplicitCXXThis()) {
245     if (llvm::Optional<std::string> S =
246             E->getType()->isAnyPointerType()
247                 ? tooling::buildArrow(*E, *Match.Context)
248                 : tooling::buildDot(*E, *Match.Context))
249       *Result += *S;
250     else
251       return llvm::make_error<StringError>(
252           errc::invalid_argument,
253           "Could not construct object text from ID: " + Data.BaseId);
254   }
255   return Data.Member->eval(Match, Result);
256 }
257 
258 Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match,
259                std::string *Result) {
260   auto &M = Match.Nodes.getMap();
261   return (M.find(Data.Id) != M.end() ? Data.TrueStencil : Data.FalseStencil)
262       ->eval(Match, Result);
263 }
264 
265 Error evalData(const MatchConsumer<std::string> &Fn,
266                const MatchFinder::MatchResult &Match, std::string *Result) {
267   Expected<std::string> Value = Fn(Match);
268   if (!Value)
269     return Value.takeError();
270   *Result += *Value;
271   return Error::success();
272 }
273 
274 Error evalData(const SequenceData &Data, const MatchFinder::MatchResult &Match,
275                std::string *Result) {
276   for (const auto &S : Data.Stencils)
277     if (auto Err = S->eval(Match, Result))
278       return Err;
279   return Error::success();
280 }
281 
282 template <typename T> class StencilImpl : public StencilInterface {
283   T Data;
284 
285 public:
286   template <typename... Ps>
287   explicit StencilImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {}
288 
289   Error eval(const MatchFinder::MatchResult &Match,
290              std::string *Result) const override {
291     return evalData(Data, Match, Result);
292   }
293 
294   std::string toString() const override { return toStringData(Data); }
295 };
296 } // namespace
297 
298 Stencil transformer::detail::makeStencil(StringRef Text) { return text(Text); }
299 
300 Stencil transformer::detail::makeStencil(RangeSelector Selector) {
301   return selection(std::move(Selector));
302 }
303 
304 Stencil transformer::text(StringRef Text) {
305   return std::make_shared<StencilImpl<RawTextData>>(std::string(Text));
306 }
307 
308 Stencil transformer::selection(RangeSelector Selector) {
309   return std::make_shared<StencilImpl<SelectorData>>(std::move(Selector));
310 }
311 
312 Stencil transformer::dPrint(StringRef Id) {
313   return std::make_shared<StencilImpl<DebugPrintNodeData>>(std::string(Id));
314 }
315 
316 Stencil transformer::expression(llvm::StringRef Id) {
317   return std::make_shared<StencilImpl<UnaryOperationData>>(
318       UnaryNodeOperator::Parens, std::string(Id));
319 }
320 
321 Stencil transformer::deref(llvm::StringRef ExprId) {
322   return std::make_shared<StencilImpl<UnaryOperationData>>(
323       UnaryNodeOperator::Deref, std::string(ExprId));
324 }
325 
326 Stencil transformer::maybeDeref(llvm::StringRef ExprId) {
327   return std::make_shared<StencilImpl<UnaryOperationData>>(
328       UnaryNodeOperator::MaybeDeref, std::string(ExprId));
329 }
330 
331 Stencil transformer::addressOf(llvm::StringRef ExprId) {
332   return std::make_shared<StencilImpl<UnaryOperationData>>(
333       UnaryNodeOperator::AddressOf, std::string(ExprId));
334 }
335 
336 Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) {
337   return std::make_shared<StencilImpl<UnaryOperationData>>(
338       UnaryNodeOperator::MaybeAddressOf, std::string(ExprId));
339 }
340 
341 Stencil transformer::access(StringRef BaseId, Stencil Member) {
342   return std::make_shared<StencilImpl<AccessData>>(BaseId, std::move(Member));
343 }
344 
345 Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil,
346                              Stencil FalseStencil) {
347   return std::make_shared<StencilImpl<IfBoundData>>(Id, std::move(TrueStencil),
348                                                     std::move(FalseStencil));
349 }
350 
351 Stencil transformer::run(MatchConsumer<std::string> Fn) {
352   return std::make_shared<StencilImpl<MatchConsumer<std::string>>>(
353       std::move(Fn));
354 }
355 
356 Stencil transformer::catVector(std::vector<Stencil> Parts) {
357   // Only one argument, so don't wrap in sequence.
358   if (Parts.size() == 1)
359     return std::move(Parts[0]);
360   return std::make_shared<StencilImpl<SequenceData>>(std::move(Parts));
361 }
362