1 //===- unittest/Tooling/StencilTest.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/Stencil.h"
10 #include "clang/AST/ASTTypeTraits.h"
11 #include "clang/AST/Expr.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Tooling/FixIt.h"
14 #include "clang/Tooling/Tooling.h"
15 #include "llvm/Support/Error.h"
16 #include "llvm/Testing/Support/Error.h"
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
19
20 using namespace clang;
21 using namespace transformer;
22 using namespace ast_matchers;
23
24 namespace {
25 using ::llvm::Failed;
26 using ::llvm::HasValue;
27 using ::llvm::StringError;
28 using ::testing::AllOf;
29 using ::testing::HasSubstr;
30 using MatchResult = MatchFinder::MatchResult;
31
32 // Create a valid translation-unit from a statement.
wrapSnippet(StringRef ExtraPreface,StringRef StatementCode)33 static std::string wrapSnippet(StringRef ExtraPreface,
34 StringRef StatementCode) {
35 constexpr char Preface[] = R"cc(
36 namespace N { class C {}; }
37 namespace { class AnonC {}; }
38 struct S { int Field; };
39 namespace std {
40 template <typename T>
41 struct unique_ptr {
42 T* operator->() const;
43 T& operator*() const;
44 };
45 }
46 )cc";
47 return (Preface + ExtraPreface + "auto stencil_test_snippet = []{" +
48 StatementCode + "};")
49 .str();
50 }
51
wrapMatcher(const StatementMatcher & Matcher)52 static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) {
53 return varDecl(hasName("stencil_test_snippet"),
54 hasDescendant(compoundStmt(hasAnySubstatement(Matcher))));
55 }
56
57 struct TestMatch {
58 // The AST unit from which `result` is built. We bundle it because it backs
59 // the result. Users are not expected to access it.
60 std::unique_ptr<ASTUnit> AstUnit;
61 // The result to use in the test. References `ast_unit`.
62 MatchResult Result;
63 };
64
65 // Matches `Matcher` against the statement `StatementCode` and returns the
66 // result. Handles putting the statement inside a function and modifying the
67 // matcher correspondingly. `Matcher` should match one of the statements in
68 // `StatementCode` exactly -- that is, produce exactly one match. However,
69 // `StatementCode` may contain other statements not described by `Matcher`.
70 // `ExtraPreface` (optionally) adds extra decls to the TU, before the code.
matchStmt(StringRef StatementCode,StatementMatcher Matcher,StringRef ExtraPreface="")71 static llvm::Optional<TestMatch> matchStmt(StringRef StatementCode,
72 StatementMatcher Matcher,
73 StringRef ExtraPreface = "") {
74 auto AstUnit = tooling::buildASTFromCodeWithArgs(
75 wrapSnippet(ExtraPreface, StatementCode), {"-Wno-unused-value"});
76 if (AstUnit == nullptr) {
77 ADD_FAILURE() << "AST construction failed";
78 return llvm::None;
79 }
80 ASTContext &Context = AstUnit->getASTContext();
81 auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context);
82 // We expect a single, exact match for the statement.
83 if (Matches.size() != 1) {
84 ADD_FAILURE() << "Wrong number of matches: " << Matches.size();
85 return llvm::None;
86 }
87 return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)};
88 }
89
90 class StencilTest : public ::testing::Test {
91 protected:
92 // Verifies that the given stencil fails when evaluated on a valid match
93 // result. Binds a statement to "stmt", a (non-member) ctor-initializer to
94 // "init", an expression to "expr" and a (nameless) declaration to "decl".
testError(const Stencil & Stencil,::testing::Matcher<std::string> Matcher)95 void testError(const Stencil &Stencil,
96 ::testing::Matcher<std::string> Matcher) {
97 const std::string Snippet = R"cc(
98 struct A {};
99 class F : public A {
100 public:
101 F(int) {}
102 };
103 F(1);
104 )cc";
105 auto StmtMatch = matchStmt(
106 Snippet,
107 stmt(hasDescendant(
108 cxxConstructExpr(
109 hasDeclaration(decl(hasDescendant(cxxCtorInitializer(
110 isBaseInitializer())
111 .bind("init")))
112 .bind("decl")))
113 .bind("expr")))
114 .bind("stmt"));
115 ASSERT_TRUE(StmtMatch);
116 if (auto ResultOrErr = Stencil->eval(StmtMatch->Result)) {
117 ADD_FAILURE() << "Expected failure but succeeded: " << *ResultOrErr;
118 } else {
119 auto Err = llvm::handleErrors(ResultOrErr.takeError(),
120 [&Matcher](const StringError &Err) {
121 EXPECT_THAT(Err.getMessage(), Matcher);
122 });
123 if (Err) {
124 ADD_FAILURE() << "Unhandled error: " << llvm::toString(std::move(Err));
125 }
126 }
127 }
128
129 // Tests failures caused by references to unbound nodes. `unbound_id` is the
130 // id that will cause the failure.
testUnboundNodeError(const Stencil & Stencil,StringRef UnboundId)131 void testUnboundNodeError(const Stencil &Stencil, StringRef UnboundId) {
132 testError(Stencil,
133 AllOf(HasSubstr(std::string(UnboundId)), HasSubstr("not bound")));
134 }
135 };
136
TEST_F(StencilTest,SingleStatement)137 TEST_F(StencilTest, SingleStatement) {
138 StringRef Condition("C"), Then("T"), Else("E");
139 const std::string Snippet = R"cc(
140 if (true)
141 return 1;
142 else
143 return 0;
144 )cc";
145 auto StmtMatch = matchStmt(
146 Snippet, ifStmt(hasCondition(expr().bind(Condition)),
147 hasThen(stmt().bind(Then)), hasElse(stmt().bind(Else))));
148 ASSERT_TRUE(StmtMatch);
149 // Invert the if-then-else.
150 auto Stencil =
151 cat("if (!", node(std::string(Condition)), ") ",
152 statement(std::string(Else)), " else ", statement(std::string(Then)));
153 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result),
154 HasValue("if (!true) return 0; else return 1;"));
155 }
156
TEST_F(StencilTest,UnboundNode)157 TEST_F(StencilTest, UnboundNode) {
158 const std::string Snippet = R"cc(
159 if (true)
160 return 1;
161 else
162 return 0;
163 )cc";
164 auto StmtMatch = matchStmt(Snippet, ifStmt(hasCondition(stmt().bind("a1")),
165 hasThen(stmt().bind("a2"))));
166 ASSERT_TRUE(StmtMatch);
167 auto Stencil = cat("if(!", node("a1"), ") ", node("UNBOUND"), ";");
168 auto ResultOrErr = Stencil->eval(StmtMatch->Result);
169 EXPECT_TRUE(llvm::errorToBool(ResultOrErr.takeError()))
170 << "Expected unbound node, got " << *ResultOrErr;
171 }
172
173 // Tests that a stencil with a single parameter (`Id`) evaluates to the expected
174 // string, when `Id` is bound to the expression-statement in `Snippet`.
testExpr(StringRef Id,StringRef Snippet,const Stencil & Stencil,StringRef Expected)175 void testExpr(StringRef Id, StringRef Snippet, const Stencil &Stencil,
176 StringRef Expected) {
177 auto StmtMatch = matchStmt(Snippet, expr().bind(Id));
178 ASSERT_TRUE(StmtMatch);
179 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result),
180 HasValue(std::string(Expected)));
181 }
182
testFailure(StringRef Id,StringRef Snippet,const Stencil & Stencil,testing::Matcher<std::string> MessageMatcher)183 void testFailure(StringRef Id, StringRef Snippet, const Stencil &Stencil,
184 testing::Matcher<std::string> MessageMatcher) {
185 auto StmtMatch = matchStmt(Snippet, expr().bind(Id));
186 ASSERT_TRUE(StmtMatch);
187 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result),
188 Failed<StringError>(testing::Property(
189 &StringError::getMessage, MessageMatcher)));
190 }
191
TEST_F(StencilTest,SelectionOp)192 TEST_F(StencilTest, SelectionOp) {
193 StringRef Id = "id";
194 testExpr(Id, "3;", cat(node(std::string(Id))), "3");
195 }
196
TEST_F(StencilTest,IfBoundOpBound)197 TEST_F(StencilTest, IfBoundOpBound) {
198 StringRef Id = "id";
199 testExpr(Id, "3;", ifBound(Id, cat("5"), cat("7")), "5");
200 }
201
TEST_F(StencilTest,IfBoundOpUnbound)202 TEST_F(StencilTest, IfBoundOpUnbound) {
203 StringRef Id = "id";
204 testExpr(Id, "3;", ifBound("other", cat("5"), cat("7")), "7");
205 }
206
selectMatcher()207 static auto selectMatcher() {
208 // The `anything` matcher is not bound, to test for none of the cases
209 // matching.
210 return expr(anyOf(integerLiteral().bind("int"), cxxBoolLiteral().bind("bool"),
211 floatLiteral().bind("float"), anything()));
212 }
213
selectStencil()214 static auto selectStencil() {
215 return selectBound({
216 {"int", cat("I")},
217 {"bool", cat("B")},
218 {"bool", cat("redundant")},
219 {"float", cat("F")},
220 });
221 }
222
TEST_F(StencilTest,SelectBoundChooseDetectedMatch)223 TEST_F(StencilTest, SelectBoundChooseDetectedMatch) {
224 std::string Input = "3;";
225 auto StmtMatch = matchStmt(Input, selectMatcher());
226 ASSERT_TRUE(StmtMatch);
227 EXPECT_THAT_EXPECTED(selectStencil()->eval(StmtMatch->Result),
228 HasValue(std::string("I")));
229 }
230
TEST_F(StencilTest,SelectBoundChooseFirst)231 TEST_F(StencilTest, SelectBoundChooseFirst) {
232 std::string Input = "true;";
233 auto StmtMatch = matchStmt(Input, selectMatcher());
234 ASSERT_TRUE(StmtMatch);
235 EXPECT_THAT_EXPECTED(selectStencil()->eval(StmtMatch->Result),
236 HasValue(std::string("B")));
237 }
238
TEST_F(StencilTest,SelectBoundDiesOnExhaustedCases)239 TEST_F(StencilTest, SelectBoundDiesOnExhaustedCases) {
240 std::string Input = "\"string\";";
241 auto StmtMatch = matchStmt(Input, selectMatcher());
242 ASSERT_TRUE(StmtMatch);
243 EXPECT_THAT_EXPECTED(
244 selectStencil()->eval(StmtMatch->Result),
245 Failed<StringError>(testing::Property(
246 &StringError::getMessage,
247 AllOf(HasSubstr("selectBound failed"), HasSubstr("no default")))));
248 }
249
TEST_F(StencilTest,SelectBoundSucceedsWithDefault)250 TEST_F(StencilTest, SelectBoundSucceedsWithDefault) {
251 std::string Input = "\"string\";";
252 auto StmtMatch = matchStmt(Input, selectMatcher());
253 ASSERT_TRUE(StmtMatch);
254 auto Stencil = selectBound({{"int", cat("I")}}, cat("D"));
255 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result),
256 HasValue(std::string("D")));
257 }
258
TEST_F(StencilTest,ExpressionOpNoParens)259 TEST_F(StencilTest, ExpressionOpNoParens) {
260 StringRef Id = "id";
261 testExpr(Id, "3;", expression(Id), "3");
262 }
263
264 // Don't parenthesize a parens expression.
TEST_F(StencilTest,ExpressionOpNoParensParens)265 TEST_F(StencilTest, ExpressionOpNoParensParens) {
266 StringRef Id = "id";
267 testExpr(Id, "(3);", expression(Id), "(3)");
268 }
269
TEST_F(StencilTest,ExpressionOpBinaryOpParens)270 TEST_F(StencilTest, ExpressionOpBinaryOpParens) {
271 StringRef Id = "id";
272 testExpr(Id, "3+4;", expression(Id), "(3+4)");
273 }
274
275 // `expression` shares code with other ops, so we get sufficient coverage of the
276 // error handling code with this test. If that changes in the future, more error
277 // tests should be added.
TEST_F(StencilTest,ExpressionOpUnbound)278 TEST_F(StencilTest, ExpressionOpUnbound) {
279 StringRef Id = "id";
280 testFailure(Id, "3;", expression("ACACA"),
281 AllOf(HasSubstr("ACACA"), HasSubstr("not bound")));
282 }
283
TEST_F(StencilTest,DerefPointer)284 TEST_F(StencilTest, DerefPointer) {
285 StringRef Id = "id";
286 testExpr(Id, "int *x; x;", deref(Id), "*x");
287 }
288
TEST_F(StencilTest,DerefBinOp)289 TEST_F(StencilTest, DerefBinOp) {
290 StringRef Id = "id";
291 testExpr(Id, "int *x; x + 1;", deref(Id), "*(x + 1)");
292 }
293
TEST_F(StencilTest,DerefAddressExpr)294 TEST_F(StencilTest, DerefAddressExpr) {
295 StringRef Id = "id";
296 testExpr(Id, "int x; &x;", deref(Id), "x");
297 }
298
TEST_F(StencilTest,AddressOfValue)299 TEST_F(StencilTest, AddressOfValue) {
300 StringRef Id = "id";
301 testExpr(Id, "int x; x;", addressOf(Id), "&x");
302 }
303
TEST_F(StencilTest,AddressOfDerefExpr)304 TEST_F(StencilTest, AddressOfDerefExpr) {
305 StringRef Id = "id";
306 testExpr(Id, "int *x; *x;", addressOf(Id), "x");
307 }
308
TEST_F(StencilTest,MaybeDerefValue)309 TEST_F(StencilTest, MaybeDerefValue) {
310 StringRef Id = "id";
311 testExpr(Id, "int x; x;", maybeDeref(Id), "x");
312 }
313
TEST_F(StencilTest,MaybeDerefPointer)314 TEST_F(StencilTest, MaybeDerefPointer) {
315 StringRef Id = "id";
316 testExpr(Id, "int *x; x;", maybeDeref(Id), "*x");
317 }
318
TEST_F(StencilTest,MaybeDerefBinOp)319 TEST_F(StencilTest, MaybeDerefBinOp) {
320 StringRef Id = "id";
321 testExpr(Id, "int *x; x + 1;", maybeDeref(Id), "*(x + 1)");
322 }
323
TEST_F(StencilTest,MaybeDerefAddressExpr)324 TEST_F(StencilTest, MaybeDerefAddressExpr) {
325 StringRef Id = "id";
326 testExpr(Id, "int x; &x;", maybeDeref(Id), "x");
327 }
328
TEST_F(StencilTest,MaybeDerefSmartPointer)329 TEST_F(StencilTest, MaybeDerefSmartPointer) {
330 StringRef Id = "id";
331 std::string Snippet = R"cc(
332 std::unique_ptr<S> x;
333 x;
334 )cc";
335 testExpr(Id, Snippet, maybeDeref(Id), "*x");
336 }
337
TEST_F(StencilTest,MaybeDerefSmartPointerFromMemberExpr)338 TEST_F(StencilTest, MaybeDerefSmartPointerFromMemberExpr) {
339 StringRef Id = "id";
340 std::string Snippet = "std::unique_ptr<S> x; x->Field;";
341 auto StmtMatch =
342 matchStmt(Snippet, memberExpr(hasObjectExpression(expr().bind(Id))));
343 ASSERT_TRUE(StmtMatch);
344 const Stencil Stencil = maybeDeref(Id);
345 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), HasValue("*x"));
346 }
347
TEST_F(StencilTest,MaybeAddressOfPointer)348 TEST_F(StencilTest, MaybeAddressOfPointer) {
349 StringRef Id = "id";
350 testExpr(Id, "int *x; x;", maybeAddressOf(Id), "x");
351 }
352
TEST_F(StencilTest,MaybeAddressOfValue)353 TEST_F(StencilTest, MaybeAddressOfValue) {
354 StringRef Id = "id";
355 testExpr(Id, "int x; x;", addressOf(Id), "&x");
356 }
357
TEST_F(StencilTest,MaybeAddressOfBinOp)358 TEST_F(StencilTest, MaybeAddressOfBinOp) {
359 StringRef Id = "id";
360 testExpr(Id, "int x; x + 1;", maybeAddressOf(Id), "&(x + 1)");
361 }
362
TEST_F(StencilTest,MaybeAddressOfDerefExpr)363 TEST_F(StencilTest, MaybeAddressOfDerefExpr) {
364 StringRef Id = "id";
365 testExpr(Id, "int *x; *x;", addressOf(Id), "x");
366 }
367
TEST_F(StencilTest,MaybeAddressOfSmartPointer)368 TEST_F(StencilTest, MaybeAddressOfSmartPointer) {
369 StringRef Id = "id";
370 testExpr(Id, "std::unique_ptr<S> x; x;", maybeAddressOf(Id), "x");
371 }
372
TEST_F(StencilTest,MaybeAddressOfSmartPointerFromMemberCall)373 TEST_F(StencilTest, MaybeAddressOfSmartPointerFromMemberCall) {
374 StringRef Id = "id";
375 std::string Snippet = "std::unique_ptr<S> x; x->Field;";
376 auto StmtMatch =
377 matchStmt(Snippet, memberExpr(hasObjectExpression(expr().bind(Id))));
378 ASSERT_TRUE(StmtMatch);
379 const Stencil Stencil = maybeAddressOf(Id);
380 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), HasValue("x"));
381 }
382
TEST_F(StencilTest,MaybeAddressOfSmartPointerDerefNoCancel)383 TEST_F(StencilTest, MaybeAddressOfSmartPointerDerefNoCancel) {
384 StringRef Id = "id";
385 testExpr(Id, "std::unique_ptr<S> x; *x;", maybeAddressOf(Id), "&*x");
386 }
387
TEST_F(StencilTest,AccessOpValue)388 TEST_F(StencilTest, AccessOpValue) {
389 StringRef Snippet = R"cc(
390 S x;
391 x;
392 )cc";
393 StringRef Id = "id";
394 testExpr(Id, Snippet, access(Id, "field"), "x.field");
395 }
396
TEST_F(StencilTest,AccessOpValueExplicitText)397 TEST_F(StencilTest, AccessOpValueExplicitText) {
398 StringRef Snippet = R"cc(
399 S x;
400 x;
401 )cc";
402 StringRef Id = "id";
403 testExpr(Id, Snippet, access(Id, cat("field")), "x.field");
404 }
405
TEST_F(StencilTest,AccessOpValueAddress)406 TEST_F(StencilTest, AccessOpValueAddress) {
407 StringRef Snippet = R"cc(
408 S x;
409 &x;
410 )cc";
411 StringRef Id = "id";
412 testExpr(Id, Snippet, access(Id, "field"), "x.field");
413 }
414
TEST_F(StencilTest,AccessOpPointer)415 TEST_F(StencilTest, AccessOpPointer) {
416 StringRef Snippet = R"cc(
417 S *x;
418 x;
419 )cc";
420 StringRef Id = "id";
421 testExpr(Id, Snippet, access(Id, "field"), "x->field");
422 }
423
TEST_F(StencilTest,AccessOpPointerDereference)424 TEST_F(StencilTest, AccessOpPointerDereference) {
425 StringRef Snippet = R"cc(
426 S *x;
427 *x;
428 )cc";
429 StringRef Id = "id";
430 testExpr(Id, Snippet, access(Id, "field"), "x->field");
431 }
432
TEST_F(StencilTest,AccessOpSmartPointer)433 TEST_F(StencilTest, AccessOpSmartPointer) {
434 StringRef Snippet = R"cc(
435 std::unique_ptr<S> x;
436 x;
437 )cc";
438 StringRef Id = "id";
439 testExpr(Id, Snippet, access(Id, "field"), "x->field");
440 }
441
TEST_F(StencilTest,AccessOpSmartPointerDereference)442 TEST_F(StencilTest, AccessOpSmartPointerDereference) {
443 StringRef Snippet = R"cc(
444 std::unique_ptr<S> x;
445 *x;
446 )cc";
447 StringRef Id = "id";
448 testExpr(Id, Snippet, access(Id, "field"), "x->field");
449 }
450
TEST_F(StencilTest,AccessOpSmartPointerMemberCall)451 TEST_F(StencilTest, AccessOpSmartPointerMemberCall) {
452 StringRef Snippet = R"cc(
453 std::unique_ptr<S> x;
454 x->Field;
455 )cc";
456 StringRef Id = "id";
457 auto StmtMatch =
458 matchStmt(Snippet, memberExpr(hasObjectExpression(expr().bind(Id))));
459 ASSERT_TRUE(StmtMatch);
460 EXPECT_THAT_EXPECTED(access(Id, "field")->eval(StmtMatch->Result),
461 HasValue("x->field"));
462 }
463
TEST_F(StencilTest,AccessOpExplicitThis)464 TEST_F(StencilTest, AccessOpExplicitThis) {
465 using clang::ast_matchers::hasObjectExpression;
466 using clang::ast_matchers::memberExpr;
467
468 // Set up the code so we can bind to a use of this.
469 StringRef Snippet = R"cc(
470 class C {
471 public:
472 int x;
473 int foo() { return this->x; }
474 };
475 )cc";
476 auto StmtMatch = matchStmt(
477 Snippet,
478 traverse(TK_AsIs, returnStmt(hasReturnValue(ignoringImplicit(memberExpr(
479 hasObjectExpression(expr().bind("obj"))))))));
480 ASSERT_TRUE(StmtMatch);
481 const Stencil Stencil = access("obj", "field");
482 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result),
483 HasValue("this->field"));
484 }
485
TEST_F(StencilTest,AccessOpImplicitThis)486 TEST_F(StencilTest, AccessOpImplicitThis) {
487 using clang::ast_matchers::hasObjectExpression;
488 using clang::ast_matchers::memberExpr;
489
490 // Set up the code so we can bind to a use of (implicit) this.
491 StringRef Snippet = R"cc(
492 class C {
493 public:
494 int x;
495 int foo() { return x; }
496 };
497 )cc";
498 auto StmtMatch =
499 matchStmt(Snippet, returnStmt(hasReturnValue(ignoringImplicit(memberExpr(
500 hasObjectExpression(expr().bind("obj")))))));
501 ASSERT_TRUE(StmtMatch);
502 const Stencil Stencil = access("obj", "field");
503 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), HasValue("field"));
504 }
505
TEST_F(StencilTest,DescribeType)506 TEST_F(StencilTest, DescribeType) {
507 std::string Snippet = "int *x; x;";
508 std::string Expected = "int *";
509 auto StmtMatch =
510 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("type"))));
511 ASSERT_TRUE(StmtMatch);
512 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch->Result),
513 HasValue(std::string(Expected)));
514 }
515
TEST_F(StencilTest,DescribeSugaredType)516 TEST_F(StencilTest, DescribeSugaredType) {
517 std::string Snippet = "using Ty = int; Ty *x; x;";
518 std::string Expected = "Ty *";
519 auto StmtMatch =
520 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("type"))));
521 ASSERT_TRUE(StmtMatch);
522 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch->Result),
523 HasValue(std::string(Expected)));
524 }
525
TEST_F(StencilTest,DescribeDeclType)526 TEST_F(StencilTest, DescribeDeclType) {
527 std::string Snippet = "S s; s;";
528 std::string Expected = "S";
529 auto StmtMatch =
530 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("type"))));
531 ASSERT_TRUE(StmtMatch);
532 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch->Result),
533 HasValue(std::string(Expected)));
534 }
535
TEST_F(StencilTest,DescribeQualifiedType)536 TEST_F(StencilTest, DescribeQualifiedType) {
537 std::string Snippet = "N::C c; c;";
538 std::string Expected = "N::C";
539 auto StmtMatch =
540 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("type"))));
541 ASSERT_TRUE(StmtMatch);
542 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch->Result),
543 HasValue(std::string(Expected)));
544 }
545
TEST_F(StencilTest,DescribeUnqualifiedType)546 TEST_F(StencilTest, DescribeUnqualifiedType) {
547 std::string Snippet = "using N::C; C c; c;";
548 std::string Expected = "N::C";
549 auto StmtMatch =
550 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("type"))));
551 ASSERT_TRUE(StmtMatch);
552 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch->Result),
553 HasValue(std::string(Expected)));
554 }
555
TEST_F(StencilTest,DescribeAnonNamespaceType)556 TEST_F(StencilTest, DescribeAnonNamespaceType) {
557 std::string Snippet = "AnonC c; c;";
558 std::string Expected = "(anonymous namespace)::AnonC";
559 auto StmtMatch =
560 matchStmt(Snippet, declRefExpr(hasType(qualType().bind("type"))));
561 ASSERT_TRUE(StmtMatch);
562 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch->Result),
563 HasValue(std::string(Expected)));
564 }
565
TEST_F(StencilTest,RunOp)566 TEST_F(StencilTest, RunOp) {
567 StringRef Id = "id";
568 auto SimpleFn = [Id](const MatchResult &R) {
569 return std::string(R.Nodes.getNodeAs<Stmt>(Id) != nullptr ? "Bound"
570 : "Unbound");
571 };
572 testExpr(Id, "3;", run(SimpleFn), "Bound");
573 }
574
TEST_F(StencilTest,CatOfMacroRangeSucceeds)575 TEST_F(StencilTest, CatOfMacroRangeSucceeds) {
576 StringRef Snippet = R"cpp(
577 #define MACRO 3.77
578 double foo(double d);
579 foo(MACRO);)cpp";
580
581 auto StmtMatch =
582 matchStmt(Snippet, callExpr(callee(functionDecl(hasName("foo"))),
583 argumentCountIs(1),
584 hasArgument(0, expr().bind("arg"))));
585 ASSERT_TRUE(StmtMatch);
586 Stencil S = cat(node("arg"));
587 EXPECT_THAT_EXPECTED(S->eval(StmtMatch->Result), HasValue("MACRO"));
588 }
589
TEST_F(StencilTest,CatOfMacroArgRangeSucceeds)590 TEST_F(StencilTest, CatOfMacroArgRangeSucceeds) {
591 StringRef Snippet = R"cpp(
592 #define MACRO(a, b) a + b
593 MACRO(2, 3);)cpp";
594
595 auto StmtMatch =
596 matchStmt(Snippet, binaryOperator(hasRHS(expr().bind("rhs"))));
597 ASSERT_TRUE(StmtMatch);
598 Stencil S = cat(node("rhs"));
599 EXPECT_THAT_EXPECTED(S->eval(StmtMatch->Result), HasValue("3"));
600 }
601
TEST_F(StencilTest,CatOfMacroArgSubRangeSucceeds)602 TEST_F(StencilTest, CatOfMacroArgSubRangeSucceeds) {
603 StringRef Snippet = R"cpp(
604 #define MACRO(a, b) a + b
605 int foo(int);
606 MACRO(2, foo(3));)cpp";
607
608 auto StmtMatch = matchStmt(
609 Snippet, binaryOperator(hasRHS(callExpr(
610 callee(functionDecl(hasName("foo"))), argumentCountIs(1),
611 hasArgument(0, expr().bind("arg"))))));
612 ASSERT_TRUE(StmtMatch);
613 Stencil S = cat(node("arg"));
614 EXPECT_THAT_EXPECTED(S->eval(StmtMatch->Result), HasValue("3"));
615 }
616
TEST_F(StencilTest,CatOfInvalidRangeFails)617 TEST_F(StencilTest, CatOfInvalidRangeFails) {
618 StringRef Snippet = R"cpp(
619 #define MACRO (3.77)
620 double foo(double d);
621 foo(MACRO);)cpp";
622
623 auto StmtMatch =
624 matchStmt(Snippet, callExpr(callee(functionDecl(hasName("foo"))),
625 argumentCountIs(1),
626 hasArgument(0, expr().bind("arg"))));
627 ASSERT_TRUE(StmtMatch);
628 Stencil S = cat(node("arg"));
629 Expected<std::string> Result = S->eval(StmtMatch->Result);
630 ASSERT_FALSE(Result);
631 llvm::handleAllErrors(Result.takeError(), [](const llvm::StringError &E) {
632 EXPECT_THAT(E.getMessage(), AllOf(HasSubstr("selected range"),
633 HasSubstr("macro expansion")));
634 });
635 }
636
637 // The `StencilToStringTest` tests verify that the string representation of the
638 // stencil combinator matches (as best possible) the spelling of the
639 // combinator's construction. Exceptions include those combinators that have no
640 // explicit spelling (like raw text) and those supporting non-printable
641 // arguments (like `run`, `selection`).
642
TEST(StencilToStringTest,RawTextOp)643 TEST(StencilToStringTest, RawTextOp) {
644 auto S = cat("foo bar baz");
645 StringRef Expected = R"("foo bar baz")";
646 EXPECT_EQ(S->toString(), Expected);
647 }
648
TEST(StencilToStringTest,RawTextOpEscaping)649 TEST(StencilToStringTest, RawTextOpEscaping) {
650 auto S = cat("foo \"bar\" baz\\n");
651 StringRef Expected = R"("foo \"bar\" baz\\n")";
652 EXPECT_EQ(S->toString(), Expected);
653 }
654
655 TEST(StencilToStringTest, DescribeOp) {
656 auto S = describe("Id");
657 StringRef Expected = R"repr(describe("Id"))repr";
658 EXPECT_EQ(S->toString(), Expected);
659 }
660
661 TEST(StencilToStringTest, DebugPrintNodeOp) {
662 auto S = dPrint("Id");
663 StringRef Expected = R"repr(dPrint("Id"))repr";
664 EXPECT_EQ(S->toString(), Expected);
665 }
666
667 TEST(StencilToStringTest, ExpressionOp) {
668 auto S = expression("Id");
669 StringRef Expected = R"repr(expression("Id"))repr";
670 EXPECT_EQ(S->toString(), Expected);
671 }
672
673 TEST(StencilToStringTest, DerefOp) {
674 auto S = deref("Id");
675 StringRef Expected = R"repr(deref("Id"))repr";
676 EXPECT_EQ(S->toString(), Expected);
677 }
678
679 TEST(StencilToStringTest, AddressOfOp) {
680 auto S = addressOf("Id");
681 StringRef Expected = R"repr(addressOf("Id"))repr";
682 EXPECT_EQ(S->toString(), Expected);
683 }
684
685 TEST(StencilToStringTest, SelectionOp) {
686 auto S1 = cat(node("node1"));
687 EXPECT_EQ(S1->toString(), "selection(...)");
688 }
689
690 TEST(StencilToStringTest, AccessOpText) {
691 auto S = access("Id", "memberData");
692 StringRef Expected = R"repr(access("Id", "memberData"))repr";
693 EXPECT_EQ(S->toString(), Expected);
694 }
695
696 TEST(StencilToStringTest, AccessOpSelector) {
697 auto S = access("Id", cat(name("otherId")));
698 StringRef Expected = R"repr(access("Id", selection(...)))repr";
699 EXPECT_EQ(S->toString(), Expected);
700 }
701
702 TEST(StencilToStringTest, AccessOpStencil) {
703 auto S = access("Id", cat("foo_", "bar"));
704 StringRef Expected = R"repr(access("Id", seq("foo_", "bar")))repr";
705 EXPECT_EQ(S->toString(), Expected);
706 }
707
708 TEST(StencilToStringTest, IfBoundOp) {
709 auto S = ifBound("Id", cat("trueText"), access("exprId", "memberData"));
710 StringRef Expected =
711 R"repr(ifBound("Id", "trueText", access("exprId", "memberData")))repr";
712 EXPECT_EQ(S->toString(), Expected);
713 }
714
715 TEST(StencilToStringTest, SelectBoundOp) {
716 auto S = selectBound({
717 {"int", cat("I")},
718 {"float", cat("F")},
719 });
720 StringRef Expected = R"repr(selectBound({{"int", "I"}, {"float", "F"}}))repr";
721 EXPECT_EQ(S->toString(), Expected);
722 }
723
724 TEST(StencilToStringTest, SelectBoundOpWithOneCase) {
725 auto S = selectBound({{"int", cat("I")}});
726 StringRef Expected = R"repr(selectBound({{"int", "I"}}))repr";
727 EXPECT_EQ(S->toString(), Expected);
728 }
729
730 TEST(StencilToStringTest, SelectBoundOpWithDefault) {
731 auto S = selectBound({{"int", cat("I")}, {"float", cat("F")}}, cat("D"));
732 StringRef Expected =
733 R"cc(selectBound({{"int", "I"}, {"float", "F"}}, "D"))cc";
734 EXPECT_EQ(S->toString(), Expected);
735 }
736
737 TEST(StencilToStringTest, RunOp) {
738 auto F1 = [](const MatchResult &R) { return "foo"; };
739 auto S1 = run(F1);
740 EXPECT_EQ(S1->toString(), "run(...)");
741 }
742
743 TEST(StencilToStringTest, Sequence) {
744 auto S = cat("foo", access("x", "m()"), "bar",
745 ifBound("x", cat("t"), access("e", "f")));
746 StringRef Expected = R"repr(seq("foo", access("x", "m()"), "bar", )repr"
747 R"repr(ifBound("x", "t", access("e", "f"))))repr";
748 EXPECT_EQ(S->toString(), Expected);
749 }
750
751 TEST(StencilToStringTest, SequenceEmpty) {
752 auto S = cat();
753 StringRef Expected = "seq()";
754 EXPECT_EQ(S->toString(), Expected);
755 }
756
757 TEST(StencilToStringTest, SequenceSingle) {
758 auto S = cat("foo");
759 StringRef Expected = "\"foo\"";
760 EXPECT_EQ(S->toString(), Expected);
761 }
762
TEST(StencilToStringTest,SequenceFromVector)763 TEST(StencilToStringTest, SequenceFromVector) {
764 auto S = catVector({cat("foo"), access("x", "m()"), cat("bar"),
765 ifBound("x", cat("t"), access("e", "f"))});
766 StringRef Expected = R"repr(seq("foo", access("x", "m()"), "bar", )repr"
767 R"repr(ifBound("x", "t", access("e", "f"))))repr";
768 EXPECT_EQ(S->toString(), Expected);
769 }
770 } // namespace
771