/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 78 | const Expr *tooling::reallyIgnoreImplicit(const Expr &E) { |
23 | 78 | const Expr *Expr = E.IgnoreImplicit(); |
24 | 78 | if (const auto *CE = dyn_cast<CXXConstructExpr>(Expr)) { |
25 | 2 | if (CE->getNumArgs() > 0 && |
26 | 2 | CE->getArg(0)->getSourceRange() == Expr->getSourceRange()) |
27 | 2 | return CE->getArg(0)->IgnoreImplicit(); |
28 | 2 | } |
29 | 76 | return Expr; |
30 | 78 | } |
31 | | |
32 | 53 | bool tooling::mayEverNeedParens(const Expr &E) { |
33 | 53 | const Expr *Expr = reallyIgnoreImplicit(E); |
34 | | // We always want parens around unary, binary, and ternary operators, because |
35 | | // they are lower precedence. |
36 | 53 | if (isa<UnaryOperator>(Expr) || isa<BinaryOperator>(Expr)50 || |
37 | 53 | isa<AbstractConditionalOperator>(Expr)42 ) |
38 | 14 | 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 | 39 | if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr)) |
46 | 3 | return Op->getOperator() != OO_Call && Op->getOperator() != OO_Subscript && |
47 | 3 | Op->getOperator() != OO_Arrow; |
48 | | |
49 | 36 | return false; |
50 | 39 | } |
51 | | |
52 | 25 | bool tooling::needParensAfterUnaryOperator(const Expr &E) { |
53 | 25 | const Expr *Expr = reallyIgnoreImplicit(E); |
54 | 25 | if (isa<BinaryOperator>(Expr) || isa<AbstractConditionalOperator>(Expr)19 ) |
55 | 7 | return true; |
56 | | |
57 | 18 | if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(Expr)) |
58 | 3 | return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus2 && |
59 | 3 | Op->getOperator() != OO_MinusMinus2 && Op->getOperator() != OO_Call2 && |
60 | 3 | Op->getOperator() != OO_Subscript2 ; |
61 | | |
62 | 15 | return false; |
63 | 18 | } |
64 | | |
65 | 23 | bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) { |
66 | 23 | using namespace ast_matchers; |
67 | 23 | const auto PointerLikeTy = type(hasUnqualifiedDesugaredType( |
68 | 23 | recordType(hasDeclaration(cxxRecordDecl(hasAnyName( |
69 | 23 | "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr", |
70 | 23 | "::std::optional", "::absl::optional", "::llvm::Optional", |
71 | 23 | "absl::StatusOr", "::llvm::Expected")))))); |
72 | 23 | return match(PointerLikeTy, Ty, Context).size() > 0; |
73 | 23 | } |
74 | | |
75 | | std::optional<std::string> tooling::buildParens(const Expr &E, |
76 | 8 | const ASTContext &Context) { |
77 | 8 | StringRef Text = getText(E, Context); |
78 | 8 | if (Text.empty()) |
79 | 0 | return std::nullopt; |
80 | 8 | if (mayEverNeedParens(E)) |
81 | 3 | return ("(" + Text + ")").str(); |
82 | 5 | return Text.str(); |
83 | 8 | } |
84 | | |
85 | | std::optional<std::string> |
86 | 12 | tooling::buildDereference(const Expr &E, const ASTContext &Context) { |
87 | 12 | if (const auto *Op = dyn_cast<UnaryOperator>(&E)) |
88 | 4 | if (Op->getOpcode() == UO_AddrOf) { |
89 | | // Strip leading '&'. |
90 | 4 | StringRef Text = |
91 | 4 | getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); |
92 | 4 | if (Text.empty()) |
93 | 0 | return std::nullopt; |
94 | 4 | return Text.str(); |
95 | 4 | } |
96 | | |
97 | 8 | StringRef Text = getText(E, Context); |
98 | 8 | if (Text.empty()) |
99 | 0 | return std::nullopt; |
100 | | // Add leading '*'. |
101 | 8 | if (needParensAfterUnaryOperator(E)) |
102 | 3 | return ("*(" + Text + ")").str(); |
103 | 5 | return ("*" + Text).str(); |
104 | 8 | } |
105 | | |
106 | | std::optional<std::string> tooling::buildAddressOf(const Expr &E, |
107 | 11 | const ASTContext &Context) { |
108 | 11 | if (E.isImplicitCXXThis()) |
109 | 1 | return std::string("this"); |
110 | 10 | if (const auto *Op = dyn_cast<UnaryOperator>(&E)) |
111 | 4 | if (Op->getOpcode() == UO_Deref) { |
112 | | // Strip leading '*'. |
113 | 4 | StringRef Text = |
114 | 4 | getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context); |
115 | 4 | if (Text.empty()) |
116 | 0 | return std::nullopt; |
117 | 4 | return Text.str(); |
118 | 4 | } |
119 | | // Add leading '&'. |
120 | 6 | StringRef Text = getText(E, Context); |
121 | 6 | if (Text.empty()) |
122 | 0 | return std::nullopt; |
123 | 6 | if (needParensAfterUnaryOperator(E)) { |
124 | 2 | return ("&(" + Text + ")").str(); |
125 | 2 | } |
126 | 4 | return ("&" + Text).str(); |
127 | 6 | } |
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 | 15 | buildAccessForValue(const Expr &E, const ASTContext &Context) { |
133 | 15 | if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E)) |
134 | 7 | if (Op->getOpcode() == UO_Deref) { |
135 | | // Strip leading '*', add following '->'. |
136 | 7 | const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); |
137 | 7 | StringRef DerefText = getText(*SubExpr, Context); |
138 | 7 | if (DerefText.empty()) |
139 | 0 | return std::nullopt; |
140 | 7 | if (needParensBeforeDotOrArrow(*SubExpr)) |
141 | 2 | return ("(" + DerefText + ")->").str(); |
142 | 5 | return (DerefText + "->").str(); |
143 | 7 | } |
144 | | |
145 | | // Add following '.'. |
146 | 8 | StringRef Text = getText(E, Context); |
147 | 8 | if (Text.empty()) |
148 | 0 | return std::nullopt; |
149 | 8 | if (needParensBeforeDotOrArrow(E)) { |
150 | 3 | return ("(" + Text + ").").str(); |
151 | 3 | } |
152 | 5 | return (Text + ".").str(); |
153 | 8 | } |
154 | | |
155 | | // Append the appropriate access operation (syntactically) to `E`, assuming `E` |
156 | | // is a pointer value. |
157 | | static std::optional<std::string> |
158 | 20 | buildAccessForPointer(const Expr &E, const ASTContext &Context) { |
159 | 20 | if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E)) |
160 | 8 | if (Op->getOpcode() == UO_AddrOf) { |
161 | | // Strip leading '&', add following '.'. |
162 | 8 | const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts(); |
163 | 8 | StringRef DerefText = getText(*SubExpr, Context); |
164 | 8 | if (DerefText.empty()) |
165 | 0 | return std::nullopt; |
166 | 8 | if (needParensBeforeDotOrArrow(*SubExpr)) |
167 | 2 | return ("(" + DerefText + ").").str(); |
168 | 6 | return (DerefText + ".").str(); |
169 | 8 | } |
170 | | |
171 | | // Add following '->'. |
172 | 12 | StringRef Text = getText(E, Context); |
173 | 12 | if (Text.empty()) |
174 | 0 | return std::nullopt; |
175 | 12 | if (needParensBeforeDotOrArrow(E)) |
176 | 2 | return ("(" + Text + ")->").str(); |
177 | 10 | return (Text + "->").str(); |
178 | 12 | } |
179 | | |
180 | | std::optional<std::string> tooling::buildDot(const Expr &E, |
181 | 5 | const ASTContext &Context) { |
182 | 5 | return buildAccessForValue(E, Context); |
183 | 5 | } |
184 | | |
185 | | std::optional<std::string> tooling::buildArrow(const Expr &E, |
186 | 5 | const ASTContext &Context) { |
187 | 5 | return buildAccessForPointer(E, Context); |
188 | 5 | } |
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 | 25 | OverloadedOperatorKind K) { |
194 | 25 | if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(&E)) { |
195 | 6 | if (OpCall->getOperator() == K && OpCall->getNumArgs() == 15 ) |
196 | 5 | return OpCall->getArg(0); |
197 | 6 | } |
198 | 20 | return nullptr; |
199 | 25 | } |
200 | | |
201 | 17 | static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) { |
202 | 17 | switch (C) { |
203 | 3 | case PLTClass::Value: |
204 | 3 | return false; |
205 | 14 | case PLTClass::Pointer: |
206 | 14 | return isKnownPointerLikeType(Ty, Context); |
207 | 17 | } |
208 | 0 | llvm_unreachable("Unknown PLTClass enum"); |
209 | 0 | } |
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 | 28 | PLTClass Classification) { |
216 | 28 | if (RawExpression.isImplicitCXXThis()) |
217 | | // Return the empty string, because `std::nullopt` signifies some sort of |
218 | | // failure. |
219 | 3 | return std::string(); |
220 | | |
221 | 25 | const Expr *E = RawExpression.IgnoreImplicitAsWritten(); |
222 | | |
223 | 25 | if (E->getType()->isAnyPointerType() || |
224 | 25 | treatLikePointer(E->getType(), Classification, Context)14 ) { |
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 | 13 | if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Arrow)) |
229 | 2 | E = Obj; |
230 | 13 | return buildAccessForPointer(*E, Context); |
231 | 13 | } |
232 | | |
233 | 12 | if (const auto *Obj = maybeGetOperatorObjectArg(*E, clang::OO_Star)) { |
234 | 3 | if (treatLikePointer(Obj->getType(), Classification, Context)) |
235 | 2 | return buildAccessForPointer(*Obj, Context); |
236 | 10 | }; |
237 | | |
238 | 10 | return buildAccessForValue(*E, Context); |
239 | 12 | } |