1 //===--- SourceCodeBuilder.cpp ----------------------------------*- 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/SourceCodeBuilders.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Expr.h"
12 #include "clang/AST/ExprCXX.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/ASTMatchers/ASTMatchers.h"
15 #include "clang/Tooling/Transformer/SourceCode.h"
16 #include "llvm/ADT/Twine.h"
19 using namespace clang
;
20 using namespace tooling
;
22 const Expr
*tooling::reallyIgnoreImplicit(const Expr
&E
) {
23 const Expr
*Expr
= E
.IgnoreImplicit();
24 if (const auto *CE
= dyn_cast
<CXXConstructExpr
>(Expr
)) {
25 if (CE
->getNumArgs() > 0 &&
26 CE
->getArg(0)->getSourceRange() == Expr
->getSourceRange())
27 return CE
->getArg(0)->IgnoreImplicit();
32 bool tooling::mayEverNeedParens(const Expr
&E
) {
33 const Expr
*Expr
= reallyIgnoreImplicit(E
);
34 // We always want parens around unary, binary, and ternary operators, because
35 // they are lower precedence.
36 if (isa
<UnaryOperator
>(Expr
) || isa
<BinaryOperator
>(Expr
) ||
37 isa
<AbstractConditionalOperator
>(Expr
))
40 // We need parens around calls to all overloaded operators except: function
41 // calls, subscripts, and expressions that are already part of an (implicit)
42 // call to operator->. These latter are all in the same precedence level as
43 // dot/arrow and that level is left associative, so they don't need parens
44 // when appearing on the left.
45 if (const auto *Op
= dyn_cast
<CXXOperatorCallExpr
>(Expr
))
46 return Op
->getOperator() != OO_Call
&& Op
->getOperator() != OO_Subscript
&&
47 Op
->getOperator() != OO_Arrow
;
52 bool tooling::needParensAfterUnaryOperator(const Expr
&E
) {
53 const Expr
*Expr
= reallyIgnoreImplicit(E
);
54 if (isa
<BinaryOperator
>(Expr
) || isa
<AbstractConditionalOperator
>(Expr
))
57 if (const auto *Op
= dyn_cast
<CXXOperatorCallExpr
>(Expr
))
58 return Op
->getNumArgs() == 2 && Op
->getOperator() != OO_PlusPlus
&&
59 Op
->getOperator() != OO_MinusMinus
&& Op
->getOperator() != OO_Call
&&
60 Op
->getOperator() != OO_Subscript
;
65 bool tooling::isKnownPointerLikeType(QualType Ty
, ASTContext
&Context
) {
66 using namespace ast_matchers
;
67 const auto PointerLikeTy
= type(hasUnqualifiedDesugaredType(
68 recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
69 "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr",
70 "::std::optional", "::absl::optional", "::llvm::Optional",
71 "absl::StatusOr", "::llvm::Expected"))))));
72 return match(PointerLikeTy
, Ty
, Context
).size() > 0;
75 std::optional
<std::string
> tooling::buildParens(const Expr
&E
,
76 const ASTContext
&Context
) {
77 StringRef Text
= getText(E
, Context
);
80 if (mayEverNeedParens(E
))
81 return ("(" + Text
+ ")").str();
85 std::optional
<std::string
>
86 tooling::buildDereference(const Expr
&E
, const ASTContext
&Context
) {
87 if (const auto *Op
= dyn_cast
<UnaryOperator
>(&E
))
88 if (Op
->getOpcode() == UO_AddrOf
) {
91 getText(*Op
->getSubExpr()->IgnoreParenImpCasts(), Context
);
97 StringRef Text
= getText(E
, Context
);
101 if (needParensAfterUnaryOperator(E
))
102 return ("*(" + Text
+ ")").str();
103 return ("*" + Text
).str();
106 std::optional
<std::string
> tooling::buildAddressOf(const Expr
&E
,
107 const ASTContext
&Context
) {
108 if (E
.isImplicitCXXThis())
109 return std::string("this");
110 if (const auto *Op
= dyn_cast
<UnaryOperator
>(&E
))
111 if (Op
->getOpcode() == UO_Deref
) {
112 // Strip leading '*'.
114 getText(*Op
->getSubExpr()->IgnoreParenImpCasts(), Context
);
120 StringRef Text
= getText(E
, Context
);
123 if (needParensAfterUnaryOperator(E
)) {
124 return ("&(" + Text
+ ")").str();
126 return ("&" + Text
).str();
129 // Append the appropriate access operation (syntactically) to `E`, assuming `E`
130 // is a non-pointer value.
131 static std::optional
<std::string
>
132 buildAccessForValue(const Expr
&E
, const ASTContext
&Context
) {
133 if (const auto *Op
= llvm::dyn_cast
<UnaryOperator
>(&E
))
134 if (Op
->getOpcode() == UO_Deref
) {
135 // Strip leading '*', add following '->'.
136 const Expr
*SubExpr
= Op
->getSubExpr()->IgnoreParenImpCasts();
137 StringRef DerefText
= getText(*SubExpr
, Context
);
138 if (DerefText
.empty())
140 if (needParensBeforeDotOrArrow(*SubExpr
))
141 return ("(" + DerefText
+ ")->").str();
142 return (DerefText
+ "->").str();
145 // Add following '.'.
146 StringRef Text
= getText(E
, Context
);
149 if (needParensBeforeDotOrArrow(E
)) {
150 return ("(" + Text
+ ").").str();
152 return (Text
+ ".").str();
155 // Append the appropriate access operation (syntactically) to `E`, assuming `E`
156 // is a pointer value.
157 static std::optional
<std::string
>
158 buildAccessForPointer(const Expr
&E
, const ASTContext
&Context
) {
159 if (const auto *Op
= llvm::dyn_cast
<UnaryOperator
>(&E
))
160 if (Op
->getOpcode() == UO_AddrOf
) {
161 // Strip leading '&', add following '.'.
162 const Expr
*SubExpr
= Op
->getSubExpr()->IgnoreParenImpCasts();
163 StringRef DerefText
= getText(*SubExpr
, Context
);
164 if (DerefText
.empty())
166 if (needParensBeforeDotOrArrow(*SubExpr
))
167 return ("(" + DerefText
+ ").").str();
168 return (DerefText
+ ".").str();
171 // Add following '->'.
172 StringRef Text
= getText(E
, Context
);
175 if (needParensBeforeDotOrArrow(E
))
176 return ("(" + Text
+ ")->").str();
177 return (Text
+ "->").str();
180 std::optional
<std::string
> tooling::buildDot(const Expr
&E
,
181 const ASTContext
&Context
) {
182 return buildAccessForValue(E
, Context
);
185 std::optional
<std::string
> tooling::buildArrow(const Expr
&E
,
186 const ASTContext
&Context
) {
187 return buildAccessForPointer(E
, Context
);
190 // If `E` is an overloaded-operator call of kind `K` on an object `O`, returns
191 // `O`. Otherwise, returns `nullptr`.
192 static const Expr
*maybeGetOperatorObjectArg(const Expr
&E
,
193 OverloadedOperatorKind K
) {
194 if (const auto *OpCall
= dyn_cast
<clang::CXXOperatorCallExpr
>(&E
)) {
195 if (OpCall
->getOperator() == K
&& OpCall
->getNumArgs() == 1)
196 return OpCall
->getArg(0);
201 static bool treatLikePointer(QualType Ty
, PLTClass C
, ASTContext
&Context
) {
203 case PLTClass::Value
:
205 case PLTClass::Pointer
:
206 return isKnownPointerLikeType(Ty
, Context
);
208 llvm_unreachable("Unknown PLTClass enum");
211 // FIXME: move over the other `maybe` functionality from Stencil. Should all be
213 std::optional
<std::string
> tooling::buildAccess(const Expr
&RawExpression
,
215 PLTClass Classification
) {
216 if (RawExpression
.isImplicitCXXThis())
217 // Return the empty string, because `std::nullopt` signifies some sort of
219 return std::string();
221 const Expr
*E
= RawExpression
.IgnoreImplicitAsWritten();
223 if (E
->getType()->isAnyPointerType() ||
224 treatLikePointer(E
->getType(), Classification
, Context
)) {
225 // Strip off operator-> calls. They can only occur inside an actual arrow
226 // member access, so we treat them as equivalent to an actual object
228 if (const auto *Obj
= maybeGetOperatorObjectArg(*E
, clang::OO_Arrow
))
230 return buildAccessForPointer(*E
, Context
);
233 if (const auto *Obj
= maybeGetOperatorObjectArg(*E
, clang::OO_Star
)) {
234 if (treatLikePointer(Obj
->getType(), Classification
, Context
))
235 return buildAccessForPointer(*Obj
, Context
);
238 return buildAccessForValue(*E
, Context
);