1 //===--- Stencil.cpp - Stencil implementation -------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
26 using namespace clang
;
27 using namespace transformer
;
29 using ast_matchers::BoundNodes
;
30 using ast_matchers::MatchFinder
;
34 using llvm::StringError
;
36 static llvm::Expected
<DynTypedNode
> getNode(const BoundNodes
&Nodes
,
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
);
46 static Error
printNode(StringRef Id
, const MatchFinder::MatchResult
&Match
,
47 std::string
*Result
) {
49 llvm::raw_string_ostream
Os(Output
);
50 auto NodeOrErr
= getNode(Match
.Nodes
, Id
);
51 if (auto Err
= NodeOrErr
.takeError())
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);
58 NodeOrErr
->print(Os
, PP
);
61 return Error::success();
65 // An arbitrary fragment of code within a stencil.
66 class RawTextStencil
: public StencilInterface
{
70 explicit RawTextStencil(std::string T
) : Text(std::move(T
)) {}
72 std::string
toString() const override
{
74 llvm::raw_string_ostream
OS(Result
);
76 OS
.write_escaped(Text
);
81 Error
eval(const MatchFinder::MatchResult
&Match
,
82 std::string
*Result
) const override
{
84 return Error::success();
88 // A debugging operation to dump the AST for a particular (bound) AST node.
89 class DebugPrintNodeStencil
: public StencilInterface
{
93 explicit DebugPrintNodeStencil(std::string S
) : Id(std::move(S
)) {}
95 std::string
toString() const override
{
96 return (llvm::Twine("dPrint(\"") + Id
+ "\")").str();
99 Error
eval(const MatchFinder::MatchResult
&Match
,
100 std::string
*Result
) const override
{
101 return printNode(Id
, Match
, Result
);
105 // Operators that take a single node Id as an argument.
106 enum class UnaryNodeOperator
{
115 // Generic container for stencil operations with a (single) node-id argument.
116 class UnaryOperationStencil
: public StencilInterface
{
117 UnaryNodeOperator Op
;
121 UnaryOperationStencil(UnaryNodeOperator Op
, std::string Id
)
122 : Op(Op
), Id(std::move(Id
)) {}
124 std::string
toString() const override
{
127 case UnaryNodeOperator::Parens
:
128 OpName
= "expression";
130 case UnaryNodeOperator::Deref
:
133 case UnaryNodeOperator::MaybeDeref
:
134 OpName
= "maybeDeref";
136 case UnaryNodeOperator::AddressOf
:
137 OpName
= "addressOf";
139 case UnaryNodeOperator::MaybeAddressOf
:
140 OpName
= "maybeAddressOf";
142 case UnaryNodeOperator::Describe
:
146 return (OpName
+ "(\"" + Id
+ "\")").str();
149 Error
eval(const MatchFinder::MatchResult
&Match
,
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
);
156 const auto *E
= Match
.Nodes
.getNodeAs
<Expr
>(Id
);
158 return llvm::make_error
<StringError
>(errc::invalid_argument
,
159 "Id not bound or not Expr: " + Id
);
160 std::optional
<std::string
> Source
;
162 case UnaryNodeOperator::Parens
:
163 Source
= tooling::buildParens(*E
, *Match
.Context
);
165 case UnaryNodeOperator::Deref
:
166 Source
= tooling::buildDereference(*E
, *Match
.Context
);
168 case UnaryNodeOperator::MaybeDeref
:
169 if (E
->getType()->isAnyPointerType() ||
170 tooling::isKnownPointerLikeType(E
->getType(), *Match
.Context
)) {
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
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);
180 Source
= tooling::buildDereference(*E
, *Match
.Context
);
183 *Result
+= tooling::getText(*E
, *Match
.Context
);
184 return Error::success();
185 case UnaryNodeOperator::AddressOf
:
186 Source
= tooling::buildAddressOf(*E
, *Match
.Context
);
188 case UnaryNodeOperator::MaybeAddressOf
:
189 if (E
->getType()->isAnyPointerType() ||
190 tooling::isKnownPointerLikeType(E
->getType(), *Match
.Context
)) {
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
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);
200 *Result
+= tooling::getText(*E
, *Match
.Context
);
201 return Error::success();
203 Source
= tooling::buildAddressOf(*E
, *Match
.Context
);
205 case UnaryNodeOperator::Describe
:
206 llvm_unreachable("This case is handled at the start of the function");
209 return llvm::make_error
<StringError
>(
210 errc::invalid_argument
,
211 "Could not construct expression source from ID: " + Id
);
213 return Error::success();
217 // The fragment of code corresponding to the selected range.
218 class SelectorStencil
: public StencilInterface
{
219 RangeSelector Selector
;
222 explicit SelectorStencil(RangeSelector S
) : Selector(std::move(S
)) {}
224 std::string
toString() const override
{ return "selection(...)"; }
226 Error
eval(const MatchFinder::MatchResult
&Match
,
227 std::string
*Result
) const override
{
228 auto RawRange
= Selector(Match
);
230 return RawRange
.takeError();
231 CharSourceRange Range
= Lexer::makeFileCharRange(
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; " +
248 return llvm::createStringError(
249 errc::invalid_argument
,
250 "selected range could not be resolved to a valid source range");
252 // Validate `Range`, because `makeFileCharRange` accepts some ranges that
253 // `validateRange` rejects.
254 if (auto Err
= tooling::validateRange(Range
, *Match
.SourceManager
,
255 /*AllowSystemHeaders=*/true))
257 llvm::createStringError(errc::invalid_argument
,
258 "selected range is not valid for editing"),
260 *Result
+= tooling::getText(Range
, *Match
.Context
);
261 return Error::success();
265 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
266 class AccessStencil
: public StencilInterface
{
271 AccessStencil(StringRef BaseId
, Stencil Member
)
272 : BaseId(std::string(BaseId
)), Member(std::move(Member
)) {}
274 std::string
toString() const override
{
275 return (llvm::Twine("access(\"") + BaseId
+ "\", " + Member
->toString() +
280 Error
eval(const MatchFinder::MatchResult
&Match
,
281 std::string
*Result
) const override
{
282 const auto *E
= Match
.Nodes
.getNodeAs
<Expr
>(BaseId
);
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
);
288 return llvm::make_error
<StringError
>(
289 errc::invalid_argument
,
290 "Could not construct object text from ID: " + BaseId
);
292 return Member
->eval(Match
, Result
);
296 class IfBoundStencil
: public StencilInterface
{
299 Stencil FalseStencil
;
302 IfBoundStencil(StringRef Id
, Stencil TrueStencil
, Stencil FalseStencil
)
303 : Id(std::string(Id
)), TrueStencil(std::move(TrueStencil
)),
304 FalseStencil(std::move(FalseStencil
)) {}
306 std::string
toString() const override
{
307 return (llvm::Twine("ifBound(\"") + Id
+ "\", " + TrueStencil
->toString() +
308 ", " + FalseStencil
->toString() + ")")
312 Error
eval(const MatchFinder::MatchResult
&Match
,
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
);
320 class 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)
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");
336 ~SelectBoundStencil() override
{}
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
);
347 if (DefaultStencil
!= nullptr) {
348 return DefaultStencil
->eval(match
, result
);
351 llvm::SmallVector
<llvm::StringRef
, 2> CaseIDs
;
352 CaseIDs
.reserve(CaseStencils
.size());
353 for (const auto &S
: CaseStencils
)
354 CaseIDs
.emplace_back(S
.first
);
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
, ", ") + "}");
362 std::string
toString() const override
{
364 llvm::raw_string_ostream
Stream(Buffer
);
365 Stream
<< "selectBound({";
367 for (const auto &S
: CaseStencils
) {
372 Stream
<< "{\"" << S
.first
<< "\", " << S
.second
->toString();
375 if (DefaultStencil
!= nullptr) {
376 Stream
<< ", " << DefaultStencil
->toString();
383 std::vector
<std::pair
<std::string
, Stencil
>> CaseStencils
;
384 Stencil DefaultStencil
;
387 class SequenceStencil
: public StencilInterface
{
388 std::vector
<Stencil
> Stencils
;
391 SequenceStencil(std::vector
<Stencil
> Stencils
)
392 : Stencils(std::move(Stencils
)) {}
394 std::string
toString() const override
{
395 llvm::SmallVector
<std::string
, 2> Parts
;
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();
402 Error
eval(const MatchFinder::MatchResult
&Match
,
403 std::string
*Result
) const override
{
404 for (const auto &S
: Stencils
)
405 if (auto Err
= S
->eval(Match
, Result
))
407 return Error::success();
411 class RunStencil
: public StencilInterface
{
412 MatchConsumer
<std::string
> Consumer
;
415 explicit RunStencil(MatchConsumer
<std::string
> C
) : Consumer(std::move(C
)) {}
417 std::string
toString() const override
{ return "run(...)"; }
419 Error
eval(const MatchFinder::MatchResult
&Match
,
420 std::string
*Result
) const override
{
422 Expected
<std::string
> Value
= Consumer(Match
);
424 return Value
.takeError();
426 return Error::success();
431 Stencil
transformer::detail::makeStencil(StringRef Text
) {
432 return std::make_shared
<RawTextStencil
>(std::string(Text
));
435 Stencil
transformer::detail::makeStencil(RangeSelector Selector
) {
436 return std::make_shared
<SelectorStencil
>(std::move(Selector
));
439 Stencil
transformer::dPrint(StringRef Id
) {
440 return std::make_shared
<DebugPrintNodeStencil
>(std::string(Id
));
443 Stencil
transformer::expression(llvm::StringRef Id
) {
444 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::Parens
,
448 Stencil
transformer::deref(llvm::StringRef ExprId
) {
449 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::Deref
,
450 std::string(ExprId
));
453 Stencil
transformer::maybeDeref(llvm::StringRef ExprId
) {
454 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::MaybeDeref
,
455 std::string(ExprId
));
458 Stencil
transformer::addressOf(llvm::StringRef ExprId
) {
459 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::AddressOf
,
460 std::string(ExprId
));
463 Stencil
transformer::maybeAddressOf(llvm::StringRef ExprId
) {
464 return std::make_shared
<UnaryOperationStencil
>(
465 UnaryNodeOperator::MaybeAddressOf
, std::string(ExprId
));
468 Stencil
transformer::describe(StringRef Id
) {
469 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::Describe
,
473 Stencil
transformer::access(StringRef BaseId
, Stencil Member
) {
474 return std::make_shared
<AccessStencil
>(BaseId
, std::move(Member
));
477 Stencil
transformer::ifBound(StringRef Id
, Stencil TrueStencil
,
478 Stencil FalseStencil
) {
479 return std::make_shared
<IfBoundStencil
>(Id
, std::move(TrueStencil
),
480 std::move(FalseStencil
));
483 Stencil
transformer::selectBound(
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
));
490 Stencil
transformer::run(MatchConsumer
<std::string
> Fn
) {
491 return std::make_shared
<RunStencil
>(std::move(Fn
));
494 Stencil
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
));