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