1//===- unittest/Tooling/SourceCodeBuildersTest.cpp ------------------------===//
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/Type.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
13#include "clang/Tooling/Tooling.h"
14#include "llvm/Testing/Support/SupportHelpers.h"
15#include "gmock/gmock.h"
16#include "gtest/gtest.h"
17#include <optional>
18
19using namespace clang;
20using namespace tooling;
21using namespace ast_matchers;
22
23namespace {
24using MatchResult = MatchFinder::MatchResult;
25using llvm::ValueIs;
26
27// Create a valid translation unit from a statement.
28static std::string wrapSnippet(StringRef StatementCode) {
29 return ("namespace std {\n"
30 "template <typename T> struct unique_ptr {\n"
31 " T* operator->() const;\n"
32 " T& operator*() const;\n"
33 "};\n"
34 "template <typename T> struct shared_ptr {\n"
35 " T* operator->() const;\n"
36 " T& operator*() const;\n"
37 "};\n"
38 "}\n"
39 "struct A { void super(); };\n"
40 "struct S : public A { S(); S(int); int Field; };\n"
41 "S operator+(const S &a, const S &b);\n"
42 "struct Smart {\n"
43 " S* operator->() const;\n"
44 " S& operator*() const;\n"
45 "};\n"
46 "auto test_snippet = []{" +
47 StatementCode + "};")
48 .str();
49}
50
51static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) {
52 return varDecl(hasName(Name: "test_snippet"),
53 hasDescendant(compoundStmt(hasAnySubstatement(InnerMatcher: Matcher))));
54}
55
56struct TestMatch {
57 // The AST unit from which `result` is built. We bundle it because it backs
58 // the result. Users are not expected to access it.
59 std::unique_ptr<ASTUnit> AstUnit;
60 // The result to use in the test. References `ast_unit`.
61 MatchResult Result;
62};
63
64// Matches `Matcher` against the statement `StatementCode` and returns the
65// result. Handles putting the statement inside a function and modifying the
66// matcher correspondingly. `Matcher` should match one of the statements in
67// `StatementCode` exactly -- that is, produce exactly one match. However,
68// `StatementCode` may contain other statements not described by `Matcher`.
69static std::optional<TestMatch> matchStmt(StringRef StatementCode,
70 StatementMatcher Matcher) {
71 auto AstUnit = buildASTFromCodeWithArgs(Code: wrapSnippet(StatementCode),
72 Args: {"-Wno-unused-value"});
73 if (AstUnit == nullptr) {
74 ADD_FAILURE() << "AST construction failed";
75 return std::nullopt;
76 }
77 ASTContext &Context = AstUnit->getASTContext();
78 auto Matches = ast_matchers::match(Matcher: wrapMatcher(Matcher), Context);
79 // We expect a single, exact match for the statement.
80 if (Matches.size() != 1) {
81 ADD_FAILURE() << "Wrong number of matches: " << Matches.size();
82 return std::nullopt;
83 }
84 return TestMatch{.AstUnit: std::move(AstUnit), .Result: MatchResult(Matches[0], &Context)};
85}
86
87static void testPredicate(bool (*Pred)(const Expr &), StringRef Snippet,
88 bool Expected) {
89 auto StmtMatch = matchStmt(StatementCode: Snippet, Matcher: expr().bind(ID: "expr"));
90 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;
91 EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr")))
92 << "Snippet: " << Snippet;
93}
94
95// Tests the predicate on the call argument, assuming `Snippet` is a function
96// call.
97static void testPredicateOnArg(bool (*Pred)(const Expr &), StringRef Snippet,
98 bool Expected) {
99 auto StmtMatch = matchStmt(
100 StatementCode: Snippet, Matcher: expr(ignoringImplicit(InnerMatcher: callExpr(hasArgument(
101 N: 0, InnerMatcher: ignoringElidableConstructorCall(InnerMatcher: expr().bind(ID: "arg")))))));
102 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;
103 EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("arg")))
104 << "Snippet: " << Snippet;
105}
106
107TEST(SourceCodeBuildersTest, needParensAfterUnaryOperator) {
108 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "3 + 5;", Expected: true);
109 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "true ? 3 : 5;", Expected: true);
110 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "S(3) + S(5);", Expected: true);
111
112 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "int x; x;", Expected: false);
113 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "int(3.0);", Expected: false);
114 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "void f(); f();", Expected: false);
115 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "int a[3]; a[0];", Expected: false);
116 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "S x; x.Field;", Expected: false);
117 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "int x = 1; --x;", Expected: false);
118 testPredicate(Pred: needParensAfterUnaryOperator, Snippet: "int x = 1; -x;", Expected: false);
119}
120
121TEST(SourceCodeBuildersTest, needParensAfterUnaryOperatorInImplicitConversion) {
122 // The binary operation will be embedded in various implicit
123 // expressions. Verify they are ignored.
124 testPredicateOnArg(Pred: needParensAfterUnaryOperator, Snippet: "void f(S); f(3 + 5);",
125 Expected: true);
126}
127
128TEST(SourceCodeBuildersTest, mayEverNeedParens) {
129 testPredicate(Pred: mayEverNeedParens, Snippet: "3 + 5;", Expected: true);
130 testPredicate(Pred: mayEverNeedParens, Snippet: "true ? 3 : 5;", Expected: true);
131 testPredicate(Pred: mayEverNeedParens, Snippet: "int x = 1; --x;", Expected: true);
132 testPredicate(Pred: mayEverNeedParens, Snippet: "int x = 1; -x;", Expected: true);
133
134 testPredicate(Pred: mayEverNeedParens, Snippet: "int x; x;", Expected: false);
135 testPredicate(Pred: mayEverNeedParens, Snippet: "int(3.0);", Expected: false);
136 testPredicate(Pred: mayEverNeedParens, Snippet: "void f(); f();", Expected: false);
137 testPredicate(Pred: mayEverNeedParens, Snippet: "int a[3]; a[0];", Expected: false);
138 testPredicate(Pred: mayEverNeedParens, Snippet: "S x; x.Field;", Expected: false);
139}
140
141TEST(SourceCodeBuildersTest, mayEverNeedParensInImplictConversion) {
142 // The binary operation will be embedded in various implicit
143 // expressions. Verify they are ignored.
144 testPredicateOnArg(Pred: mayEverNeedParens, Snippet: "void f(S); f(3 + 5);", Expected: true);
145}
146
147TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeUniquePtr) {
148 std::string Snippet = "std::unique_ptr<int> P; P;";
149 auto StmtMatch =
150 matchStmt(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "ty"))));
151 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;
152 EXPECT_TRUE(
153 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"),
154 *StmtMatch->Result.Context))
155 << "Snippet: " << Snippet;
156}
157
158TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeSharedPtr) {
159 std::string Snippet = "std::shared_ptr<int> P; P;";
160 auto StmtMatch =
161 matchStmt(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "ty"))));
162 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;
163 EXPECT_TRUE(
164 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"),
165 *StmtMatch->Result.Context))
166 << "Snippet: " << Snippet;
167}
168
169TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeUnknownTypeFalse) {
170 std::string Snippet = "Smart P; P;";
171 auto StmtMatch =
172 matchStmt(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "ty"))));
173 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;
174 EXPECT_FALSE(
175 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"),
176 *StmtMatch->Result.Context))
177 << "Snippet: " << Snippet;
178}
179
180TEST(SourceCodeBuildersTest, isKnownPointerLikeTypeNormalTypeFalse) {
181 std::string Snippet = "int *P; P;";
182 auto StmtMatch =
183 matchStmt(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "ty"))));
184 ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;
185 EXPECT_FALSE(
186 isKnownPointerLikeType(*StmtMatch->Result.Nodes.getNodeAs<QualType>("ty"),
187 *StmtMatch->Result.Context))
188 << "Snippet: " << Snippet;
189}
190
191static void testBuilder(
192 std::optional<std::string> (*Builder)(const Expr &, const ASTContext &),
193 StringRef Snippet, StringRef Expected) {
194 auto StmtMatch = matchStmt(StatementCode: Snippet, Matcher: expr().bind(ID: "expr"));
195 ASSERT_TRUE(StmtMatch);
196 EXPECT_THAT(Builder(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),
197 *StmtMatch->Result.Context),
198 ValueIs(std::string(Expected)));
199}
200
201static void testBuildAccess(StringRef Snippet, StringRef Expected,
202 PLTClass C = PLTClass::Pointer) {
203 auto StmtMatch = matchStmt(StatementCode: Snippet, Matcher: expr().bind(ID: "expr"));
204 ASSERT_TRUE(StmtMatch);
205 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),
206 *StmtMatch->Result.Context, C),
207 ValueIs(std::string(Expected)));
208}
209
210TEST(SourceCodeBuildersTest, BuildParensUnaryOp) {
211 testBuilder(Builder: buildParens, Snippet: "-4;", Expected: "(-4)");
212}
213
214TEST(SourceCodeBuildersTest, BuildParensBinOp) {
215 testBuilder(Builder: buildParens, Snippet: "4 + 4;", Expected: "(4 + 4)");
216}
217
218TEST(SourceCodeBuildersTest, BuildParensValue) {
219 testBuilder(Builder: buildParens, Snippet: "4;", Expected: "4");
220}
221
222TEST(SourceCodeBuildersTest, BuildParensSubscript) {
223 testBuilder(Builder: buildParens, Snippet: "int a[3]; a[0];", Expected: "a[0]");
224}
225
226TEST(SourceCodeBuildersTest, BuildParensCall) {
227 testBuilder(Builder: buildParens, Snippet: "int f(int); f(4);", Expected: "f(4)");
228}
229
230TEST(SourceCodeBuildersTest, BuildAddressOfValue) {
231 testBuilder(Builder: buildAddressOf, Snippet: "S x; x;", Expected: "&x");
232}
233
234TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereference) {
235 testBuilder(Builder: buildAddressOf, Snippet: "S *x; *x;", Expected: "x");
236}
237
238TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereferenceIgnoresParens) {
239 testBuilder(Builder: buildAddressOf, Snippet: "S *x; *(x);", Expected: "x");
240}
241
242TEST(SourceCodeBuildersTest, BuildAddressOfBinaryOperation) {
243 testBuilder(Builder: buildAddressOf, Snippet: "S x; x + x;", Expected: "&(x + x)");
244}
245
246TEST(SourceCodeBuildersTest, BuildAddressOfImplicitThis) {
247 StringRef Snippet = R"cc(
248 struct Struct {
249 void foo() {}
250 void bar() {
251 foo();
252 }
253 };
254 )cc";
255 auto StmtMatch = matchStmt(
256 StatementCode: Snippet,
257 Matcher: cxxMemberCallExpr(onImplicitObjectArgument(InnerMatcher: cxxThisExpr().bind(ID: "expr"))));
258 ASSERT_TRUE(StmtMatch);
259 EXPECT_THAT(buildAddressOf(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),
260 *StmtMatch->Result.Context),
261 ValueIs(std::string("this")));
262}
263
264TEST(SourceCodeBuildersTest, BuildDereferencePointer) {
265 testBuilder(Builder: buildDereference, Snippet: "S *x; x;", Expected: "*x");
266}
267
268TEST(SourceCodeBuildersTest, BuildDereferenceValueAddress) {
269 testBuilder(Builder: buildDereference, Snippet: "S x; &x;", Expected: "x");
270}
271
272TEST(SourceCodeBuildersTest, BuildDereferenceValueAddressIgnoresParens) {
273 testBuilder(Builder: buildDereference, Snippet: "S x; &(x);", Expected: "x");
274}
275
276TEST(SourceCodeBuildersTest, BuildDereferenceBinaryOperation) {
277 testBuilder(Builder: buildDereference, Snippet: "S *x; x + 1;", Expected: "*(x + 1)");
278}
279
280TEST(SourceCodeBuildersTest, BuildDotValue) {
281 testBuilder(Builder: buildDot, Snippet: "S x; x;", Expected: "x.");
282}
283
284TEST(SourceCodeBuildersTest, BuildDotPointerDereference) {
285 testBuilder(Builder: buildDot, Snippet: "S *x; *x;", Expected: "x->");
286}
287
288TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceIgnoresParens) {
289 testBuilder(Builder: buildDot, Snippet: "S *x; *(x);", Expected: "x->");
290}
291
292TEST(SourceCodeBuildersTest, BuildDotBinaryOperation) {
293 testBuilder(Builder: buildDot, Snippet: "S x; x + x;", Expected: "(x + x).");
294}
295
296TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceExprWithParens) {
297 testBuilder(Builder: buildDot, Snippet: "S *x; *(x + 1);", Expected: "(x + 1)->");
298}
299
300TEST(SourceCodeBuildersTest, BuildArrowPointer) {
301 testBuilder(Builder: buildArrow, Snippet: "S *x; x;", Expected: "x->");
302}
303
304TEST(SourceCodeBuildersTest, BuildArrowValueAddress) {
305 testBuilder(Builder: buildArrow, Snippet: "S x; &x;", Expected: "x.");
306}
307
308TEST(SourceCodeBuildersTest, BuildArrowValueAddressIgnoresParens) {
309 testBuilder(Builder: buildArrow, Snippet: "S x; &(x);", Expected: "x.");
310}
311
312TEST(SourceCodeBuildersTest, BuildArrowBinaryOperation) {
313 testBuilder(Builder: buildArrow, Snippet: "S *x; x + 1;", Expected: "(x + 1)->");
314}
315
316TEST(SourceCodeBuildersTest, BuildArrowValueAddressWithParens) {
317 testBuilder(Builder: buildArrow, Snippet: "S x; &(true ? x : x);", Expected: "(true ? x : x).");
318}
319
320TEST(SourceCodeBuildersTest, BuildAccessValue) {
321 testBuildAccess(Snippet: "S x; x;", Expected: "x.");
322}
323
324TEST(SourceCodeBuildersTest, BuildAccessPointerDereference) {
325 testBuildAccess(Snippet: "S *x; *x;", Expected: "x->");
326}
327
328TEST(SourceCodeBuildersTest, BuildAccessPointerDereferenceIgnoresParens) {
329 testBuildAccess(Snippet: "S *x; *(x);", Expected: "x->");
330}
331
332TEST(SourceCodeBuildersTest, BuildAccessValueBinaryOperation) {
333 testBuildAccess(Snippet: "S x; x + x;", Expected: "(x + x).");
334}
335
336TEST(SourceCodeBuildersTest, BuildAccessPointerDereferenceExprWithParens) {
337 testBuildAccess(Snippet: "S *x; *(x + 1);", Expected: "(x + 1)->");
338}
339
340TEST(SourceCodeBuildersTest, BuildAccessPointer) {
341 testBuildAccess(Snippet: "S *x; x;", Expected: "x->");
342}
343
344TEST(SourceCodeBuildersTest, BuildAccessValueAddress) {
345 testBuildAccess(Snippet: "S x; &x;", Expected: "x.");
346}
347
348TEST(SourceCodeBuildersTest, BuildAccessValueAddressIgnoresParens) {
349 testBuildAccess(Snippet: "S x; &(x);", Expected: "x.");
350}
351
352TEST(SourceCodeBuildersTest, BuildAccessPointerBinaryOperation) {
353 testBuildAccess(Snippet: "S *x; x + 1;", Expected: "(x + 1)->");
354}
355
356TEST(SourceCodeBuildersTest, BuildAccessValueAddressWithParens) {
357 testBuildAccess(Snippet: "S x; &(true ? x : x);", Expected: "(true ? x : x).");
358}
359
360TEST(SourceCodeBuildersTest, BuildAccessSmartPointer) {
361 testBuildAccess(Snippet: "std::unique_ptr<int> x; x;", Expected: "x->");
362}
363
364TEST(SourceCodeBuildersTest, BuildAccessSmartPointerAsValue) {
365 testBuildAccess(Snippet: "std::unique_ptr<int> x; x;", Expected: "x.", C: PLTClass::Value);
366}
367
368TEST(SourceCodeBuildersTest, BuildAccessSmartPointerDeref) {
369 testBuildAccess(Snippet: "std::unique_ptr<int> x; *x;", Expected: "x->");
370}
371
372TEST(SourceCodeBuildersTest, BuildAccessSmartPointerDerefAsValue) {
373 testBuildAccess(Snippet: "std::unique_ptr<int> x; *x;", Expected: "(*x).", C: PLTClass::Value);
374}
375
376TEST(SourceCodeBuildersTest, BuildAccessSmartPointerMemberCall) {
377 StringRef Snippet = R"cc(
378 Smart x;
379 x->Field;
380 )cc";
381 auto StmtMatch =
382 matchStmt(StatementCode: Snippet, Matcher: memberExpr(hasObjectExpression(InnerMatcher: expr().bind(ID: "expr"))));
383 ASSERT_TRUE(StmtMatch);
384 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),
385 *StmtMatch->Result.Context),
386 ValueIs(std::string("x->")));
387}
388
389TEST(SourceCodeBuildersTest, BuildAccessIgnoreImplicit) {
390 StringRef Snippet = R"cc(
391 S x;
392 A *a;
393 a = &x;
394 )cc";
395 auto StmtMatch =
396 matchStmt(StatementCode: Snippet, Matcher: binaryOperator(isAssignmentOperator(),
397 hasRHS(InnerMatcher: expr().bind(ID: "expr"))));
398 ASSERT_TRUE(StmtMatch);
399 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),
400 *StmtMatch->Result.Context),
401 ValueIs(std::string("x.")));
402}
403
404TEST(SourceCodeBuildersTest, BuildAccessImplicitThis) {
405 StringRef Snippet = R"cc(
406 struct Struct {
407 void foo() {}
408 void bar() {
409 foo();
410 }
411 };
412 )cc";
413 auto StmtMatch = matchStmt(
414 StatementCode: Snippet,
415 Matcher: cxxMemberCallExpr(onImplicitObjectArgument(InnerMatcher: cxxThisExpr().bind(ID: "expr"))));
416 ASSERT_TRUE(StmtMatch);
417 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),
418 *StmtMatch->Result.Context),
419 ValueIs(std::string()));
420}
421
422TEST(SourceCodeBuildersTest, BuildAccessImplicitThisIgnoreImplicitCasts) {
423 StringRef Snippet = "struct B : public A { void f() { super(); } };";
424 auto StmtMatch = matchStmt(
425 StatementCode: Snippet,
426 Matcher: cxxMemberCallExpr(onImplicitObjectArgument(InnerMatcher: expr().bind(ID: "expr"))));
427 ASSERT_TRUE(StmtMatch);
428 EXPECT_THAT(buildAccess(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),
429 *StmtMatch->Result.Context),
430 ValueIs(std::string()));
431}
432} // namespace
433

source code of clang/unittests/Tooling/SourceCodeBuildersTest.cpp