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 NodeOrErr
->print(Os
, PrintingPolicy(Match
.Context
->getLangOpts()));
55 return Error::success();
59 // An arbitrary fragment of code within a stencil.
60 class RawTextStencil
: public StencilInterface
{
64 explicit RawTextStencil(std::string T
) : Text(std::move(T
)) {}
66 std::string
toString() const override
{
68 llvm::raw_string_ostream
OS(Result
);
70 OS
.write_escaped(Text
);
76 Error
eval(const MatchFinder::MatchResult
&Match
,
77 std::string
*Result
) const override
{
79 return Error::success();
83 // A debugging operation to dump the AST for a particular (bound) AST node.
84 class DebugPrintNodeStencil
: public StencilInterface
{
88 explicit DebugPrintNodeStencil(std::string S
) : Id(std::move(S
)) {}
90 std::string
toString() const override
{
91 return (llvm::Twine("dPrint(\"") + Id
+ "\")").str();
94 Error
eval(const MatchFinder::MatchResult
&Match
,
95 std::string
*Result
) const override
{
96 return printNode(Id
, Match
, Result
);
100 // Operators that take a single node Id as an argument.
101 enum class UnaryNodeOperator
{
110 // Generic container for stencil operations with a (single) node-id argument.
111 class UnaryOperationStencil
: public StencilInterface
{
112 UnaryNodeOperator Op
;
116 UnaryOperationStencil(UnaryNodeOperator Op
, std::string Id
)
117 : Op(Op
), Id(std::move(Id
)) {}
119 std::string
toString() const override
{
122 case UnaryNodeOperator::Parens
:
123 OpName
= "expression";
125 case UnaryNodeOperator::Deref
:
128 case UnaryNodeOperator::MaybeDeref
:
129 OpName
= "maybeDeref";
131 case UnaryNodeOperator::AddressOf
:
132 OpName
= "addressOf";
134 case UnaryNodeOperator::MaybeAddressOf
:
135 OpName
= "maybeAddressOf";
137 case UnaryNodeOperator::Describe
:
141 return (OpName
+ "(\"" + Id
+ "\")").str();
144 Error
eval(const MatchFinder::MatchResult
&Match
,
145 std::string
*Result
) const override
{
146 // The `Describe` operation can be applied to any node, not just
147 // expressions, so it is handled here, separately.
148 if (Op
== UnaryNodeOperator::Describe
)
149 return printNode(Id
, Match
, Result
);
151 const auto *E
= Match
.Nodes
.getNodeAs
<Expr
>(Id
);
153 return llvm::make_error
<StringError
>(errc::invalid_argument
,
154 "Id not bound or not Expr: " + Id
);
155 std::optional
<std::string
> Source
;
157 case UnaryNodeOperator::Parens
:
158 Source
= tooling::buildParens(*E
, *Match
.Context
);
160 case UnaryNodeOperator::Deref
:
161 Source
= tooling::buildDereference(*E
, *Match
.Context
);
163 case UnaryNodeOperator::MaybeDeref
:
164 if (E
->getType()->isAnyPointerType() ||
165 tooling::isKnownPointerLikeType(E
->getType(), *Match
.Context
)) {
166 // Strip off any operator->. This can only occur inside an actual arrow
167 // member access, so we treat it as equivalent to an actual object
169 if (const auto *OpCall
= dyn_cast
<clang::CXXOperatorCallExpr
>(E
)) {
170 if (OpCall
->getOperator() == clang::OO_Arrow
&&
171 OpCall
->getNumArgs() == 1) {
172 E
= OpCall
->getArg(0);
175 Source
= tooling::buildDereference(*E
, *Match
.Context
);
178 *Result
+= tooling::getText(*E
, *Match
.Context
);
179 return Error::success();
180 case UnaryNodeOperator::AddressOf
:
181 Source
= tooling::buildAddressOf(*E
, *Match
.Context
);
183 case UnaryNodeOperator::MaybeAddressOf
:
184 if (E
->getType()->isAnyPointerType() ||
185 tooling::isKnownPointerLikeType(E
->getType(), *Match
.Context
)) {
186 // Strip off any operator->. This can only occur inside an actual arrow
187 // member access, so we treat it as equivalent to an actual object
189 if (const auto *OpCall
= dyn_cast
<clang::CXXOperatorCallExpr
>(E
)) {
190 if (OpCall
->getOperator() == clang::OO_Arrow
&&
191 OpCall
->getNumArgs() == 1) {
192 E
= OpCall
->getArg(0);
195 *Result
+= tooling::getText(*E
, *Match
.Context
);
196 return Error::success();
198 Source
= tooling::buildAddressOf(*E
, *Match
.Context
);
200 case UnaryNodeOperator::Describe
:
201 llvm_unreachable("This case is handled at the start of the function");
204 return llvm::make_error
<StringError
>(
205 errc::invalid_argument
,
206 "Could not construct expression source from ID: " + Id
);
208 return Error::success();
212 // The fragment of code corresponding to the selected range.
213 class SelectorStencil
: public StencilInterface
{
214 RangeSelector Selector
;
217 explicit SelectorStencil(RangeSelector S
) : Selector(std::move(S
)) {}
219 std::string
toString() const override
{ return "selection(...)"; }
221 Error
eval(const MatchFinder::MatchResult
&Match
,
222 std::string
*Result
) const override
{
223 auto RawRange
= Selector(Match
);
225 return RawRange
.takeError();
226 CharSourceRange Range
= Lexer::makeFileCharRange(
227 *RawRange
, *Match
.SourceManager
, Match
.Context
->getLangOpts());
228 if (Range
.isInvalid()) {
229 // Validate the original range to attempt to get a meaningful error
230 // message. If it's valid, then something else is the cause and we just
231 // return the generic failure message.
232 if (auto Err
= tooling::validateRange(*RawRange
, *Match
.SourceManager
,
233 /*AllowSystemHeaders=*/true))
234 return handleErrors(std::move(Err
), [](std::unique_ptr
<StringError
> E
) {
235 assert(E
->convertToErrorCode() ==
236 llvm::make_error_code(errc::invalid_argument
) &&
237 "Validation errors must carry the invalid_argument code");
238 return llvm::createStringError(
239 errc::invalid_argument
,
240 "selected range could not be resolved to a valid source range; " +
243 return llvm::createStringError(
244 errc::invalid_argument
,
245 "selected range could not be resolved to a valid source range");
247 // Validate `Range`, because `makeFileCharRange` accepts some ranges that
248 // `validateRange` rejects.
249 if (auto Err
= tooling::validateRange(Range
, *Match
.SourceManager
,
250 /*AllowSystemHeaders=*/true))
252 llvm::createStringError(errc::invalid_argument
,
253 "selected range is not valid for editing"),
255 *Result
+= tooling::getText(Range
, *Match
.Context
);
256 return Error::success();
260 // A stencil operation to build a member access `e.m` or `e->m`, as appropriate.
261 class AccessStencil
: public StencilInterface
{
266 AccessStencil(StringRef BaseId
, Stencil Member
)
267 : BaseId(std::string(BaseId
)), Member(std::move(Member
)) {}
269 std::string
toString() const override
{
270 return (llvm::Twine("access(\"") + BaseId
+ "\", " + Member
->toString() +
275 Error
eval(const MatchFinder::MatchResult
&Match
,
276 std::string
*Result
) const override
{
277 const auto *E
= Match
.Nodes
.getNodeAs
<Expr
>(BaseId
);
279 return llvm::make_error
<StringError
>(errc::invalid_argument
,
280 "Id not bound: " + BaseId
);
281 std::optional
<std::string
> S
= tooling::buildAccess(*E
, *Match
.Context
);
283 return llvm::make_error
<StringError
>(
284 errc::invalid_argument
,
285 "Could not construct object text from ID: " + BaseId
);
287 return Member
->eval(Match
, Result
);
291 class IfBoundStencil
: public StencilInterface
{
294 Stencil FalseStencil
;
297 IfBoundStencil(StringRef Id
, Stencil TrueStencil
, Stencil FalseStencil
)
298 : Id(std::string(Id
)), TrueStencil(std::move(TrueStencil
)),
299 FalseStencil(std::move(FalseStencil
)) {}
301 std::string
toString() const override
{
302 return (llvm::Twine("ifBound(\"") + Id
+ "\", " + TrueStencil
->toString() +
303 ", " + FalseStencil
->toString() + ")")
307 Error
eval(const MatchFinder::MatchResult
&Match
,
308 std::string
*Result
) const override
{
309 auto &M
= Match
.Nodes
.getMap();
310 return (M
.find(Id
) != M
.end() ? TrueStencil
: FalseStencil
)
311 ->eval(Match
, Result
);
315 class SelectBoundStencil
: public clang::transformer::StencilInterface
{
316 static bool containsNoNullStencils(
317 const std::vector
<std::pair
<std::string
, Stencil
>> &Cases
) {
318 for (const auto &S
: Cases
)
319 if (S
.second
== nullptr)
325 SelectBoundStencil(std::vector
<std::pair
<std::string
, Stencil
>> Cases
,
327 : CaseStencils(std::move(Cases
)), DefaultStencil(std::move(Default
)) {
328 assert(containsNoNullStencils(CaseStencils
) &&
329 "cases of selectBound may not be null");
331 ~SelectBoundStencil() override
{}
333 llvm::Error
eval(const MatchFinder::MatchResult
&match
,
334 std::string
*result
) const override
{
335 const BoundNodes::IDToNodeMap
&NodeMap
= match
.Nodes
.getMap();
336 for (const auto &S
: CaseStencils
) {
337 if (NodeMap
.count(S
.first
) > 0) {
338 return S
.second
->eval(match
, result
);
342 if (DefaultStencil
!= nullptr) {
343 return DefaultStencil
->eval(match
, result
);
346 llvm::SmallVector
<llvm::StringRef
, 2> CaseIDs
;
347 CaseIDs
.reserve(CaseStencils
.size());
348 for (const auto &S
: CaseStencils
)
349 CaseIDs
.emplace_back(S
.first
);
351 return llvm::createStringError(
352 errc::result_out_of_range
,
353 llvm::Twine("selectBound failed: no cases bound and no default: {") +
354 llvm::join(CaseIDs
, ", ") + "}");
357 std::string
toString() const override
{
359 llvm::raw_string_ostream
Stream(Buffer
);
360 Stream
<< "selectBound({";
362 for (const auto &S
: CaseStencils
) {
367 Stream
<< "{\"" << S
.first
<< "\", " << S
.second
->toString();
370 if (DefaultStencil
!= nullptr) {
371 Stream
<< ", " << DefaultStencil
->toString();
378 std::vector
<std::pair
<std::string
, Stencil
>> CaseStencils
;
379 Stencil DefaultStencil
;
382 class SequenceStencil
: public StencilInterface
{
383 std::vector
<Stencil
> Stencils
;
386 SequenceStencil(std::vector
<Stencil
> Stencils
)
387 : Stencils(std::move(Stencils
)) {}
389 std::string
toString() const override
{
390 llvm::SmallVector
<std::string
, 2> Parts
;
391 Parts
.reserve(Stencils
.size());
392 for (const auto &S
: Stencils
)
393 Parts
.push_back(S
->toString());
394 return (llvm::Twine("seq(") + llvm::join(Parts
, ", ") + ")").str();
397 Error
eval(const MatchFinder::MatchResult
&Match
,
398 std::string
*Result
) const override
{
399 for (const auto &S
: Stencils
)
400 if (auto Err
= S
->eval(Match
, Result
))
402 return Error::success();
406 class RunStencil
: public StencilInterface
{
407 MatchConsumer
<std::string
> Consumer
;
410 explicit RunStencil(MatchConsumer
<std::string
> C
) : Consumer(std::move(C
)) {}
412 std::string
toString() const override
{ return "run(...)"; }
414 Error
eval(const MatchFinder::MatchResult
&Match
,
415 std::string
*Result
) const override
{
417 Expected
<std::string
> Value
= Consumer(Match
);
419 return Value
.takeError();
421 return Error::success();
426 Stencil
transformer::detail::makeStencil(StringRef Text
) {
427 return std::make_shared
<RawTextStencil
>(std::string(Text
));
430 Stencil
transformer::detail::makeStencil(RangeSelector Selector
) {
431 return std::make_shared
<SelectorStencil
>(std::move(Selector
));
434 Stencil
transformer::dPrint(StringRef Id
) {
435 return std::make_shared
<DebugPrintNodeStencil
>(std::string(Id
));
438 Stencil
transformer::expression(llvm::StringRef Id
) {
439 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::Parens
,
443 Stencil
transformer::deref(llvm::StringRef ExprId
) {
444 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::Deref
,
445 std::string(ExprId
));
448 Stencil
transformer::maybeDeref(llvm::StringRef ExprId
) {
449 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::MaybeDeref
,
450 std::string(ExprId
));
453 Stencil
transformer::addressOf(llvm::StringRef ExprId
) {
454 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::AddressOf
,
455 std::string(ExprId
));
458 Stencil
transformer::maybeAddressOf(llvm::StringRef ExprId
) {
459 return std::make_shared
<UnaryOperationStencil
>(
460 UnaryNodeOperator::MaybeAddressOf
, std::string(ExprId
));
463 Stencil
transformer::describe(StringRef Id
) {
464 return std::make_shared
<UnaryOperationStencil
>(UnaryNodeOperator::Describe
,
468 Stencil
transformer::access(StringRef BaseId
, Stencil Member
) {
469 return std::make_shared
<AccessStencil
>(BaseId
, std::move(Member
));
472 Stencil
transformer::ifBound(StringRef Id
, Stencil TrueStencil
,
473 Stencil FalseStencil
) {
474 return std::make_shared
<IfBoundStencil
>(Id
, std::move(TrueStencil
),
475 std::move(FalseStencil
));
478 Stencil
transformer::selectBound(
479 std::vector
<std::pair
<std::string
, Stencil
>> CaseStencils
,
480 Stencil DefaultStencil
) {
481 return std::make_shared
<SelectBoundStencil
>(std::move(CaseStencils
),
482 std::move(DefaultStencil
));
485 Stencil
transformer::run(MatchConsumer
<std::string
> Fn
) {
486 return std::make_shared
<RunStencil
>(std::move(Fn
));
489 Stencil
transformer::catVector(std::vector
<Stencil
> Parts
) {
490 // Only one argument, so don't wrap in sequence.
491 if (Parts
.size() == 1)
492 return std::move(Parts
[0]);
493 return std::make_shared
<SequenceStencil
>(std::move(Parts
));