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