1 //===- unittest/Tooling/SourceCodeBuildersTest.cpp ------------------------===//
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/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"
19 using namespace clang
;
20 using namespace tooling
;
21 using namespace ast_matchers
;
24 using MatchResult
= MatchFinder::MatchResult
;
27 // Create a valid translation unit from a statement.
28 static 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"
34 "template <typename T> struct shared_ptr {\n"
35 " T* operator->() const;\n"
36 " T& operator*() const;\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"
43 " S* operator->() const;\n"
44 " S& operator*() const;\n"
46 "auto test_snippet = []{" +
51 static DeclarationMatcher
wrapMatcher(const StatementMatcher
&Matcher
) {
52 return varDecl(hasName("test_snippet"),
53 hasDescendant(compoundStmt(hasAnySubstatement(Matcher
))));
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`.
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`.
69 static std::optional
<TestMatch
> matchStmt(StringRef StatementCode
,
70 StatementMatcher Matcher
) {
71 auto AstUnit
= buildASTFromCodeWithArgs(wrapSnippet(StatementCode
),
72 {"-Wno-unused-value"});
73 if (AstUnit
== nullptr) {
74 ADD_FAILURE() << "AST construction failed";
77 ASTContext
&Context
= AstUnit
->getASTContext();
78 auto Matches
= ast_matchers::match(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();
84 return TestMatch
{std::move(AstUnit
), MatchResult(Matches
[0], &Context
)};
87 static void testPredicate(bool (*Pred
)(const Expr
&), StringRef Snippet
,
89 auto StmtMatch
= matchStmt(Snippet
, expr().bind("expr"));
90 ASSERT_TRUE(StmtMatch
) << "Snippet: " << Snippet
;
91 EXPECT_EQ(Expected
, Pred(*StmtMatch
->Result
.Nodes
.getNodeAs
<Expr
>("expr")))
92 << "Snippet: " << Snippet
;
95 // Tests the predicate on the call argument, assuming `Snippet` is a function
97 static void testPredicateOnArg(bool (*Pred
)(const Expr
&), StringRef Snippet
,
99 auto StmtMatch
= matchStmt(
100 Snippet
, expr(ignoringImplicit(callExpr(hasArgument(
101 0, ignoringElidableConstructorCall(expr().bind("arg")))))));
102 ASSERT_TRUE(StmtMatch
) << "Snippet: " << Snippet
;
103 EXPECT_EQ(Expected
, Pred(*StmtMatch
->Result
.Nodes
.getNodeAs
<Expr
>("arg")))
104 << "Snippet: " << Snippet
;
107 TEST(SourceCodeBuildersTest
, needParensAfterUnaryOperator
) {
108 testPredicate(needParensAfterUnaryOperator
, "3 + 5;", true);
109 testPredicate(needParensAfterUnaryOperator
, "true ? 3 : 5;", true);
110 testPredicate(needParensAfterUnaryOperator
, "S(3) + S(5);", true);
112 testPredicate(needParensAfterUnaryOperator
, "int x; x;", false);
113 testPredicate(needParensAfterUnaryOperator
, "int(3.0);", false);
114 testPredicate(needParensAfterUnaryOperator
, "void f(); f();", false);
115 testPredicate(needParensAfterUnaryOperator
, "int a[3]; a[0];", false);
116 testPredicate(needParensAfterUnaryOperator
, "S x; x.Field;", false);
117 testPredicate(needParensAfterUnaryOperator
, "int x = 1; --x;", false);
118 testPredicate(needParensAfterUnaryOperator
, "int x = 1; -x;", false);
121 TEST(SourceCodeBuildersTest
, needParensAfterUnaryOperatorInImplicitConversion
) {
122 // The binary operation will be embedded in various implicit
123 // expressions. Verify they are ignored.
124 testPredicateOnArg(needParensAfterUnaryOperator
, "void f(S); f(3 + 5);",
128 TEST(SourceCodeBuildersTest
, mayEverNeedParens
) {
129 testPredicate(mayEverNeedParens
, "3 + 5;", true);
130 testPredicate(mayEverNeedParens
, "true ? 3 : 5;", true);
131 testPredicate(mayEverNeedParens
, "int x = 1; --x;", true);
132 testPredicate(mayEverNeedParens
, "int x = 1; -x;", true);
134 testPredicate(mayEverNeedParens
, "int x; x;", false);
135 testPredicate(mayEverNeedParens
, "int(3.0);", false);
136 testPredicate(mayEverNeedParens
, "void f(); f();", false);
137 testPredicate(mayEverNeedParens
, "int a[3]; a[0];", false);
138 testPredicate(mayEverNeedParens
, "S x; x.Field;", false);
141 TEST(SourceCodeBuildersTest
, mayEverNeedParensInImplictConversion
) {
142 // The binary operation will be embedded in various implicit
143 // expressions. Verify they are ignored.
144 testPredicateOnArg(mayEverNeedParens
, "void f(S); f(3 + 5);", true);
147 TEST(SourceCodeBuildersTest
, isKnownPointerLikeTypeUniquePtr
) {
148 std::string Snippet
= "std::unique_ptr<int> P; P;";
150 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("ty"))));
151 ASSERT_TRUE(StmtMatch
) << "Snippet: " << Snippet
;
153 isKnownPointerLikeType(*StmtMatch
->Result
.Nodes
.getNodeAs
<QualType
>("ty"),
154 *StmtMatch
->Result
.Context
))
155 << "Snippet: " << Snippet
;
158 TEST(SourceCodeBuildersTest
, isKnownPointerLikeTypeSharedPtr
) {
159 std::string Snippet
= "std::shared_ptr<int> P; P;";
161 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("ty"))));
162 ASSERT_TRUE(StmtMatch
) << "Snippet: " << Snippet
;
164 isKnownPointerLikeType(*StmtMatch
->Result
.Nodes
.getNodeAs
<QualType
>("ty"),
165 *StmtMatch
->Result
.Context
))
166 << "Snippet: " << Snippet
;
169 TEST(SourceCodeBuildersTest
, isKnownPointerLikeTypeUnknownTypeFalse
) {
170 std::string Snippet
= "Smart P; P;";
172 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("ty"))));
173 ASSERT_TRUE(StmtMatch
) << "Snippet: " << Snippet
;
175 isKnownPointerLikeType(*StmtMatch
->Result
.Nodes
.getNodeAs
<QualType
>("ty"),
176 *StmtMatch
->Result
.Context
))
177 << "Snippet: " << Snippet
;
180 TEST(SourceCodeBuildersTest
, isKnownPointerLikeTypeNormalTypeFalse
) {
181 std::string Snippet
= "int *P; P;";
183 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("ty"))));
184 ASSERT_TRUE(StmtMatch
) << "Snippet: " << Snippet
;
186 isKnownPointerLikeType(*StmtMatch
->Result
.Nodes
.getNodeAs
<QualType
>("ty"),
187 *StmtMatch
->Result
.Context
))
188 << "Snippet: " << Snippet
;
191 static void testBuilder(
192 std::optional
<std::string
> (*Builder
)(const Expr
&, const ASTContext
&),
193 StringRef Snippet
, StringRef Expected
) {
194 auto StmtMatch
= matchStmt(Snippet
, expr().bind("expr"));
195 ASSERT_TRUE(StmtMatch
);
196 EXPECT_THAT(Builder(*StmtMatch
->Result
.Nodes
.getNodeAs
<Expr
>("expr"),
197 *StmtMatch
->Result
.Context
),
198 ValueIs(std::string(Expected
)));
201 static void testBuildAccess(StringRef Snippet
, StringRef Expected
,
202 PLTClass C
= PLTClass::Pointer
) {
203 auto StmtMatch
= matchStmt(Snippet
, expr().bind("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
)));
210 TEST(SourceCodeBuildersTest
, BuildParensUnaryOp
) {
211 testBuilder(buildParens
, "-4;", "(-4)");
214 TEST(SourceCodeBuildersTest
, BuildParensBinOp
) {
215 testBuilder(buildParens
, "4 + 4;", "(4 + 4)");
218 TEST(SourceCodeBuildersTest
, BuildParensValue
) {
219 testBuilder(buildParens
, "4;", "4");
222 TEST(SourceCodeBuildersTest
, BuildParensSubscript
) {
223 testBuilder(buildParens
, "int a[3]; a[0];", "a[0]");
226 TEST(SourceCodeBuildersTest
, BuildParensCall
) {
227 testBuilder(buildParens
, "int f(int); f(4);", "f(4)");
230 TEST(SourceCodeBuildersTest
, BuildAddressOfValue
) {
231 testBuilder(buildAddressOf
, "S x; x;", "&x");
234 TEST(SourceCodeBuildersTest
, BuildAddressOfPointerDereference
) {
235 testBuilder(buildAddressOf
, "S *x; *x;", "x");
238 TEST(SourceCodeBuildersTest
, BuildAddressOfPointerDereferenceIgnoresParens
) {
239 testBuilder(buildAddressOf
, "S *x; *(x);", "x");
242 TEST(SourceCodeBuildersTest
, BuildAddressOfBinaryOperation
) {
243 testBuilder(buildAddressOf
, "S x; x + x;", "&(x + x)");
246 TEST(SourceCodeBuildersTest
, BuildAddressOfImplicitThis
) {
247 StringRef Snippet
= R
"cc(
255 auto StmtMatch
= matchStmt(
257 cxxMemberCallExpr(onImplicitObjectArgument(cxxThisExpr().bind("expr"))));
258 ASSERT_TRUE(StmtMatch
);
259 EXPECT_THAT(buildAddressOf(*StmtMatch
->Result
.Nodes
.getNodeAs
<Expr
>("expr"),
260 *StmtMatch
->Result
.Context
),
261 ValueIs(std::string("this")));
264 TEST(SourceCodeBuildersTest
, BuildDereferencePointer
) {
265 testBuilder(buildDereference
, "S *x; x;", "*x");
268 TEST(SourceCodeBuildersTest
, BuildDereferenceValueAddress
) {
269 testBuilder(buildDereference
, "S x; &x;", "x");
272 TEST(SourceCodeBuildersTest
, BuildDereferenceValueAddressIgnoresParens
) {
273 testBuilder(buildDereference
, "S x; &(x);", "x");
276 TEST(SourceCodeBuildersTest
, BuildDereferenceBinaryOperation
) {
277 testBuilder(buildDereference
, "S *x; x + 1;", "*(x + 1)");
280 TEST(SourceCodeBuildersTest
, BuildDotValue
) {
281 testBuilder(buildDot
, "S x; x;", "x.");
284 TEST(SourceCodeBuildersTest
, BuildDotPointerDereference
) {
285 testBuilder(buildDot
, "S *x; *x;", "x->");
288 TEST(SourceCodeBuildersTest
, BuildDotPointerDereferenceIgnoresParens
) {
289 testBuilder(buildDot
, "S *x; *(x);", "x->");
292 TEST(SourceCodeBuildersTest
, BuildDotBinaryOperation
) {
293 testBuilder(buildDot
, "S x; x + x;", "(x + x).");
296 TEST(SourceCodeBuildersTest
, BuildDotPointerDereferenceExprWithParens
) {
297 testBuilder(buildDot
, "S *x; *(x + 1);", "(x + 1)->");
300 TEST(SourceCodeBuildersTest
, BuildArrowPointer
) {
301 testBuilder(buildArrow
, "S *x; x;", "x->");
304 TEST(SourceCodeBuildersTest
, BuildArrowValueAddress
) {
305 testBuilder(buildArrow
, "S x; &x;", "x.");
308 TEST(SourceCodeBuildersTest
, BuildArrowValueAddressIgnoresParens
) {
309 testBuilder(buildArrow
, "S x; &(x);", "x.");
312 TEST(SourceCodeBuildersTest
, BuildArrowBinaryOperation
) {
313 testBuilder(buildArrow
, "S *x; x + 1;", "(x + 1)->");
316 TEST(SourceCodeBuildersTest
, BuildArrowValueAddressWithParens
) {
317 testBuilder(buildArrow
, "S x; &(true ? x : x);", "(true ? x : x).");
320 TEST(SourceCodeBuildersTest
, BuildAccessValue
) {
321 testBuildAccess("S x; x;", "x.");
324 TEST(SourceCodeBuildersTest
, BuildAccessPointerDereference
) {
325 testBuildAccess("S *x; *x;", "x->");
328 TEST(SourceCodeBuildersTest
, BuildAccessPointerDereferenceIgnoresParens
) {
329 testBuildAccess("S *x; *(x);", "x->");
332 TEST(SourceCodeBuildersTest
, BuildAccessValueBinaryOperation
) {
333 testBuildAccess("S x; x + x;", "(x + x).");
336 TEST(SourceCodeBuildersTest
, BuildAccessPointerDereferenceExprWithParens
) {
337 testBuildAccess("S *x; *(x + 1);", "(x + 1)->");
340 TEST(SourceCodeBuildersTest
, BuildAccessPointer
) {
341 testBuildAccess("S *x; x;", "x->");
344 TEST(SourceCodeBuildersTest
, BuildAccessValueAddress
) {
345 testBuildAccess("S x; &x;", "x.");
348 TEST(SourceCodeBuildersTest
, BuildAccessValueAddressIgnoresParens
) {
349 testBuildAccess("S x; &(x);", "x.");
352 TEST(SourceCodeBuildersTest
, BuildAccessPointerBinaryOperation
) {
353 testBuildAccess("S *x; x + 1;", "(x + 1)->");
356 TEST(SourceCodeBuildersTest
, BuildAccessValueAddressWithParens
) {
357 testBuildAccess("S x; &(true ? x : x);", "(true ? x : x).");
360 TEST(SourceCodeBuildersTest
, BuildAccessSmartPointer
) {
361 testBuildAccess("std::unique_ptr<int> x; x;", "x->");
364 TEST(SourceCodeBuildersTest
, BuildAccessSmartPointerAsValue
) {
365 testBuildAccess("std::unique_ptr<int> x; x;", "x.", PLTClass::Value
);
368 TEST(SourceCodeBuildersTest
, BuildAccessSmartPointerDeref
) {
369 testBuildAccess("std::unique_ptr<int> x; *x;", "x->");
372 TEST(SourceCodeBuildersTest
, BuildAccessSmartPointerDerefAsValue
) {
373 testBuildAccess("std::unique_ptr<int> x; *x;", "(*x).", PLTClass::Value
);
376 TEST(SourceCodeBuildersTest
, BuildAccessSmartPointerMemberCall
) {
377 StringRef Snippet
= R
"cc(
382 matchStmt(Snippet
, memberExpr(hasObjectExpression(expr().bind("expr"))));
383 ASSERT_TRUE(StmtMatch
);
384 EXPECT_THAT(buildAccess(*StmtMatch
->Result
.Nodes
.getNodeAs
<Expr
>("expr"),
385 *StmtMatch
->Result
.Context
),
386 ValueIs(std::string("x->")));
389 TEST(SourceCodeBuildersTest
, BuildAccessIgnoreImplicit
) {
390 StringRef Snippet
= R
"cc(
396 matchStmt(Snippet
, binaryOperator(isAssignmentOperator(),
397 hasRHS(expr().bind("expr"))));
398 ASSERT_TRUE(StmtMatch
);
399 EXPECT_THAT(buildAccess(*StmtMatch
->Result
.Nodes
.getNodeAs
<Expr
>("expr"),
400 *StmtMatch
->Result
.Context
),
401 ValueIs(std::string("x.")));
404 TEST(SourceCodeBuildersTest
, BuildAccessImplicitThis
) {
405 StringRef Snippet
= R
"cc(
413 auto StmtMatch
= matchStmt(
415 cxxMemberCallExpr(onImplicitObjectArgument(cxxThisExpr().bind("expr"))));
416 ASSERT_TRUE(StmtMatch
);
417 EXPECT_THAT(buildAccess(*StmtMatch
->Result
.Nodes
.getNodeAs
<Expr
>("expr"),
418 *StmtMatch
->Result
.Context
),
419 ValueIs(std::string()));
422 TEST(SourceCodeBuildersTest
, BuildAccessImplicitThisIgnoreImplicitCasts
) {
423 StringRef Snippet
= "struct B : public A { void f() { super(); } };";
424 auto StmtMatch
= matchStmt(
426 cxxMemberCallExpr(onImplicitObjectArgument(expr().bind("expr"))));
427 ASSERT_TRUE(StmtMatch
);
428 EXPECT_THAT(buildAccess(*StmtMatch
->Result
.Nodes
.getNodeAs
<Expr
>("expr"),
429 *StmtMatch
->Result
.Context
),
430 ValueIs(std::string()));