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/Basic/SourceLocation.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 "llvm/Support/Error.h" |
22 | #include <memory> |
23 | #include <string> |
24 | |
25 | using namespace clang; |
26 | using namespace transformer; |
27 | |
28 | using ast_matchers::BoundNodes; |
29 | using ast_matchers::MatchFinder; |
30 | using llvm::errc; |
31 | using llvm::Error; |
32 | using llvm::Expected; |
33 | using llvm::StringError; |
34 | |
35 | static llvm::Expected<DynTypedNode> getNode(const BoundNodes &Nodes, |
36 | StringRef Id) { |
37 | auto &NodesMap = Nodes.getMap(); |
38 | auto It = NodesMap.find(x: Id); |
39 | if (It == NodesMap.end()) |
40 | return llvm::make_error<llvm::StringError>(Args: llvm::errc::invalid_argument, |
41 | Args: "Id not bound: "+ Id); |
42 | return It->second; |
43 | } |
44 | |
45 | static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match, |
46 | std::string *Result) { |
47 | std::string Output; |
48 | llvm::raw_string_ostream Os(Output); |
49 | auto NodeOrErr = getNode(Match.Nodes, Id); |
50 | if (auto Err = NodeOrErr.takeError()) |
51 | return Err; |
52 | const PrintingPolicy PP(Match.Context->getLangOpts()); |
53 | if (const auto *ND = NodeOrErr->get<NamedDecl>()) { |
54 | // For NamedDecls, we can do a better job than printing the whole thing. |
55 | ND->getNameForDiagnostic(Os, PP, false); |
56 | } else { |
57 | NodeOrErr->print(Os, PP); |
58 | } |
59 | *Result += Output; |
60 | return Error::success(); |
61 | } |
62 | |
63 | namespace { |
64 | // An arbitrary fragment of code within a stencil. |
65 | class RawTextStencil : public StencilInterface { |
66 | std::string Text; |
67 | |
68 | public: |
69 | explicit RawTextStencil(std::string T) : Text(std::move(T)) {} |
70 | |
71 | std::string toString() const override { |
72 | std::string Result; |
73 | llvm::raw_string_ostream OS(Result); |
74 | OS << "\""; |
75 | OS.write_escaped(Str: Text); |
76 | OS << "\""; |
77 | return Result; |
78 | } |
79 | |
80 | Error eval(const MatchFinder::MatchResult &Match, |
81 | std::string *Result) const override { |
82 | Result->append(str: Text); |
83 | return Error::success(); |
84 | } |
85 | }; |
86 | |
87 | // A debugging operation to dump the AST for a particular (bound) AST node. |
88 | class DebugPrintNodeStencil : public StencilInterface { |
89 | std::string Id; |
90 | |
91 | public: |
92 | explicit DebugPrintNodeStencil(std::string S) : Id(std::move(S)) {} |
93 | |
94 | std::string toString() const override { |
95 | return (llvm::Twine("dPrint(\"") + Id + "\")").str(); |
96 | } |
97 | |
98 | Error eval(const MatchFinder::MatchResult &Match, |
99 | std::string *Result) const override { |
100 | return printNode(Id, Match, Result); |
101 | } |
102 | }; |
103 | |
104 | // Operators that take a single node Id as an argument. |
105 | enum class UnaryNodeOperator { |
106 | Parens, |
107 | Deref, |
108 | MaybeDeref, |
109 | AddressOf, |
110 | MaybeAddressOf, |
111 | Describe, |
112 | }; |
113 | |
114 | // Generic container for stencil operations with a (single) node-id argument. |
115 | class UnaryOperationStencil : public StencilInterface { |
116 | UnaryNodeOperator Op; |
117 | std::string Id; |
118 | |
119 | public: |
120 | UnaryOperationStencil(UnaryNodeOperator Op, std::string Id) |
121 | : Op(Op), Id(std::move(Id)) {} |
122 | |
123 | std::string toString() const override { |
124 | StringRef OpName; |
125 | switch (Op) { |
126 | case UnaryNodeOperator::Parens: |
127 | OpName = "expression"; |
128 | break; |
129 | case UnaryNodeOperator::Deref: |
130 | OpName = "deref"; |
131 | break; |
132 | case UnaryNodeOperator::MaybeDeref: |
133 | OpName = "maybeDeref"; |
134 | break; |
135 | case UnaryNodeOperator::AddressOf: |
136 | OpName = "addressOf"; |
137 | break; |
138 | case UnaryNodeOperator::MaybeAddressOf: |
139 | OpName = "maybeAddressOf"; |
140 | break; |
141 | case UnaryNodeOperator::Describe: |
142 | OpName = "describe"; |
143 | break; |
144 | } |
145 | return (OpName + "(\""+ Id + "\")").str(); |
146 | } |
147 | |
148 | Error eval(const MatchFinder::MatchResult &Match, |
149 | std::string *Result) const override { |
150 | // The `Describe` operation can be applied to any node, not just |
151 | // expressions, so it is handled here, separately. |
152 | if (Op == UnaryNodeOperator::Describe) |
153 | return printNode(Id, Match, Result); |
154 | |
155 | const auto *E = Match.Nodes.getNodeAs<Expr>(ID: Id); |
156 | if (E == nullptr) |
157 | return llvm::make_error<StringError>(Args: errc::invalid_argument, |
158 | Args: "Id not bound or not Expr: "+ Id); |
159 | std::optional<std::string> Source; |
160 | switch (Op) { |
161 | case UnaryNodeOperator::Parens: |
162 | Source = tooling::buildParens(E: *E, Context: *Match.Context); |
163 | break; |
164 | case UnaryNodeOperator::Deref: |
165 | Source = tooling::buildDereference(E: *E, Context: *Match.Context); |
166 | break; |
167 | case UnaryNodeOperator::MaybeDeref: |
168 | if (E->getType()->isAnyPointerType() || |
169 | tooling::isKnownPointerLikeType(Ty: E->getType(), Context&: *Match.Context)) { |
170 | // Strip off any operator->. This can only occur inside an actual arrow |
171 | // member access, so we treat it as equivalent to an actual object |
172 | // expression. |
173 | if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(Val: E)) { |
174 | if (OpCall->getOperator() == clang::OO_Arrow && |
175 | OpCall->getNumArgs() == 1) { |
176 | E = OpCall->getArg(0); |
177 | } |
178 | } |
179 | Source = tooling::buildDereference(E: *E, Context: *Match.Context); |
180 | break; |
181 | } |
182 | *Result += tooling::getText(Node: *E, Context: *Match.Context); |
183 | return Error::success(); |
184 | case UnaryNodeOperator::AddressOf: |
185 | Source = tooling::buildAddressOf(E: *E, Context: *Match.Context); |
186 | break; |
187 | case UnaryNodeOperator::MaybeAddressOf: |
188 | if (E->getType()->isAnyPointerType() || |
189 | tooling::isKnownPointerLikeType(Ty: E->getType(), Context&: *Match.Context)) { |
190 | // Strip off any operator->. This can only occur inside an actual arrow |
191 | // member access, so we treat it as equivalent to an actual object |
192 | // expression. |
193 | if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(Val: E)) { |
194 | if (OpCall->getOperator() == clang::OO_Arrow && |
195 | OpCall->getNumArgs() == 1) { |
196 | E = OpCall->getArg(0); |
197 | } |
198 | } |
199 | *Result += tooling::getText(Node: *E, Context: *Match.Context); |
200 | return Error::success(); |
201 | } |
202 | Source = tooling::buildAddressOf(E: *E, Context: *Match.Context); |
203 | break; |
204 | case UnaryNodeOperator::Describe: |
205 | llvm_unreachable("This case is handled at the start of the function"); |
206 | } |
207 | if (!Source) |
208 | return llvm::make_error<StringError>( |
209 | Args: errc::invalid_argument, |
210 | Args: "Could not construct expression source from ID: "+ Id); |
211 | *Result += *Source; |
212 | return Error::success(); |
213 | } |
214 | }; |
215 | |
216 | // The fragment of code corresponding to the selected range. |
217 | class SelectorStencil : public StencilInterface { |
218 | RangeSelector Selector; |
219 | |
220 | public: |
221 | explicit SelectorStencil(RangeSelector S) : Selector(std::move(S)) {} |
222 | |
223 | std::string toString() const override { return "selection(...)"; } |
224 | |
225 | Error eval(const MatchFinder::MatchResult &Match, |
226 | std::string *Result) const override { |
227 | auto RawRange = Selector(Match); |
228 | if (!RawRange) |
229 | return RawRange.takeError(); |
230 | CharSourceRange Range = Lexer::makeFileCharRange( |
231 | Range: *RawRange, SM: *Match.SourceManager, LangOpts: Match.Context->getLangOpts()); |
232 | if (Range.isInvalid()) { |
233 | // Validate the original range to attempt to get a meaningful error |
234 | // message. If it's valid, then something else is the cause and we just |
235 | // return the generic failure message. |
236 | if (auto Err = tooling::validateRange(Range: *RawRange, SM: *Match.SourceManager, |
237 | /*AllowSystemHeaders=*/true)) |
238 | return handleErrors(E: std::move(Err), Hs: [](std::unique_ptr<StringError> E) { |
239 | assert(E->convertToErrorCode() == |
240 | llvm::make_error_code(errc::invalid_argument) && |
241 | "Validation errors must carry the invalid_argument code"); |
242 | return llvm::createStringError( |
243 | EC: errc::invalid_argument, |
244 | S: "selected range could not be resolved to a valid source range; "+ |
245 | E->getMessage()); |
246 | }); |
247 | return llvm::createStringError( |
248 | EC: errc::invalid_argument, |
249 | S: "selected range could not be resolved to a valid source range"); |
250 | } |
251 | // Validate `Range`, because `makeFileCharRange` accepts some ranges that |
252 | // `validateRange` rejects. |
253 | if (auto Err = tooling::validateRange(Range, SM: *Match.SourceManager, |
254 | /*AllowSystemHeaders=*/true)) |
255 | return joinErrors( |
256 | E1: llvm::createStringError(EC: errc::invalid_argument, |
257 | S: "selected range is not valid for editing"), |
258 | E2: std::move(Err)); |
259 | *Result += tooling::getText(Range, Context: *Match.Context); |
260 | return Error::success(); |
261 | } |
262 | }; |
263 | |
264 | // A stencil operation to build a member access `e.m` or `e->m`, as appropriate. |
265 | class AccessStencil : public StencilInterface { |
266 | std::string BaseId; |
267 | Stencil Member; |
268 | |
269 | public: |
270 | AccessStencil(StringRef BaseId, Stencil Member) |
271 | : BaseId(std::string(BaseId)), Member(std::move(Member)) {} |
272 | |
273 | std::string toString() const override { |
274 | return (llvm::Twine("access(\"") + BaseId + "\", "+ Member->toString() + |
275 | ")") |
276 | .str(); |
277 | } |
278 | |
279 | Error eval(const MatchFinder::MatchResult &Match, |
280 | std::string *Result) const override { |
281 | const auto *E = Match.Nodes.getNodeAs<Expr>(ID: BaseId); |
282 | if (E == nullptr) |
283 | return llvm::make_error<StringError>(Args: errc::invalid_argument, |
284 | Args: "Id not bound: "+ BaseId); |
285 | std::optional<std::string> S = tooling::buildAccess(E: *E, Context&: *Match.Context); |
286 | if (!S) |
287 | return llvm::make_error<StringError>( |
288 | Args: errc::invalid_argument, |
289 | Args: "Could not construct object text from ID: "+ BaseId); |
290 | *Result += *S; |
291 | return Member->eval(Match, Result); |
292 | } |
293 | }; |
294 | |
295 | class IfBoundStencil : public StencilInterface { |
296 | std::string Id; |
297 | Stencil TrueStencil; |
298 | Stencil FalseStencil; |
299 | |
300 | public: |
301 | IfBoundStencil(StringRef Id, Stencil TrueStencil, Stencil FalseStencil) |
302 | : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)), |
303 | FalseStencil(std::move(FalseStencil)) {} |
304 | |
305 | std::string toString() const override { |
306 | return (llvm::Twine("ifBound(\"") + Id + "\", "+ TrueStencil->toString() + |
307 | ", "+ FalseStencil->toString() + ")") |
308 | .str(); |
309 | } |
310 | |
311 | Error eval(const MatchFinder::MatchResult &Match, |
312 | std::string *Result) const override { |
313 | auto &M = Match.Nodes.getMap(); |
314 | return (M.find(x: Id) != M.end() ? TrueStencil : FalseStencil) |
315 | ->eval(Match, Result); |
316 | } |
317 | }; |
318 | |
319 | class SelectBoundStencil : public clang::transformer::StencilInterface { |
320 | static bool containsNoNullStencils( |
321 | const std::vector<std::pair<std::string, Stencil>> &Cases) { |
322 | for (const auto &S : Cases) |
323 | if (S.second == nullptr) |
324 | return false; |
325 | return true; |
326 | } |
327 | |
328 | public: |
329 | SelectBoundStencil(std::vector<std::pair<std::string, Stencil>> Cases, |
330 | Stencil Default) |
331 | : CaseStencils(std::move(Cases)), DefaultStencil(std::move(Default)) { |
332 | assert(containsNoNullStencils(CaseStencils) && |
333 | "cases of selectBound may not be null"); |
334 | } |
335 | ~SelectBoundStencil() override {} |
336 | |
337 | llvm::Error eval(const MatchFinder::MatchResult &match, |
338 | std::string *result) const override { |
339 | const BoundNodes::IDToNodeMap &NodeMap = match.Nodes.getMap(); |
340 | for (const auto &S : CaseStencils) { |
341 | if (NodeMap.count(x: S.first) > 0) { |
342 | return S.second->eval(Match: match, Result: result); |
343 | } |
344 | } |
345 | |
346 | if (DefaultStencil != nullptr) { |
347 | return DefaultStencil->eval(Match: match, Result: result); |
348 | } |
349 | |
350 | llvm::SmallVector<llvm::StringRef, 2> CaseIDs; |
351 | CaseIDs.reserve(N: CaseStencils.size()); |
352 | for (const auto &S : CaseStencils) |
353 | CaseIDs.emplace_back(Args: S.first); |
354 | |
355 | return llvm::createStringError( |
356 | EC: errc::result_out_of_range, |
357 | S: llvm::Twine("selectBound failed: no cases bound and no default: {") + |
358 | llvm::join(R&: CaseIDs, Separator: ", ") + "}"); |
359 | } |
360 | |
361 | std::string toString() const override { |
362 | std::string Buffer; |
363 | llvm::raw_string_ostream Stream(Buffer); |
364 | Stream << "selectBound({"; |
365 | bool First = true; |
366 | for (const auto &S : CaseStencils) { |
367 | if (First) |
368 | First = false; |
369 | else |
370 | Stream << "}, "; |
371 | Stream << "{\""<< S.first << "\", "<< S.second->toString(); |
372 | } |
373 | Stream << "}}"; |
374 | if (DefaultStencil != nullptr) { |
375 | Stream << ", "<< DefaultStencil->toString(); |
376 | } |
377 | Stream << ")"; |
378 | return Buffer; |
379 | } |
380 | |
381 | private: |
382 | std::vector<std::pair<std::string, Stencil>> CaseStencils; |
383 | Stencil DefaultStencil; |
384 | }; |
385 | |
386 | class SequenceStencil : public StencilInterface { |
387 | std::vector<Stencil> Stencils; |
388 | |
389 | public: |
390 | SequenceStencil(std::vector<Stencil> Stencils) |
391 | : Stencils(std::move(Stencils)) {} |
392 | |
393 | std::string toString() const override { |
394 | llvm::SmallVector<std::string, 2> Parts; |
395 | Parts.reserve(N: Stencils.size()); |
396 | for (const auto &S : Stencils) |
397 | Parts.push_back(Elt: S->toString()); |
398 | return (llvm::Twine("seq(") + llvm::join(R&: Parts, Separator: ", ") + ")").str(); |
399 | } |
400 | |
401 | Error eval(const MatchFinder::MatchResult &Match, |
402 | std::string *Result) const override { |
403 | for (const auto &S : Stencils) |
404 | if (auto Err = S->eval(Match, Result)) |
405 | return Err; |
406 | return Error::success(); |
407 | } |
408 | }; |
409 | |
410 | class RunStencil : public StencilInterface { |
411 | MatchConsumer<std::string> Consumer; |
412 | |
413 | public: |
414 | explicit RunStencil(MatchConsumer<std::string> C) : Consumer(std::move(C)) {} |
415 | |
416 | std::string toString() const override { return "run(...)"; } |
417 | |
418 | Error eval(const MatchFinder::MatchResult &Match, |
419 | std::string *Result) const override { |
420 | |
421 | Expected<std::string> Value = Consumer(Match); |
422 | if (!Value) |
423 | return Value.takeError(); |
424 | *Result += *Value; |
425 | return Error::success(); |
426 | } |
427 | }; |
428 | } // namespace |
429 | |
430 | Stencil transformer::detail::makeStencil(StringRef Text) { |
431 | return std::make_shared<RawTextStencil>(args: std::string(Text)); |
432 | } |
433 | |
434 | Stencil transformer::detail::makeStencil(RangeSelector Selector) { |
435 | return std::make_shared<SelectorStencil>(args: std::move(Selector)); |
436 | } |
437 | |
438 | Stencil transformer::dPrint(StringRef Id) { |
439 | return std::make_shared<DebugPrintNodeStencil>(args: std::string(Id)); |
440 | } |
441 | |
442 | Stencil transformer::expression(llvm::StringRef Id) { |
443 | return std::make_shared<UnaryOperationStencil>(args: UnaryNodeOperator::Parens, |
444 | args: std::string(Id)); |
445 | } |
446 | |
447 | Stencil transformer::deref(llvm::StringRef ExprId) { |
448 | return std::make_shared<UnaryOperationStencil>(args: UnaryNodeOperator::Deref, |
449 | args: std::string(ExprId)); |
450 | } |
451 | |
452 | Stencil transformer::maybeDeref(llvm::StringRef ExprId) { |
453 | return std::make_shared<UnaryOperationStencil>(args: UnaryNodeOperator::MaybeDeref, |
454 | args: std::string(ExprId)); |
455 | } |
456 | |
457 | Stencil transformer::addressOf(llvm::StringRef ExprId) { |
458 | return std::make_shared<UnaryOperationStencil>(args: UnaryNodeOperator::AddressOf, |
459 | args: std::string(ExprId)); |
460 | } |
461 | |
462 | Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) { |
463 | return std::make_shared<UnaryOperationStencil>( |
464 | args: UnaryNodeOperator::MaybeAddressOf, args: std::string(ExprId)); |
465 | } |
466 | |
467 | Stencil transformer::describe(StringRef Id) { |
468 | return std::make_shared<UnaryOperationStencil>(args: UnaryNodeOperator::Describe, |
469 | args: std::string(Id)); |
470 | } |
471 | |
472 | Stencil transformer::access(StringRef BaseId, Stencil Member) { |
473 | return std::make_shared<AccessStencil>(args&: BaseId, args: std::move(Member)); |
474 | } |
475 | |
476 | Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil, |
477 | Stencil FalseStencil) { |
478 | return std::make_shared<IfBoundStencil>(args&: Id, args: std::move(TrueStencil), |
479 | args: std::move(FalseStencil)); |
480 | } |
481 | |
482 | Stencil transformer::selectBound( |
483 | std::vector<std::pair<std::string, Stencil>> CaseStencils, |
484 | Stencil DefaultStencil) { |
485 | return std::make_shared<SelectBoundStencil>(args: std::move(CaseStencils), |
486 | args: std::move(DefaultStencil)); |
487 | } |
488 | |
489 | Stencil transformer::run(MatchConsumer<std::string> Fn) { |
490 | return std::make_shared<RunStencil>(args: std::move(Fn)); |
491 | } |
492 | |
493 | Stencil transformer::catVector(std::vector<Stencil> Parts) { |
494 | // Only one argument, so don't wrap in sequence. |
495 | if (Parts.size() == 1) |
496 | return std::move(Parts[0]); |
497 | return std::make_shared<SequenceStencil>(args: std::move(Parts)); |
498 | } |
499 |
Definitions
- getNode
- printNode
- RawTextStencil
- RawTextStencil
- toString
- eval
- DebugPrintNodeStencil
- DebugPrintNodeStencil
- toString
- eval
- UnaryNodeOperator
- UnaryOperationStencil
- UnaryOperationStencil
- toString
- eval
- SelectorStencil
- SelectorStencil
- toString
- eval
- AccessStencil
- AccessStencil
- toString
- eval
- IfBoundStencil
- IfBoundStencil
- toString
- eval
- SelectBoundStencil
- containsNoNullStencils
- SelectBoundStencil
- ~SelectBoundStencil
- eval
- toString
- SequenceStencil
- SequenceStencil
- toString
- eval
- RunStencil
- RunStencil
- toString
- eval
- makeStencil
- makeStencil
- dPrint
- expression
- deref
- maybeDeref
- addressOf
- maybeAddressOf
- describe
- access
- ifBound
- selectBound
- run
Improve your Profiling and Debugging skills
Find out more