1//===--- SourceCodeBuilder.cpp ----------------------------------*- 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/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"
17#include <string>
18
19using namespace clang;
20using namespace tooling;
21
22const Expr *tooling::reallyIgnoreImplicit(const Expr &E) {
23 const Expr *Expr = E.IgnoreImplicit();
24 if (const auto *CE = dyn_cast<CXXConstructExpr>(Val: Expr)) {
25 if (CE->getNumArgs() > 0 &&
26 CE->getArg(Arg: 0)->getSourceRange() == Expr->getSourceRange())
27 return CE->getArg(Arg: 0)->IgnoreImplicit();
28 }
29 return Expr;
30}
31
32bool 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>(Val: Expr) || isa<BinaryOperator>(Val: Expr) ||
37 isa<AbstractConditionalOperator>(Val: Expr))
38 return true;
39
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>(Val: Expr))
46 return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
47 Op->getOperator() != OO_Arrow;
48
49 return false;
50}
51
52bool tooling::needParensAfterUnaryOperator(const Expr &E) {
53 const Expr *Expr = reallyIgnoreImplicit(E);
54 if (isa<BinaryOperator>(Val: Expr) || isa<AbstractConditionalOperator>(Val: Expr))
55 return true;
56
57 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Val: Expr))
58 return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
59 Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
60 Op->getOperator() != OO_Subscript;
61
62 return false;
63}
64
65bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) {
66 using namespace ast_matchers;
67 const auto PointerLikeTy = type(hasUnqualifiedDesugaredType(
68 InnerMatcher: recordType(hasDeclaration(InnerMatcher: 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(Matcher: PointerLikeTy, Node: Ty, Context).size() > 0;
73}
74
75std::optional<std::string> tooling::buildParens(const Expr &E,
76 const ASTContext &Context) {
77 StringRef Text = getText(Node: E, Context);
78 if (Text.empty())
79 return std::nullopt;
80 if (mayEverNeedParens(E))
81 return ("(" + Text + ")").str();
82 return Text.str();
83}
84
85std::optional<std::string>
86tooling::buildDereference(const Expr &E, const ASTContext &Context) {
87 if (const auto *Op = dyn_cast<UnaryOperator>(Val: &E))
88 if (Op->getOpcode() == UO_AddrOf) {
89 // Strip leading '&'.
90 StringRef Text =
91 getText(Node: *Op->getSubExpr()->IgnoreParenImpCasts(), Context);
92 if (Text.empty())
93 return std::nullopt;
94 return Text.str();
95 }
96
97 StringRef Text = getText(Node: E, Context);
98 if (Text.empty())
99 return std::nullopt;
100 // Add leading '*'.
101 if (needParensAfterUnaryOperator(E))
102 return ("*(" + Text + ")").str();
103 return ("*" + Text).str();
104}
105
106std::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>(Val: &E))
111 if (Op->getOpcode() == UO_Deref) {
112 // Strip leading '*'.
113 StringRef Text =
114 getText(Node: *Op->getSubExpr()->IgnoreParenImpCasts(), Context);
115 if (Text.empty())
116 return std::nullopt;
117 return Text.str();
118 }
119 // Add leading '&'.
120 StringRef Text = getText(Node: E, Context);
121 if (Text.empty())
122 return std::nullopt;
123 if (needParensAfterUnaryOperator(E)) {
124 return ("&(" + Text + ")").str();
125 }
126 return ("&" + Text).str();
127}
128
129// Append the appropriate access operation (syntactically) to `E`, assuming `E`
130// is a non-pointer value.
131static std::optional<std::string>
132buildAccessForValue(const Expr &E, const ASTContext &Context) {
133 if (const auto *Op = llvm::dyn_cast<UnaryOperator>(Val: &E))
134 if (Op->getOpcode() == UO_Deref) {
135 // Strip leading '*', add following '->'.
136 const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
137 StringRef DerefText = getText(Node: *SubExpr, Context);
138 if (DerefText.empty())
139 return std::nullopt;
140 if (needParensBeforeDotOrArrow(E: *SubExpr))
141 return ("(" + DerefText + ")->").str();
142 return (DerefText + "->").str();
143 }
144
145 // Add following '.'.
146 StringRef Text = getText(Node: E, Context);
147 if (Text.empty())
148 return std::nullopt;
149 if (needParensBeforeDotOrArrow(E)) {
150 return ("(" + Text + ").").str();
151 }
152 return (Text + ".").str();
153}
154
155// Append the appropriate access operation (syntactically) to `E`, assuming `E`
156// is a pointer value.
157static std::optional<std::string>
158buildAccessForPointer(const Expr &E, const ASTContext &Context) {
159 if (const auto *Op = llvm::dyn_cast<UnaryOperator>(Val: &E))
160 if (Op->getOpcode() == UO_AddrOf) {
161 // Strip leading '&', add following '.'.
162 const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
163 StringRef DerefText = getText(Node: *SubExpr, Context);
164 if (DerefText.empty())
165 return std::nullopt;
166 if (needParensBeforeDotOrArrow(E: *SubExpr))
167 return ("(" + DerefText + ").").str();
168 return (DerefText + ".").str();
169 }
170
171 // Add following '->'.
172 StringRef Text = getText(Node: E, Context);
173 if (Text.empty())
174 return std::nullopt;
175 if (needParensBeforeDotOrArrow(E))
176 return ("(" + Text + ")->").str();
177 return (Text + "->").str();
178}
179
180std::optional<std::string> tooling::buildDot(const Expr &E,
181 const ASTContext &Context) {
182 return buildAccessForValue(E, Context);
183}
184
185std::optional<std::string> tooling::buildArrow(const Expr &E,
186 const ASTContext &Context) {
187 return buildAccessForPointer(E, Context);
188}
189
190// If `E` is an overloaded-operator call of kind `K` on an object `O`, returns
191// `O`. Otherwise, returns `nullptr`.
192static const Expr *maybeGetOperatorObjectArg(const Expr &E,
193 OverloadedOperatorKind K) {
194 if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(Val: &E)) {
195 if (OpCall->getOperator() == K && OpCall->getNumArgs() == 1)
196 return OpCall->getArg(Arg: 0);
197 }
198 return nullptr;
199}
200
201static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) {
202 switch (C) {
203 case PLTClass::Value:
204 return false;
205 case PLTClass::Pointer:
206 return isKnownPointerLikeType(Ty, Context);
207 }
208 llvm_unreachable("Unknown PLTClass enum");
209}
210
211// FIXME: move over the other `maybe` functionality from Stencil. Should all be
212// in one place.
213std::optional<std::string> tooling::buildAccess(const Expr &RawExpression,
214 ASTContext &Context,
215 PLTClass Classification) {
216 if (RawExpression.isImplicitCXXThis())
217 // Return the empty string, because `std::nullopt` signifies some sort of
218 // failure.
219 return std::string();
220
221 const Expr *E = RawExpression.IgnoreImplicitAsWritten();
222
223 if (E->getType()->isAnyPointerType() ||
224 treatLikePointer(Ty: E->getType(), C: 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
227 // expression.
228 if (const auto *Obj = maybeGetOperatorObjectArg(E: *E, K: clang::OO_Arrow))
229 E = Obj;
230 return buildAccessForPointer(E: *E, Context);
231 }
232
233 if (const auto *Obj = maybeGetOperatorObjectArg(E: *E, K: clang::OO_Star)) {
234 if (treatLikePointer(Ty: Obj->getType(), C: Classification, Context))
235 return buildAccessForPointer(E: *Obj, Context);
236 };
237
238 return buildAccessForValue(E: *E, Context);
239}
240