150
|
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 }
|