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/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "clang/Tooling/Tooling.h"
13 #include "llvm/Testing/Support/SupportHelpers.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 using namespace clang;
18 using namespace tooling;
19 using namespace ast_matchers;
20 
21 namespace {
22 using MatchResult = MatchFinder::MatchResult;
23 using llvm::ValueIs;
24 
25 // Create a valid translation unit from a statement.
wrapSnippet(StringRef StatementCode)26 static std::string wrapSnippet(StringRef StatementCode) {
27   return ("struct S { S(); S(int); int field; };\n"
28           "S operator+(const S &a, const S &b);\n"
29           "auto test_snippet = []{" +
30           StatementCode + "};")
31       .str();
32 }
33 
wrapMatcher(const StatementMatcher & Matcher)34 static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) {
35   return varDecl(hasName("test_snippet"),
36                  hasDescendant(compoundStmt(hasAnySubstatement(Matcher))));
37 }
38 
39 struct TestMatch {
40   // The AST unit from which `result` is built. We bundle it because it backs
41   // the result. Users are not expected to access it.
42   std::unique_ptr<ASTUnit> AstUnit;
43   // The result to use in the test. References `ast_unit`.
44   MatchResult Result;
45 };
46 
47 // Matches `Matcher` against the statement `StatementCode` and returns the
48 // result. Handles putting the statement inside a function and modifying the
49 // matcher correspondingly. `Matcher` should match one of the statements in
50 // `StatementCode` exactly -- that is, produce exactly one match. However,
51 // `StatementCode` may contain other statements not described by `Matcher`.
matchStmt(StringRef StatementCode,StatementMatcher Matcher)52 static llvm::Optional<TestMatch> matchStmt(StringRef StatementCode,
53                                            StatementMatcher Matcher) {
54   auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode));
55   if (AstUnit == nullptr) {
56     ADD_FAILURE() << "AST construction failed";
57     return llvm::None;
58   }
59   ASTContext &Context = AstUnit->getASTContext();
60   auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context);
61   // We expect a single, exact match for the statement.
62   if (Matches.size() != 1) {
63     ADD_FAILURE() << "Wrong number of matches: " << Matches.size();
64     return llvm::None;
65   }
66   return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)};
67 }
68 
testPredicate(bool (* Pred)(const Expr &),StringRef Snippet,bool Expected)69 static void testPredicate(bool (*Pred)(const Expr &), StringRef Snippet,
70                           bool Expected) {
71   auto StmtMatch = matchStmt(Snippet, expr().bind("expr"));
72   ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;
73   EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr")))
74       << "Snippet: " << Snippet;
75 }
76 
77 // Tests the predicate on the call argument, assuming `Snippet` is a function
78 // call.
testPredicateOnArg(bool (* Pred)(const Expr &),StringRef Snippet,bool Expected)79 static void testPredicateOnArg(bool (*Pred)(const Expr &), StringRef Snippet,
80                                bool Expected) {
81   auto StmtMatch = matchStmt(
82       Snippet, expr(ignoringImplicit(callExpr(hasArgument(
83                    0, ignoringElidableConstructorCall(expr().bind("arg")))))));
84   ASSERT_TRUE(StmtMatch) << "Snippet: " << Snippet;
85   EXPECT_EQ(Expected, Pred(*StmtMatch->Result.Nodes.getNodeAs<Expr>("arg")))
86       << "Snippet: " << Snippet;
87 }
88 
TEST(SourceCodeBuildersTest,needParensAfterUnaryOperator)89 TEST(SourceCodeBuildersTest, needParensAfterUnaryOperator) {
90   testPredicate(needParensAfterUnaryOperator, "3 + 5;", true);
91   testPredicate(needParensAfterUnaryOperator, "true ? 3 : 5;", true);
92   testPredicate(needParensAfterUnaryOperator, "S(3) + S(5);", true);
93 
94   testPredicate(needParensAfterUnaryOperator, "int x; x;", false);
95   testPredicate(needParensAfterUnaryOperator, "int(3.0);", false);
96   testPredicate(needParensAfterUnaryOperator, "void f(); f();", false);
97   testPredicate(needParensAfterUnaryOperator, "int a[3]; a[0];", false);
98   testPredicate(needParensAfterUnaryOperator, "S x; x.field;", false);
99   testPredicate(needParensAfterUnaryOperator, "int x = 1; --x;", false);
100   testPredicate(needParensAfterUnaryOperator, "int x = 1; -x;", false);
101 }
102 
TEST(SourceCodeBuildersTest,needParensAfterUnaryOperatorInImplicitConversion)103 TEST(SourceCodeBuildersTest, needParensAfterUnaryOperatorInImplicitConversion) {
104   // The binary operation will be embedded in various implicit
105   // expressions. Verify they are ignored.
106   testPredicateOnArg(needParensAfterUnaryOperator, "void f(S); f(3 + 5);",
107                      true);
108 }
109 
TEST(SourceCodeBuildersTest,mayEverNeedParens)110 TEST(SourceCodeBuildersTest, mayEverNeedParens) {
111   testPredicate(mayEverNeedParens, "3 + 5;", true);
112   testPredicate(mayEverNeedParens, "true ? 3 : 5;", true);
113   testPredicate(mayEverNeedParens, "int x = 1; --x;", true);
114   testPredicate(mayEverNeedParens, "int x = 1; -x;", true);
115 
116   testPredicate(mayEverNeedParens, "int x; x;", false);
117   testPredicate(mayEverNeedParens, "int(3.0);", false);
118   testPredicate(mayEverNeedParens, "void f(); f();", false);
119   testPredicate(mayEverNeedParens, "int a[3]; a[0];", false);
120   testPredicate(mayEverNeedParens, "S x; x.field;", false);
121 }
122 
TEST(SourceCodeBuildersTest,mayEverNeedParensInImplictConversion)123 TEST(SourceCodeBuildersTest, mayEverNeedParensInImplictConversion) {
124   // The binary operation will be embedded in various implicit
125   // expressions. Verify they are ignored.
126   testPredicateOnArg(mayEverNeedParens, "void f(S); f(3 + 5);", true);
127 }
128 
testBuilder(llvm::Optional<std::string> (* Builder)(const Expr &,const ASTContext &),StringRef Snippet,StringRef Expected)129 static void testBuilder(
130     llvm::Optional<std::string> (*Builder)(const Expr &, const ASTContext &),
131     StringRef Snippet, StringRef Expected) {
132   auto StmtMatch = matchStmt(Snippet, expr().bind("expr"));
133   ASSERT_TRUE(StmtMatch);
134   EXPECT_THAT(Builder(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),
135                       *StmtMatch->Result.Context),
136               ValueIs(std::string(Expected)));
137 }
138 
TEST(SourceCodeBuildersTest,BuildParensUnaryOp)139 TEST(SourceCodeBuildersTest, BuildParensUnaryOp) {
140   testBuilder(buildParens, "-4;", "(-4)");
141 }
142 
TEST(SourceCodeBuildersTest,BuildParensBinOp)143 TEST(SourceCodeBuildersTest, BuildParensBinOp) {
144   testBuilder(buildParens, "4 + 4;", "(4 + 4)");
145 }
146 
TEST(SourceCodeBuildersTest,BuildParensValue)147 TEST(SourceCodeBuildersTest, BuildParensValue) {
148   testBuilder(buildParens, "4;", "4");
149 }
150 
TEST(SourceCodeBuildersTest,BuildParensSubscript)151 TEST(SourceCodeBuildersTest, BuildParensSubscript) {
152   testBuilder(buildParens, "int a[3]; a[0];", "a[0]");
153 }
154 
TEST(SourceCodeBuildersTest,BuildParensCall)155 TEST(SourceCodeBuildersTest, BuildParensCall) {
156   testBuilder(buildParens, "int f(int); f(4);", "f(4)");
157 }
158 
TEST(SourceCodeBuildersTest,BuildAddressOfValue)159 TEST(SourceCodeBuildersTest, BuildAddressOfValue) {
160   testBuilder(buildAddressOf, "S x; x;", "&x");
161 }
162 
TEST(SourceCodeBuildersTest,BuildAddressOfPointerDereference)163 TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereference) {
164   testBuilder(buildAddressOf, "S *x; *x;", "x");
165 }
166 
TEST(SourceCodeBuildersTest,BuildAddressOfPointerDereferenceIgnoresParens)167 TEST(SourceCodeBuildersTest, BuildAddressOfPointerDereferenceIgnoresParens) {
168   testBuilder(buildAddressOf, "S *x; *(x);", "x");
169 }
170 
TEST(SourceCodeBuildersTest,BuildAddressOfBinaryOperation)171 TEST(SourceCodeBuildersTest, BuildAddressOfBinaryOperation) {
172   testBuilder(buildAddressOf, "S x; x + x;", "&(x + x)");
173 }
174 
TEST(SourceCodeBuildersTest,BuildAddressOfImplicitThis)175 TEST(SourceCodeBuildersTest, BuildAddressOfImplicitThis) {
176   StringRef Snippet = R"cc(
177     struct Struct {
178       void foo() {}
179       void bar() {
180         foo();
181       }
182     };
183   )cc";
184   auto StmtMatch = matchStmt(
185       Snippet,
186       cxxMemberCallExpr(onImplicitObjectArgument(cxxThisExpr().bind("expr"))));
187   ASSERT_TRUE(StmtMatch);
188   EXPECT_THAT(buildAddressOf(*StmtMatch->Result.Nodes.getNodeAs<Expr>("expr"),
189                              *StmtMatch->Result.Context),
190               ValueIs(std::string("this")));
191 }
192 
TEST(SourceCodeBuildersTest,BuildDereferencePointer)193 TEST(SourceCodeBuildersTest, BuildDereferencePointer) {
194   testBuilder(buildDereference, "S *x; x;", "*x");
195 }
196 
TEST(SourceCodeBuildersTest,BuildDereferenceValueAddress)197 TEST(SourceCodeBuildersTest, BuildDereferenceValueAddress) {
198   testBuilder(buildDereference, "S x; &x;", "x");
199 }
200 
TEST(SourceCodeBuildersTest,BuildDereferenceValueAddressIgnoresParens)201 TEST(SourceCodeBuildersTest, BuildDereferenceValueAddressIgnoresParens) {
202   testBuilder(buildDereference, "S x; &(x);", "x");
203 }
204 
TEST(SourceCodeBuildersTest,BuildDereferenceBinaryOperation)205 TEST(SourceCodeBuildersTest, BuildDereferenceBinaryOperation) {
206   testBuilder(buildDereference, "S *x; x + 1;", "*(x + 1)");
207 }
208 
TEST(SourceCodeBuildersTest,BuildDotValue)209 TEST(SourceCodeBuildersTest, BuildDotValue) {
210   testBuilder(buildDot, "S x; x;", "x.");
211 }
212 
TEST(SourceCodeBuildersTest,BuildDotPointerDereference)213 TEST(SourceCodeBuildersTest, BuildDotPointerDereference) {
214   testBuilder(buildDot, "S *x; *x;", "x->");
215 }
216 
TEST(SourceCodeBuildersTest,BuildDotPointerDereferenceIgnoresParens)217 TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceIgnoresParens) {
218   testBuilder(buildDot, "S *x; *(x);", "x->");
219 }
220 
TEST(SourceCodeBuildersTest,BuildDotBinaryOperation)221 TEST(SourceCodeBuildersTest, BuildDotBinaryOperation) {
222   testBuilder(buildDot, "S x; x + x;", "(x + x).");
223 }
224 
TEST(SourceCodeBuildersTest,BuildDotPointerDereferenceExprWithParens)225 TEST(SourceCodeBuildersTest, BuildDotPointerDereferenceExprWithParens) {
226   testBuilder(buildDot, "S *x; *(x + 1);", "(x + 1)->");
227 }
228 
TEST(SourceCodeBuildersTest,BuildArrowPointer)229 TEST(SourceCodeBuildersTest, BuildArrowPointer) {
230   testBuilder(buildArrow, "S *x; x;", "x->");
231 }
232 
TEST(SourceCodeBuildersTest,BuildArrowValueAddress)233 TEST(SourceCodeBuildersTest, BuildArrowValueAddress) {
234   testBuilder(buildArrow, "S x; &x;", "x.");
235 }
236 
TEST(SourceCodeBuildersTest,BuildArrowValueAddressIgnoresParens)237 TEST(SourceCodeBuildersTest, BuildArrowValueAddressIgnoresParens) {
238   testBuilder(buildArrow, "S x; &(x);", "x.");
239 }
240 
TEST(SourceCodeBuildersTest,BuildArrowBinaryOperation)241 TEST(SourceCodeBuildersTest, BuildArrowBinaryOperation) {
242   testBuilder(buildArrow, "S *x; x + 1;", "(x + 1)->");
243 }
244 
TEST(SourceCodeBuildersTest,BuildArrowValueAddressWithParens)245 TEST(SourceCodeBuildersTest, BuildArrowValueAddressWithParens) {
246   testBuilder(buildArrow, "S x; &(true ? x : x);", "(true ? x : x).");
247 }
248 } // namespace
249