Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp
35266 views
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
19
using namespace clang;
20
using namespace tooling;
21
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();
28
}
29
return Expr;
30
}
31
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))
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>(Expr))
46
return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript &&
47
Op->getOperator() != OO_Arrow;
48
49
return false;
50
}
51
52
bool tooling::needParensAfterUnaryOperator(const Expr &E) {
53
const Expr *Expr = reallyIgnoreImplicit(E);
54
if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr))
55
return true;
56
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;
61
62
return false;
63
}
64
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;
73
}
74
75
std::optional<std::string> tooling::buildParens(const Expr &E,
76
const ASTContext &Context) {
77
StringRef Text = getText(E, Context);
78
if (Text.empty())
79
return std::nullopt;
80
if (mayEverNeedParens(E))
81
return ("(" + Text + ")").str();
82
return Text.str();
83
}
84
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) {
89
// Strip leading '&'.
90
StringRef Text =
91
getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
92
if (Text.empty())
93
return std::nullopt;
94
return Text.str();
95
}
96
97
StringRef Text = getText(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
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 '*'.
113
StringRef Text =
114
getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
115
if (Text.empty())
116
return std::nullopt;
117
return Text.str();
118
}
119
// Add leading '&'.
120
StringRef Text = getText(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.
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())
139
return std::nullopt;
140
if (needParensBeforeDotOrArrow(*SubExpr))
141
return ("(" + DerefText + ")->").str();
142
return (DerefText + "->").str();
143
}
144
145
// Add following '.'.
146
StringRef Text = getText(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.
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())
165
return std::nullopt;
166
if (needParensBeforeDotOrArrow(*SubExpr))
167
return ("(" + DerefText + ").").str();
168
return (DerefText + ".").str();
169
}
170
171
// Add following '->'.
172
StringRef Text = getText(E, Context);
173
if (Text.empty())
174
return std::nullopt;
175
if (needParensBeforeDotOrArrow(E))
176
return ("(" + Text + ")->").str();
177
return (Text + "->").str();
178
}
179
180
std::optional<std::string> tooling::buildDot(const Expr &E,
181
const ASTContext &Context) {
182
return buildAccessForValue(E, Context);
183
}
184
185
std::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`.
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);
197
}
198
return nullptr;
199
}
200
201
static 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.
213
std::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(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
227
// expression.
228
if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow))
229
E = Obj;
230
return buildAccessForPointer(*E, Context);
231
}
232
233
if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) {
234
if (treatLikePointer(Obj->getType(), Classification, Context))
235
return buildAccessForPointer(*Obj, Context);
236
};
237
238
return buildAccessForValue(*E, Context);
239
}
240
241