xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/Transformer/Stencil.cpp (revision 85868e8a1daeaae7a0e48effb2ea2310ae3b02c6)
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 transformer;
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 = tooling::buildParens(*E, *Match.Context);
175     break;
176   case UnaryNodeOperator::Deref:
177     Source = tooling::buildDereference(*E, *Match.Context);
178     break;
179   case UnaryNodeOperator::Address:
180     Source = tooling::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 += tooling::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 =
208             E->getType()->isAnyPointerType()
209                 ? tooling::buildArrow(*E, *Match.Context)
210                 : tooling::buildDot(*E, *Match.Context))
211       *Result += *S;
212     else
213       return llvm::make_error<StringError>(
214           errc::invalid_argument,
215           "Could not construct object text from ID: " + Data.BaseId);
216   }
217   return Data.Member.eval(Match, Result);
218 }
219 
220 Error evalData(const IfBoundData &Data, const MatchFinder::MatchResult &Match,
221                std::string *Result) {
222   auto &M = Match.Nodes.getMap();
223   return (M.find(Data.Id) != M.end() ? Data.TruePart : Data.FalsePart)
224       .eval(Match, Result);
225 }
226 
227 Error evalData(const MatchConsumer<std::string> &Fn,
228                const MatchFinder::MatchResult &Match, std::string *Result) {
229   Expected<std::string> Value = Fn(Match);
230   if (!Value)
231     return Value.takeError();
232   *Result += *Value;
233   return Error::success();
234 }
235 
236 template <typename T>
237 class StencilPartImpl : public StencilPartInterface {
238   T Data;
239 
240 public:
241   template <typename... Ps>
242   explicit StencilPartImpl(Ps &&... Args) : Data(std::forward<Ps>(Args)...) {}
243 
244   Error eval(const MatchFinder::MatchResult &Match,
245              std::string *Result) const override {
246     return evalData(Data, Match, Result);
247   }
248 
249   std::string toString() const override { return toStringData(Data); }
250 };
251 } // namespace
252 
253 StencilPart Stencil::wrap(StringRef Text) {
254   return transformer::text(Text);
255 }
256 
257 StencilPart Stencil::wrap(RangeSelector Selector) {
258   return transformer::selection(std::move(Selector));
259 }
260 
261 void Stencil::append(Stencil OtherStencil) {
262   for (auto &Part : OtherStencil.Parts)
263     Parts.push_back(std::move(Part));
264 }
265 
266 llvm::Expected<std::string>
267 Stencil::eval(const MatchFinder::MatchResult &Match) const {
268   std::string Result;
269   for (const auto &Part : Parts)
270     if (auto Err = Part.eval(Match, &Result))
271       return std::move(Err);
272   return Result;
273 }
274 
275 StencilPart transformer::text(StringRef Text) {
276   return StencilPart(std::make_shared<StencilPartImpl<RawTextData>>(Text));
277 }
278 
279 StencilPart transformer::selection(RangeSelector Selector) {
280   return StencilPart(
281       std::make_shared<StencilPartImpl<SelectorData>>(std::move(Selector)));
282 }
283 
284 StencilPart transformer::dPrint(StringRef Id) {
285   return StencilPart(std::make_shared<StencilPartImpl<DebugPrintNodeData>>(Id));
286 }
287 
288 StencilPart transformer::expression(llvm::StringRef Id) {
289   return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
290       UnaryNodeOperator::Parens, Id));
291 }
292 
293 StencilPart transformer::deref(llvm::StringRef ExprId) {
294   return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
295       UnaryNodeOperator::Deref, ExprId));
296 }
297 
298 StencilPart transformer::addressOf(llvm::StringRef ExprId) {
299   return StencilPart(std::make_shared<StencilPartImpl<UnaryOperationData>>(
300       UnaryNodeOperator::Address, ExprId));
301 }
302 
303 StencilPart transformer::access(StringRef BaseId, StencilPart Member) {
304   return StencilPart(
305       std::make_shared<StencilPartImpl<AccessData>>(BaseId, std::move(Member)));
306 }
307 
308 StencilPart transformer::ifBound(StringRef Id, StencilPart TruePart,
309                              StencilPart FalsePart) {
310   return StencilPart(std::make_shared<StencilPartImpl<IfBoundData>>(
311       Id, std::move(TruePart), std::move(FalsePart)));
312 }
313 
314 StencilPart transformer::run(MatchConsumer<std::string> Fn) {
315   return StencilPart(
316       std::make_shared<StencilPartImpl<MatchConsumer<std::string>>>(
317           std::move(Fn)));
318 }
319