clang 20.0.0git
Stencil.cpp
Go to the documentation of this file.
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
12#include "clang/AST/Expr.h"
15#include "clang/Lex/Lexer.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 <atomic>
23#include <memory>
24#include <string>
25
26using namespace clang;
27using namespace transformer;
28
31using llvm::errc;
32using llvm::Error;
33using llvm::Expected;
34using llvm::StringError;
35
37 StringRef Id) {
38 auto &NodesMap = Nodes.getMap();
39 auto It = NodesMap.find(Id);
40 if (It == NodesMap.end())
41 return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
42 "Id not bound: " + Id);
43 return It->second;
44}
45
46static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match,
47 std::string *Result) {
48 std::string Output;
49 llvm::raw_string_ostream Os(Output);
50 auto NodeOrErr = getNode(Match.Nodes, Id);
51 if (auto Err = NodeOrErr.takeError())
52 return Err;
53 const PrintingPolicy PP(Match.Context->getLangOpts());
54 if (const auto *ND = NodeOrErr->get<NamedDecl>()) {
55 // For NamedDecls, we can do a better job than printing the whole thing.
56 ND->getNameForDiagnostic(Os, PP, false);
57 } else {
58 NodeOrErr->print(Os, PP);
59 }
60 *Result += Output;
61 return Error::success();
62}
63
64namespace {
65// An arbitrary fragment of code within a stencil.
66class RawTextStencil : public StencilInterface {
67 std::string Text;
68
69public:
70 explicit RawTextStencil(std::string T) : Text(std::move(T)) {}
71
72 std::string toString() const override {
73 std::string Result;
74 llvm::raw_string_ostream OS(Result);
75 OS << "\"";
76 OS.write_escaped(Text);
77 OS << "\"";
78 return Result;
79 }
80
82 std::string *Result) const override {
83 Result->append(Text);
84 return Error::success();
85 }
86};
87
88// A debugging operation to dump the AST for a particular (bound) AST node.
89class DebugPrintNodeStencil : public StencilInterface {
90 std::string Id;
91
92public:
93 explicit DebugPrintNodeStencil(std::string S) : Id(std::move(S)) {}
94
95 std::string toString() const override {
96 return (llvm::Twine("dPrint(\"") + Id + "\")").str();
97 }
98
100 std::string *Result) const override {
101 return printNode(Id, Match, Result);
102 }
103};
104
105// Operators that take a single node Id as an argument.
106enum class UnaryNodeOperator {
107 Parens,
108 Deref,
109 MaybeDeref,
110 AddressOf,
111 MaybeAddressOf,
112 Describe,
113};
114
115// Generic container for stencil operations with a (single) node-id argument.
116class UnaryOperationStencil : public StencilInterface {
117 UnaryNodeOperator Op;
118 std::string Id;
119
120public:
121 UnaryOperationStencil(UnaryNodeOperator Op, std::string Id)
122 : Op(Op), Id(std::move(Id)) {}
123
124 std::string toString() const override {
125 StringRef OpName;
126 switch (Op) {
127 case UnaryNodeOperator::Parens:
128 OpName = "expression";
129 break;
130 case UnaryNodeOperator::Deref:
131 OpName = "deref";
132 break;
133 case UnaryNodeOperator::MaybeDeref:
134 OpName = "maybeDeref";
135 break;
136 case UnaryNodeOperator::AddressOf:
137 OpName = "addressOf";
138 break;
139 case UnaryNodeOperator::MaybeAddressOf:
140 OpName = "maybeAddressOf";
141 break;
142 case UnaryNodeOperator::Describe:
143 OpName = "describe";
144 break;
145 }
146 return (OpName + "(\"" + Id + "\")").str();
147 }
148
150 std::string *Result) const override {
151 // The `Describe` operation can be applied to any node, not just
152 // expressions, so it is handled here, separately.
153 if (Op == UnaryNodeOperator::Describe)
154 return printNode(Id, Match, Result);
155
156 const auto *E = Match.Nodes.getNodeAs<Expr>(Id);
157 if (E == nullptr)
158 return llvm::make_error<StringError>(errc::invalid_argument,
159 "Id not bound or not Expr: " + Id);
160 std::optional<std::string> Source;
161 switch (Op) {
162 case UnaryNodeOperator::Parens:
163 Source = tooling::buildParens(*E, *Match.Context);
164 break;
165 case UnaryNodeOperator::Deref:
166 Source = tooling::buildDereference(*E, *Match.Context);
167 break;
168 case UnaryNodeOperator::MaybeDeref:
169 if (E->getType()->isAnyPointerType() ||
171 // Strip off any operator->. This can only occur inside an actual arrow
172 // member access, so we treat it as equivalent to an actual object
173 // expression.
174 if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {
175 if (OpCall->getOperator() == clang::OO_Arrow &&
176 OpCall->getNumArgs() == 1) {
177 E = OpCall->getArg(0);
178 }
179 }
180 Source = tooling::buildDereference(*E, *Match.Context);
181 break;
182 }
183 *Result += tooling::getText(*E, *Match.Context);
184 return Error::success();
185 case UnaryNodeOperator::AddressOf:
186 Source = tooling::buildAddressOf(*E, *Match.Context);
187 break;
188 case UnaryNodeOperator::MaybeAddressOf:
189 if (E->getType()->isAnyPointerType() ||
191 // Strip off any operator->. This can only occur inside an actual arrow
192 // member access, so we treat it as equivalent to an actual object
193 // expression.
194 if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) {
195 if (OpCall->getOperator() == clang::OO_Arrow &&
196 OpCall->getNumArgs() == 1) {
197 E = OpCall->getArg(0);
198 }
199 }
200 *Result += tooling::getText(*E, *Match.Context);
201 return Error::success();
202 }
203 Source = tooling::buildAddressOf(*E, *Match.Context);
204 break;
205 case UnaryNodeOperator::Describe:
206 llvm_unreachable("This case is handled at the start of the function");
207 }
208 if (!Source)
209 return llvm::make_error<StringError>(
210 errc::invalid_argument,
211 "Could not construct expression source from ID: " + Id);
212 *Result += *Source;
213 return Error::success();
214 }
215};
216
217// The fragment of code corresponding to the selected range.
218class SelectorStencil : public StencilInterface {
220
221public:
222 explicit SelectorStencil(RangeSelector S) : Selector(std::move(S)) {}
223
224 std::string toString() const override { return "selection(...)"; }
225
227 std::string *Result) const override {
228 auto RawRange = Selector(Match);
229 if (!RawRange)
230 return RawRange.takeError();
232 *RawRange, *Match.SourceManager, Match.Context->getLangOpts());
233 if (Range.isInvalid()) {
234 // Validate the original range to attempt to get a meaningful error
235 // message. If it's valid, then something else is the cause and we just
236 // return the generic failure message.
237 if (auto Err = tooling::validateRange(*RawRange, *Match.SourceManager,
238 /*AllowSystemHeaders=*/true))
239 return handleErrors(std::move(Err), [](std::unique_ptr<StringError> E) {
240 assert(E->convertToErrorCode() ==
241 llvm::make_error_code(errc::invalid_argument) &&
242 "Validation errors must carry the invalid_argument code");
243 return llvm::createStringError(
244 errc::invalid_argument,
245 "selected range could not be resolved to a valid source range; " +
246 E->getMessage());
247 });
248 return llvm::createStringError(
249 errc::invalid_argument,
250 "selected range could not be resolved to a valid source range");
251 }
252 // Validate `Range`, because `makeFileCharRange` accepts some ranges that
253 // `validateRange` rejects.
254 if (auto Err = tooling::validateRange(Range, *Match.SourceManager,
255 /*AllowSystemHeaders=*/true))
256 return joinErrors(
257 llvm::createStringError(errc::invalid_argument,
258 "selected range is not valid for editing"),
259 std::move(Err));
260 *Result += tooling::getText(Range, *Match.Context);
261 return Error::success();
262 }
263};
264
265// A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
266class AccessStencil : public StencilInterface {
267 std::string BaseId;
269
270public:
271 AccessStencil(StringRef BaseId, Stencil Member)
272 : BaseId(std::string(BaseId)), Member(std::move(Member)) {}
273
274 std::string toString() const override {
275 return (llvm::Twine("access(\"") + BaseId + "\", " + Member->toString() +
276 ")")
277 .str();
278 }
279
281 std::string *Result) const override {
282 const auto *E = Match.Nodes.getNodeAs<Expr>(BaseId);
283 if (E == nullptr)
284 return llvm::make_error<StringError>(errc::invalid_argument,
285 "Id not bound: " + BaseId);
286 std::optional<std::string> S = tooling::buildAccess(*E, *Match.Context);
287 if (!S)
288 return llvm::make_error<StringError>(
289 errc::invalid_argument,
290 "Could not construct object text from ID: " + BaseId);
291 *Result += *S;
292 return Member->eval(Match, Result);
293 }
294};
295
296class IfBoundStencil : public StencilInterface {
297 std::string Id;
298 Stencil TrueStencil;
299 Stencil FalseStencil;
300
301public:
302 IfBoundStencil(StringRef Id, Stencil TrueStencil, Stencil FalseStencil)
303 : Id(std::string(Id)), TrueStencil(std::move(TrueStencil)),
304 FalseStencil(std::move(FalseStencil)) {}
305
306 std::string toString() const override {
307 return (llvm::Twine("ifBound(\"") + Id + "\", " + TrueStencil->toString() +
308 ", " + FalseStencil->toString() + ")")
309 .str();
310 }
311
313 std::string *Result) const override {
314 auto &M = Match.Nodes.getMap();
315 return (M.find(Id) != M.end() ? TrueStencil : FalseStencil)
316 ->eval(Match, Result);
317 }
318};
319
320class SelectBoundStencil : public clang::transformer::StencilInterface {
321 static bool containsNoNullStencils(
322 const std::vector<std::pair<std::string, Stencil>> &Cases) {
323 for (const auto &S : Cases)
324 if (S.second == nullptr)
325 return false;
326 return true;
327 }
328
329public:
330 SelectBoundStencil(std::vector<std::pair<std::string, Stencil>> Cases,
332 : CaseStencils(std::move(Cases)), DefaultStencil(std::move(Default)) {
333 assert(containsNoNullStencils(CaseStencils) &&
334 "cases of selectBound may not be null");
335 }
336 ~SelectBoundStencil() override {}
337
338 llvm::Error eval(const MatchFinder::MatchResult &match,
339 std::string *result) const override {
340 const BoundNodes::IDToNodeMap &NodeMap = match.Nodes.getMap();
341 for (const auto &S : CaseStencils) {
342 if (NodeMap.count(S.first) > 0) {
343 return S.second->eval(match, result);
344 }
345 }
346
347 if (DefaultStencil != nullptr) {
348 return DefaultStencil->eval(match, result);
349 }
350
352 CaseIDs.reserve(CaseStencils.size());
353 for (const auto &S : CaseStencils)
354 CaseIDs.emplace_back(S.first);
355
356 return llvm::createStringError(
357 errc::result_out_of_range,
358 llvm::Twine("selectBound failed: no cases bound and no default: {") +
359 llvm::join(CaseIDs, ", ") + "}");
360 }
361
362 std::string toString() const override {
363 std::string Buffer;
364 llvm::raw_string_ostream Stream(Buffer);
365 Stream << "selectBound({";
366 bool First = true;
367 for (const auto &S : CaseStencils) {
368 if (First)
369 First = false;
370 else
371 Stream << "}, ";
372 Stream << "{\"" << S.first << "\", " << S.second->toString();
373 }
374 Stream << "}}";
375 if (DefaultStencil != nullptr) {
376 Stream << ", " << DefaultStencil->toString();
377 }
378 Stream << ")";
379 return Buffer;
380 }
381
382private:
383 std::vector<std::pair<std::string, Stencil>> CaseStencils;
384 Stencil DefaultStencil;
385};
386
387class SequenceStencil : public StencilInterface {
388 std::vector<Stencil> Stencils;
389
390public:
391 SequenceStencil(std::vector<Stencil> Stencils)
392 : Stencils(std::move(Stencils)) {}
393
394 std::string toString() const override {
396 Parts.reserve(Stencils.size());
397 for (const auto &S : Stencils)
398 Parts.push_back(S->toString());
399 return (llvm::Twine("seq(") + llvm::join(Parts, ", ") + ")").str();
400 }
401
403 std::string *Result) const override {
404 for (const auto &S : Stencils)
405 if (auto Err = S->eval(Match, Result))
406 return Err;
407 return Error::success();
408 }
409};
410
411class RunStencil : public StencilInterface {
413
414public:
415 explicit RunStencil(MatchConsumer<std::string> C) : Consumer(std::move(C)) {}
416
417 std::string toString() const override { return "run(...)"; }
418
420 std::string *Result) const override {
421
422 Expected<std::string> Value = Consumer(Match);
423 if (!Value)
424 return Value.takeError();
425 *Result += *Value;
426 return Error::success();
427 }
428};
429} // namespace
430
432 return std::make_shared<RawTextStencil>(std::string(Text));
433}
434
436 return std::make_shared<SelectorStencil>(std::move(Selector));
437}
438
439Stencil transformer::dPrint(StringRef Id) {
440 return std::make_shared<DebugPrintNodeStencil>(std::string(Id));
441}
442
444 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Parens,
445 std::string(Id));
446}
447
448Stencil transformer::deref(llvm::StringRef ExprId) {
449 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Deref,
450 std::string(ExprId));
451}
452
453Stencil transformer::maybeDeref(llvm::StringRef ExprId) {
454 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::MaybeDeref,
455 std::string(ExprId));
456}
457
458Stencil transformer::addressOf(llvm::StringRef ExprId) {
459 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::AddressOf,
460 std::string(ExprId));
461}
462
463Stencil transformer::maybeAddressOf(llvm::StringRef ExprId) {
464 return std::make_shared<UnaryOperationStencil>(
465 UnaryNodeOperator::MaybeAddressOf, std::string(ExprId));
466}
467
469 return std::make_shared<UnaryOperationStencil>(UnaryNodeOperator::Describe,
470 std::string(Id));
471}
472
473Stencil transformer::access(StringRef BaseId, Stencil Member) {
474 return std::make_shared<AccessStencil>(BaseId, std::move(Member));
475}
476
477Stencil transformer::ifBound(StringRef Id, Stencil TrueStencil,
478 Stencil FalseStencil) {
479 return std::make_shared<IfBoundStencil>(Id, std::move(TrueStencil),
480 std::move(FalseStencil));
481}
482
484 std::vector<std::pair<std::string, Stencil>> CaseStencils,
485 Stencil DefaultStencil) {
486 return std::make_shared<SelectBoundStencil>(std::move(CaseStencils),
487 std::move(DefaultStencil));
488}
489
491 return std::make_shared<RunStencil>(std::move(Fn));
492}
493
494Stencil transformer::catVector(std::vector<Stencil> Parts) {
495 // Only one argument, so don't wrap in sequence.
496 if (Parts.size() == 1)
497 return std::move(Parts[0]);
498 return std::make_shared<SequenceStencil>(std::move(Parts));
499}
Defines the clang::ASTContext interface.
BoundNodesTreeBuilder Nodes
Expr * E
StringRef Text
Definition: Format.cpp:3033
uint32_t Id
Definition: SemaARM.cpp:1134
SourceRange Range
Definition: SemaObjC.cpp:758
This file collects facilities for generating source code strings.
Defines the clang::SourceLocation class and associated facilities.
static Error printNode(StringRef Id, const MatchFinder::MatchResult &Match, std::string *Result)
Definition: Stencil.cpp:46
static llvm::Expected< DynTypedNode > getNode(const BoundNodes &Nodes, StringRef Id)
Definition: Stencil.cpp:36
This file defines the Stencil abstraction: a code-generating object, parameterized by named reference...
const LangOptions & getLangOpts() const
Definition: ASTContext.h:834
Represents a character-granular source range.
This represents one expression.
Definition: Expr.h:110
QualType getType() const
Definition: Expr.h:142
static CharSourceRange makeFileCharRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
Accepts a range and returns a character range with file locations.
Definition: Lexer.cpp:954
This represents a decl that may have a name.
Definition: Decl.h:253
Smart pointer class that efficiently represents Objective-C method names.
bool isInvalid() const
bool isAnyPointerType() const
Definition: Type.h:8194
Maps string IDs to AST nodes matched by parts of a matcher.
Definition: ASTMatchers.h:109
internal::BoundNodesMap::IDToNodeMap IDToNodeMap
Type of mapping from binding identifiers to bound nodes.
Definition: ASTMatchers.h:123
const IDToNodeMap & getMap() const
Retrieve mapping from binding identifiers to bound nodes.
Definition: ASTMatchers.h:126
const T * getNodeAs(StringRef ID) const
Returns the AST node bound to ID.
Definition: ASTMatchers.h:116
A class to allow finding matches over the Clang AST.
A failable computation over nodes bound by AST matchers, with (limited) reflection via the toString m...
Definition: MatchConsumer.h:64
virtual std::string toString() const =0
Constructs a string representation of the computation, for informational purposes.
virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match, T *Result) const =0
Evaluates the computation and (potentially) updates the accumulator Result.
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
std::optional< std::string > buildDereference(const Expr &E, const ASTContext &Context)
Builds idiomatic source for the dereferencing of E: prefix with * but simplify when it already begins...
std::optional< std::string > buildAddressOf(const Expr &E, const ASTContext &Context)
Builds idiomatic source for taking the address of E: prefix with & but simplify when it already begin...
bool isKnownPointerLikeType(QualType Ty, ASTContext &Context)
std::optional< std::string > buildParens(const Expr &E, const ASTContext &Context)
Builds source for an expression, adding parens if needed for unambiguous parsing.
llvm::Error validateRange(const CharSourceRange &Range, const SourceManager &SM, bool AllowSystemHeaders)
Determines whether Range is one that can be read from.
Definition: SourceCode.cpp:53
std::optional< std::string > buildAccess(const Expr &E, ASTContext &Context, PLTClass Classification=PLTClass::Pointer)
Adds an appropriate access operator (.
StringRef getText(CharSourceRange Range, const ASTContext &Context)
Returns the source-code text in the specified range.
Definition: SourceCode.cpp:31
Stencil makeStencil(llvm::StringRef Text)
Convenience function to construct a Stencil.
Stencil addressOf(llvm::StringRef ExprId)
Constructs an expression that idiomatically takes the address of the expression bound to ExprId.
Definition: Stencil.cpp:458
Stencil maybeAddressOf(llvm::StringRef ExprId)
If ExprId is not a pointer type, constructs an expression that idiomatically takes the address of the...
Definition: Stencil.cpp:463
Stencil maybeDeref(llvm::StringRef ExprId)
If ExprId is of pointer type, constructs an idiomatic dereferencing of the expression bound to ExprId...
Definition: Stencil.cpp:453
Stencil describe(llvm::StringRef Id)
Produces a human-readable rendering of the node bound to Id, suitable for diagnostics and debugging.
Stencil access(llvm::StringRef BaseId, Stencil Member)
Constructs a MemberExpr that accesses the named member (Member) of the object bound to BaseId.
MatchConsumer< T > ifBound(std::string ID, MatchConsumer< T > TrueC, MatchConsumer< T > FalseC)
Chooses between the two consumers, based on whether ID is bound in the match.
Definition: MatchConsumer.h:47
Stencil deref(llvm::StringRef ExprId)
Constructs an idiomatic dereferencing of the expression bound to ExprId.
Definition: Stencil.cpp:448
MatchConsumer< CharSourceRange > RangeSelector
Definition: RangeSelector.h:27
std::shared_ptr< StencilInterface > Stencil
A sequence of code fragments, references to parameters and code-generation operations that together c...
Definition: Stencil.h:46
Stencil run(MatchConsumer< std::string > C)
Wraps a MatchConsumer in a Stencil, so that it can be used in a Stencil.
Definition: Stencil.cpp:490
Stencil expression(llvm::StringRef Id)
Generates the source of the expression bound to Id, wrapping it in parentheses if it may parse differ...
Definition: Stencil.cpp:443
Stencil dPrint(llvm::StringRef Id)
For debug use only; semantics are not guaranteed.
std::function< Expected< T >(const ast_matchers::MatchFinder::MatchResult &)> MatchConsumer
A failable computation over nodes bound by AST matchers.
Definition: MatchConsumer.h:35
Stencil selectBound(std::vector< std::pair< std::string, Stencil > > CaseStencils, Stencil DefaultStencil=nullptr)
Chooses between multiple stencils, based on the presence of bound nodes.
Definition: Stencil.cpp:483
Stencil catVector(std::vector< Stencil > Parts)
Constructs the string representing the concatenation of the given Parts.
Definition: Stencil.cpp:494
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
const FunctionProtoType * T
@ Parens
New-expression has a C++98 paren-delimited initializer.
Describes how types, statements, expressions, and declarations should be printed.
Definition: PrettyPrinter.h:57
Contains all information for a given match.
clang::SourceManager *const SourceManager
const BoundNodes Nodes
Contains the nodes bound on the current match.
clang::ASTContext *const Context
Utilities for interpreting the matched AST structures.