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