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