1 //===- unittest/Tooling/StencilTest.cpp -----------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
21 using namespace clang
;
22 using namespace transformer
;
23 using namespace ast_matchers
;
27 using ::llvm::HasValue
;
28 using ::llvm::StringError
;
29 using ::testing::AllOf
;
30 using ::testing::HasSubstr
;
31 using MatchResult
= MatchFinder::MatchResult
;
33 // Create a valid translation-unit from a statement.
34 static std::string
wrapSnippet(StringRef ExtraPreface
,
35 StringRef StatementCode
) {
36 constexpr char Preface
[] = R
"cc(
37 namespace N { class C {}; }
38 namespace { class AnonC {}; }
39 struct S { int Field; };
43 T* operator->() const;
47 template<class T> T desugar() { return T(); };
49 return (Preface
+ ExtraPreface
+ "auto stencil_test_snippet = []{" +
54 static DeclarationMatcher
wrapMatcher(const StatementMatcher
&Matcher
) {
55 return varDecl(hasName("stencil_test_snippet"),
56 hasDescendant(compoundStmt(hasAnySubstatement(Matcher
))));
60 // The AST unit from which `result` is built. We bundle it because it backs
61 // the result. Users are not expected to access it.
62 std::unique_ptr
<ASTUnit
> AstUnit
;
63 // The result to use in the test. References `ast_unit`.
67 // Matches `Matcher` against the statement `StatementCode` and returns the
68 // result. Handles putting the statement inside a function and modifying the
69 // matcher correspondingly. `Matcher` should match one of the statements in
70 // `StatementCode` exactly -- that is, produce exactly one match. However,
71 // `StatementCode` may contain other statements not described by `Matcher`.
72 // `ExtraPreface` (optionally) adds extra decls to the TU, before the code.
73 static std::optional
<TestMatch
> matchStmt(StringRef StatementCode
,
74 StatementMatcher Matcher
,
75 StringRef ExtraPreface
= "") {
76 auto AstUnit
= tooling::buildASTFromCodeWithArgs(
77 wrapSnippet(ExtraPreface
, StatementCode
), {"-Wno-unused-value"});
78 if (AstUnit
== nullptr) {
79 ADD_FAILURE() << "AST construction failed";
82 ASTContext
&Context
= AstUnit
->getASTContext();
83 auto Matches
= ast_matchers::match(wrapMatcher(Matcher
), Context
);
84 // We expect a single, exact match for the statement.
85 if (Matches
.size() != 1) {
86 ADD_FAILURE() << "Wrong number of matches: " << Matches
.size();
89 return TestMatch
{std::move(AstUnit
), MatchResult(Matches
[0], &Context
)};
92 class StencilTest
: public ::testing::Test
{
94 // Verifies that the given stencil fails when evaluated on a valid match
95 // result. Binds a statement to "stmt", a (non-member) ctor-initializer to
96 // "init", an expression to "expr" and a (nameless) declaration to "decl".
97 void testError(const Stencil
&Stencil
,
98 ::testing::Matcher
<std::string
> Matcher
) {
99 const std::string Snippet
= R
"cc(
107 auto StmtMatch
= matchStmt(
111 hasDeclaration(decl(hasDescendant(cxxCtorInitializer(
117 ASSERT_TRUE(StmtMatch
);
118 if (auto ResultOrErr
= Stencil
->eval(StmtMatch
->Result
)) {
119 ADD_FAILURE() << "Expected failure but succeeded: " << *ResultOrErr
;
121 auto Err
= llvm::handleErrors(ResultOrErr
.takeError(),
122 [&Matcher
](const StringError
&Err
) {
123 EXPECT_THAT(Err
.getMessage(), Matcher
);
126 ADD_FAILURE() << "Unhandled error: " << llvm::toString(std::move(Err
));
131 // Tests failures caused by references to unbound nodes. `unbound_id` is the
132 // id that will cause the failure.
133 void testUnboundNodeError(const Stencil
&Stencil
, StringRef UnboundId
) {
135 AllOf(HasSubstr(std::string(UnboundId
)), HasSubstr("not bound")));
139 TEST_F(StencilTest
, SingleStatement
) {
140 StringRef
Condition("C"), Then("T"), Else("E");
141 const std::string Snippet
= R
"cc(
147 auto StmtMatch
= matchStmt(
148 Snippet
, ifStmt(hasCondition(expr().bind(Condition
)),
149 hasThen(stmt().bind(Then
)), hasElse(stmt().bind(Else
))));
150 ASSERT_TRUE(StmtMatch
);
151 // Invert the if-then-else.
153 cat("if (!", node(std::string(Condition
)), ") ",
154 statement(std::string(Else
)), " else ", statement(std::string(Then
)));
155 EXPECT_THAT_EXPECTED(Stencil
->eval(StmtMatch
->Result
),
156 HasValue("if (!true) return 0; else return 1;"));
159 TEST_F(StencilTest
, UnboundNode
) {
160 const std::string Snippet
= R
"cc(
166 auto StmtMatch
= matchStmt(Snippet
, ifStmt(hasCondition(stmt().bind("a1")),
167 hasThen(stmt().bind("a2"))));
168 ASSERT_TRUE(StmtMatch
);
169 auto Stencil
= cat("if(!", node("a1"), ") ", node("UNBOUND"), ";");
170 auto ResultOrErr
= Stencil
->eval(StmtMatch
->Result
);
171 EXPECT_TRUE(llvm::errorToBool(ResultOrErr
.takeError()))
172 << "Expected unbound node, got " << *ResultOrErr
;
175 // Tests that a stencil with a single parameter (`Id`) evaluates to the expected
176 // string, when `Id` is bound to the expression-statement in `Snippet`.
177 void testExpr(StringRef Id
, StringRef Snippet
, const Stencil
&Stencil
,
178 StringRef Expected
) {
179 auto StmtMatch
= matchStmt(Snippet
, expr().bind(Id
));
180 ASSERT_TRUE(StmtMatch
);
181 EXPECT_THAT_EXPECTED(Stencil
->eval(StmtMatch
->Result
),
182 HasValue(std::string(Expected
)));
185 void testFailure(StringRef Id
, StringRef Snippet
, const Stencil
&Stencil
,
186 testing::Matcher
<std::string
> MessageMatcher
) {
187 auto StmtMatch
= matchStmt(Snippet
, expr().bind(Id
));
188 ASSERT_TRUE(StmtMatch
);
189 EXPECT_THAT_EXPECTED(Stencil
->eval(StmtMatch
->Result
),
190 Failed
<StringError
>(testing::Property(
191 &StringError::getMessage
, MessageMatcher
)));
194 TEST_F(StencilTest
, SelectionOp
) {
196 testExpr(Id
, "3;", cat(node(std::string(Id
))), "3");
199 TEST_F(StencilTest
, IfBoundOpBound
) {
201 testExpr(Id
, "3;", ifBound(Id
, cat("5"), cat("7")), "5");
204 TEST_F(StencilTest
, IfBoundOpUnbound
) {
206 testExpr(Id
, "3;", ifBound("other", cat("5"), cat("7")), "7");
209 static auto selectMatcher() {
210 // The `anything` matcher is not bound, to test for none of the cases
212 return expr(anyOf(integerLiteral().bind("int"), cxxBoolLiteral().bind("bool"),
213 floatLiteral().bind("float"), anything()));
216 static auto selectStencil() {
220 {"bool", cat("redundant")},
225 TEST_F(StencilTest
, SelectBoundChooseDetectedMatch
) {
226 std::string Input
= "3;";
227 auto StmtMatch
= matchStmt(Input
, selectMatcher());
228 ASSERT_TRUE(StmtMatch
);
229 EXPECT_THAT_EXPECTED(selectStencil()->eval(StmtMatch
->Result
),
230 HasValue(std::string("I")));
233 TEST_F(StencilTest
, SelectBoundChooseFirst
) {
234 std::string Input
= "true;";
235 auto StmtMatch
= matchStmt(Input
, selectMatcher());
236 ASSERT_TRUE(StmtMatch
);
237 EXPECT_THAT_EXPECTED(selectStencil()->eval(StmtMatch
->Result
),
238 HasValue(std::string("B")));
241 TEST_F(StencilTest
, SelectBoundDiesOnExhaustedCases
) {
242 std::string Input
= "\"string\";";
243 auto StmtMatch
= matchStmt(Input
, selectMatcher());
244 ASSERT_TRUE(StmtMatch
);
245 EXPECT_THAT_EXPECTED(
246 selectStencil()->eval(StmtMatch
->Result
),
247 Failed
<StringError
>(testing::Property(
248 &StringError::getMessage
,
249 AllOf(HasSubstr("selectBound failed"), HasSubstr("no default")))));
252 TEST_F(StencilTest
, SelectBoundSucceedsWithDefault
) {
253 std::string Input
= "\"string\";";
254 auto StmtMatch
= matchStmt(Input
, selectMatcher());
255 ASSERT_TRUE(StmtMatch
);
256 auto Stencil
= selectBound({{"int", cat("I")}}, cat("D"));
257 EXPECT_THAT_EXPECTED(Stencil
->eval(StmtMatch
->Result
),
258 HasValue(std::string("D")));
261 TEST_F(StencilTest
, ExpressionOpNoParens
) {
263 testExpr(Id
, "3;", expression(Id
), "3");
266 // Don't parenthesize a parens expression.
267 TEST_F(StencilTest
, ExpressionOpNoParensParens
) {
269 testExpr(Id
, "(3);", expression(Id
), "(3)");
272 TEST_F(StencilTest
, ExpressionOpBinaryOpParens
) {
274 testExpr(Id
, "3+4;", expression(Id
), "(3+4)");
277 // `expression` shares code with other ops, so we get sufficient coverage of the
278 // error handling code with this test. If that changes in the future, more error
279 // tests should be added.
280 TEST_F(StencilTest
, ExpressionOpUnbound
) {
282 testFailure(Id
, "3;", expression("ACACA"),
283 AllOf(HasSubstr("ACACA"), HasSubstr("not bound")));
286 TEST_F(StencilTest
, DerefPointer
) {
288 testExpr(Id
, "int *x; x;", deref(Id
), "*x");
291 TEST_F(StencilTest
, DerefBinOp
) {
293 testExpr(Id
, "int *x; x + 1;", deref(Id
), "*(x + 1)");
296 TEST_F(StencilTest
, DerefAddressExpr
) {
298 testExpr(Id
, "int x; &x;", deref(Id
), "x");
301 TEST_F(StencilTest
, AddressOfValue
) {
303 testExpr(Id
, "int x; x;", addressOf(Id
), "&x");
306 TEST_F(StencilTest
, AddressOfDerefExpr
) {
308 testExpr(Id
, "int *x; *x;", addressOf(Id
), "x");
311 TEST_F(StencilTest
, MaybeDerefValue
) {
313 testExpr(Id
, "int x; x;", maybeDeref(Id
), "x");
316 TEST_F(StencilTest
, MaybeDerefPointer
) {
318 testExpr(Id
, "int *x; x;", maybeDeref(Id
), "*x");
321 TEST_F(StencilTest
, MaybeDerefBinOp
) {
323 testExpr(Id
, "int *x; x + 1;", maybeDeref(Id
), "*(x + 1)");
326 TEST_F(StencilTest
, MaybeDerefAddressExpr
) {
328 testExpr(Id
, "int x; &x;", maybeDeref(Id
), "x");
331 TEST_F(StencilTest
, MaybeDerefSmartPointer
) {
333 std::string Snippet
= R
"cc(
334 std::unique_ptr<S> x;
337 testExpr(Id
, Snippet
, maybeDeref(Id
), "*x");
340 TEST_F(StencilTest
, MaybeDerefSmartPointerFromMemberExpr
) {
342 std::string Snippet
= "std::unique_ptr<S> x; x->Field;";
344 matchStmt(Snippet
, memberExpr(hasObjectExpression(expr().bind(Id
))));
345 ASSERT_TRUE(StmtMatch
);
346 const Stencil Stencil
= maybeDeref(Id
);
347 EXPECT_THAT_EXPECTED(Stencil
->eval(StmtMatch
->Result
), HasValue("*x"));
350 TEST_F(StencilTest
, MaybeAddressOfPointer
) {
352 testExpr(Id
, "int *x; x;", maybeAddressOf(Id
), "x");
355 TEST_F(StencilTest
, MaybeAddressOfValue
) {
357 testExpr(Id
, "int x; x;", addressOf(Id
), "&x");
360 TEST_F(StencilTest
, MaybeAddressOfBinOp
) {
362 testExpr(Id
, "int x; x + 1;", maybeAddressOf(Id
), "&(x + 1)");
365 TEST_F(StencilTest
, MaybeAddressOfDerefExpr
) {
367 testExpr(Id
, "int *x; *x;", addressOf(Id
), "x");
370 TEST_F(StencilTest
, MaybeAddressOfSmartPointer
) {
372 testExpr(Id
, "std::unique_ptr<S> x; x;", maybeAddressOf(Id
), "x");
375 TEST_F(StencilTest
, MaybeAddressOfSmartPointerFromMemberCall
) {
377 std::string Snippet
= "std::unique_ptr<S> x; x->Field;";
379 matchStmt(Snippet
, memberExpr(hasObjectExpression(expr().bind(Id
))));
380 ASSERT_TRUE(StmtMatch
);
381 const Stencil Stencil
= maybeAddressOf(Id
);
382 EXPECT_THAT_EXPECTED(Stencil
->eval(StmtMatch
->Result
), HasValue("x"));
385 TEST_F(StencilTest
, MaybeAddressOfSmartPointerDerefNoCancel
) {
387 testExpr(Id
, "std::unique_ptr<S> x; *x;", maybeAddressOf(Id
), "&*x");
390 TEST_F(StencilTest
, AccessOpValue
) {
391 StringRef Snippet
= R
"cc(
396 testExpr(Id
, Snippet
, access(Id
, "field"), "x.field");
399 TEST_F(StencilTest
, AccessOpValueExplicitText
) {
400 StringRef Snippet
= R
"cc(
405 testExpr(Id
, Snippet
, access(Id
, cat("field")), "x.field");
408 TEST_F(StencilTest
, AccessOpValueAddress
) {
409 StringRef Snippet
= R
"cc(
414 testExpr(Id
, Snippet
, access(Id
, "field"), "x.field");
417 TEST_F(StencilTest
, AccessOpPointer
) {
418 StringRef Snippet
= R
"cc(
423 testExpr(Id
, Snippet
, access(Id
, "field"), "x->field");
426 TEST_F(StencilTest
, AccessOpPointerDereference
) {
427 StringRef Snippet
= R
"cc(
432 testExpr(Id
, Snippet
, access(Id
, "field"), "x->field");
435 TEST_F(StencilTest
, AccessOpSmartPointer
) {
436 StringRef Snippet
= R
"cc(
437 std::unique_ptr<S> x;
441 testExpr(Id
, Snippet
, access(Id
, "field"), "x->field");
444 TEST_F(StencilTest
, AccessOpSmartPointerDereference
) {
445 StringRef Snippet
= R
"cc(
446 std::unique_ptr<S> x;
450 testExpr(Id
, Snippet
, access(Id
, "field"), "x->field");
453 TEST_F(StencilTest
, AccessOpSmartPointerMemberCall
) {
454 StringRef Snippet
= R
"cc(
455 std::unique_ptr<S> x;
460 matchStmt(Snippet
, memberExpr(hasObjectExpression(expr().bind(Id
))));
461 ASSERT_TRUE(StmtMatch
);
462 EXPECT_THAT_EXPECTED(access(Id
, "field")->eval(StmtMatch
->Result
),
463 HasValue("x->field"));
466 TEST_F(StencilTest
, AccessOpExplicitThis
) {
467 using clang::ast_matchers::hasObjectExpression
;
468 using clang::ast_matchers::memberExpr
;
470 // Set up the code so we can bind to a use of this.
471 StringRef Snippet
= R
"cc(
475 int foo() { return this->x; }
478 auto StmtMatch
= matchStmt(
480 traverse(TK_AsIs
, returnStmt(hasReturnValue(ignoringImplicit(memberExpr(
481 hasObjectExpression(expr().bind("obj"))))))));
482 ASSERT_TRUE(StmtMatch
);
483 const Stencil Stencil
= access("obj", "field");
484 EXPECT_THAT_EXPECTED(Stencil
->eval(StmtMatch
->Result
),
485 HasValue("this->field"));
488 TEST_F(StencilTest
, AccessOpImplicitThis
) {
489 using clang::ast_matchers::hasObjectExpression
;
490 using clang::ast_matchers::memberExpr
;
492 // Set up the code so we can bind to a use of (implicit) this.
493 StringRef Snippet
= R
"cc(
497 int foo() { return x; }
501 matchStmt(Snippet
, returnStmt(hasReturnValue(ignoringImplicit(memberExpr(
502 hasObjectExpression(expr().bind("obj")))))));
503 ASSERT_TRUE(StmtMatch
);
504 const Stencil Stencil
= access("obj", "field");
505 EXPECT_THAT_EXPECTED(Stencil
->eval(StmtMatch
->Result
), HasValue("field"));
508 TEST_F(StencilTest
, DescribeType
) {
509 std::string Snippet
= "int *x; x;";
510 std::string Expected
= "int *";
512 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("type"))));
513 ASSERT_TRUE(StmtMatch
);
514 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch
->Result
),
515 HasValue(std::string(Expected
)));
518 TEST_F(StencilTest
, DescribeSugaredType
) {
519 std::string Snippet
= "using Ty = int; Ty *x; x;";
520 std::string Expected
= "Ty *";
522 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("type"))));
523 ASSERT_TRUE(StmtMatch
);
524 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch
->Result
),
525 HasValue(std::string(Expected
)));
528 TEST_F(StencilTest
, DescribeDeclType
) {
529 std::string Snippet
= "S s; s;";
530 std::string Expected
= "S";
532 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("type"))));
533 ASSERT_TRUE(StmtMatch
);
534 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch
->Result
),
535 HasValue(std::string(Expected
)));
538 TEST_F(StencilTest
, DescribeQualifiedType
) {
539 std::string Snippet
= "N::C c; c;";
540 std::string Expected
= "N::C";
542 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("type"))));
543 ASSERT_TRUE(StmtMatch
);
544 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch
->Result
),
545 HasValue(std::string(Expected
)));
548 TEST_F(StencilTest
, DescribeUnqualifiedType
) {
549 std::string Snippet
= "using N::C; C c; c;";
550 std::string Expected
= "C";
552 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("type"))));
553 ASSERT_TRUE(StmtMatch
);
554 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch
->Result
),
555 HasValue(std::string(Expected
)));
558 TEST_F(StencilTest
, DescribeAnonNamespaceType
) {
559 std::string Snippet
= "auto c = desugar<AnonC>(); c;";
560 std::string Expected
= "(anonymous namespace)::AnonC";
562 matchStmt(Snippet
, declRefExpr(hasType(qualType().bind("type"))));
563 ASSERT_TRUE(StmtMatch
);
564 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch
->Result
),
565 HasValue(std::string(Expected
)));
568 TEST_F(StencilTest
, DescribeFunction
) {
569 std::string Snippet
= "int F(); F();";
570 std::string Expected
= "F";
571 auto StmtMatch
= matchStmt(Snippet
, callExpr(callee(namedDecl().bind("fn"))));
572 ASSERT_TRUE(StmtMatch
);
573 EXPECT_THAT_EXPECTED(describe("fn")->eval(StmtMatch
->Result
),
574 HasValue(std::string(Expected
)));
577 TEST_F(StencilTest
, DescribeImplicitOperator
) {
578 std::string Snippet
= "struct Tag {}; [](Tag){};";
579 std::string Expected
= "operator()";
580 auto StmtMatch
= matchStmt(
583 cxxMethodDecl(hasParameter(0, hasType(namedDecl(hasName("Tag")))))
585 ASSERT_TRUE(StmtMatch
);
586 EXPECT_THAT_EXPECTED(describe("fn")->eval(StmtMatch
->Result
),
587 HasValue(std::string(Expected
)));
590 TEST_F(StencilTest
, RunOp
) {
592 auto SimpleFn
= [Id
](const MatchResult
&R
) {
593 return std::string(R
.Nodes
.getNodeAs
<Stmt
>(Id
) != nullptr ? "Bound"
596 testExpr(Id
, "3;", run(SimpleFn
), "Bound");
599 TEST_F(StencilTest
, CatOfMacroRangeSucceeds
) {
600 StringRef Snippet
= R
"cpp(
602 double foo(double d);
606 matchStmt(Snippet
, callExpr(callee(functionDecl(hasName("foo"))),
608 hasArgument(0, expr().bind("arg"))));
609 ASSERT_TRUE(StmtMatch
);
610 Stencil S
= cat(node("arg"));
611 EXPECT_THAT_EXPECTED(S
->eval(StmtMatch
->Result
), HasValue("MACRO"));
614 TEST_F(StencilTest
, CatOfMacroArgRangeSucceeds
) {
615 StringRef Snippet
= R
"cpp(
616 #define MACRO(a, b) a + b
620 matchStmt(Snippet
, binaryOperator(hasRHS(expr().bind("rhs"))));
621 ASSERT_TRUE(StmtMatch
);
622 Stencil S
= cat(node("rhs"));
623 EXPECT_THAT_EXPECTED(S
->eval(StmtMatch
->Result
), HasValue("3"));
626 TEST_F(StencilTest
, CatOfMacroArgSubRangeSucceeds
) {
627 StringRef Snippet
= R
"cpp(
628 #define MACRO(a, b) a + b
630 MACRO(2, foo(3));)cpp";
632 auto StmtMatch
= matchStmt(
633 Snippet
, binaryOperator(hasRHS(callExpr(
634 callee(functionDecl(hasName("foo"))), argumentCountIs(1),
635 hasArgument(0, expr().bind("arg"))))));
636 ASSERT_TRUE(StmtMatch
);
637 Stencil S
= cat(node("arg"));
638 EXPECT_THAT_EXPECTED(S
->eval(StmtMatch
->Result
), HasValue("3"));
641 TEST_F(StencilTest
, CatOfInvalidRangeFails
) {
642 StringRef Snippet
= R
"cpp(
644 double foo(double d);
648 matchStmt(Snippet
, callExpr(callee(functionDecl(hasName("foo"))),
650 hasArgument(0, expr().bind("arg"))));
651 ASSERT_TRUE(StmtMatch
);
652 Stencil S
= cat(node("arg"));
653 Expected
<std::string
> Result
= S
->eval(StmtMatch
->Result
);
654 ASSERT_FALSE(Result
);
655 llvm::handleAllErrors(Result
.takeError(), [](const llvm::StringError
&E
) {
656 EXPECT_THAT(E
.getMessage(), AllOf(HasSubstr("selected range"),
657 HasSubstr("macro expansion")));
661 // The `StencilToStringTest` tests verify that the string representation of the
662 // stencil combinator matches (as best possible) the spelling of the
663 // combinator's construction. Exceptions include those combinators that have no
664 // explicit spelling (like raw text) and those supporting non-printable
665 // arguments (like `run`, `selection`).
667 TEST(StencilToStringTest
, RawTextOp
) {
668 auto S
= cat("foo bar baz");
669 StringRef Expected
= R
"("foo bar baz
")";
670 EXPECT_EQ(S
->toString(), Expected
);
673 TEST(StencilToStringTest
, RawTextOpEscaping
) {
674 auto S
= cat("foo \"bar\" baz\\n");
675 StringRef Expected
= R
"("foo
\"bar
\" baz
\\n
")";
676 EXPECT_EQ(S
->toString(), Expected
);
679 TEST(StencilToStringTest
, DescribeOp
) {
680 auto S
= describe("Id");
681 StringRef Expected
= R
"repr(describe("Id
"))repr";
682 EXPECT_EQ(S
->toString(), Expected
);
685 TEST(StencilToStringTest
, DebugPrintNodeOp
) {
686 auto S
= dPrint("Id");
687 StringRef Expected
= R
"repr(dPrint("Id
"))repr";
688 EXPECT_EQ(S
->toString(), Expected
);
691 TEST(StencilToStringTest
, ExpressionOp
) {
692 auto S
= expression("Id");
693 StringRef Expected
= R
"repr(expression("Id
"))repr";
694 EXPECT_EQ(S
->toString(), Expected
);
697 TEST(StencilToStringTest
, DerefOp
) {
698 auto S
= deref("Id");
699 StringRef Expected
= R
"repr(deref("Id
"))repr";
700 EXPECT_EQ(S
->toString(), Expected
);
703 TEST(StencilToStringTest
, AddressOfOp
) {
704 auto S
= addressOf("Id");
705 StringRef Expected
= R
"repr(addressOf("Id
"))repr";
706 EXPECT_EQ(S
->toString(), Expected
);
709 TEST(StencilToStringTest
, SelectionOp
) {
710 auto S1
= cat(node("node1"));
711 EXPECT_EQ(S1
->toString(), "selection(...)");
714 TEST(StencilToStringTest
, AccessOpText
) {
715 auto S
= access("Id", "memberData");
716 StringRef Expected
= R
"repr(access("Id
", "memberData
"))repr";
717 EXPECT_EQ(S
->toString(), Expected
);
720 TEST(StencilToStringTest
, AccessOpSelector
) {
721 auto S
= access("Id", cat(name("otherId")));
722 StringRef Expected
= R
"repr(access("Id
", selection(...)))repr";
723 EXPECT_EQ(S
->toString(), Expected
);
726 TEST(StencilToStringTest
, AccessOpStencil
) {
727 auto S
= access("Id", cat("foo_", "bar"));
728 StringRef Expected
= R
"repr(access("Id
", seq("foo_
", "bar
")))repr";
729 EXPECT_EQ(S
->toString(), Expected
);
732 TEST(StencilToStringTest
, IfBoundOp
) {
733 auto S
= ifBound("Id", cat("trueText"), access("exprId", "memberData"));
735 R
"repr(ifBound("Id
", "trueText
", access("exprId
", "memberData
")))repr";
736 EXPECT_EQ(S
->toString(), Expected
);
739 TEST(StencilToStringTest
, SelectBoundOp
) {
740 auto S
= selectBound({
744 StringRef Expected
= R
"repr(selectBound({{"int", "I
"}, {"float", "F
"}}))repr";
745 EXPECT_EQ(S
->toString(), Expected
);
748 TEST(StencilToStringTest
, SelectBoundOpWithOneCase
) {
749 auto S
= selectBound({{"int", cat("I")}});
750 StringRef Expected
= R
"repr(selectBound({{"int", "I
"}}))repr";
751 EXPECT_EQ(S
->toString(), Expected
);
754 TEST(StencilToStringTest
, SelectBoundOpWithDefault
) {
755 auto S
= selectBound({{"int", cat("I")}, {"float", cat("F")}}, cat("D"));
757 R
"cc(selectBound({{"int", "I
"}, {"float", "F
"}}, "D
"))cc";
758 EXPECT_EQ(S
->toString(), Expected
);
761 TEST(StencilToStringTest
, RunOp
) {
762 auto F1
= [](const MatchResult
&R
) { return "foo"; };
764 EXPECT_EQ(S1
->toString(), "run(...)");
767 TEST(StencilToStringTest
, Sequence
) {
768 auto S
= cat("foo", access("x", "m()"), "bar",
769 ifBound("x", cat("t"), access("e", "f")));
770 StringRef Expected
= R
"repr(seq("foo
", access("x
", "m()"), "bar
", )repr"
771 R
"repr(ifBound("x
", "t
", access("e
", "f
"))))repr";
772 EXPECT_EQ(S
->toString(), Expected
);
775 TEST(StencilToStringTest
, SequenceEmpty
) {
777 StringRef Expected
= "seq()";
778 EXPECT_EQ(S
->toString(), Expected
);
781 TEST(StencilToStringTest
, SequenceSingle
) {
783 StringRef Expected
= "\"foo\"";
784 EXPECT_EQ(S
->toString(), Expected
);
787 TEST(StencilToStringTest
, SequenceFromVector
) {
788 auto S
= catVector({cat("foo"), access("x", "m()"), cat("bar"),
789 ifBound("x", cat("t"), access("e", "f"))});
790 StringRef Expected
= R
"repr(seq("foo
", access("x
", "m()"), "bar
", )repr"
791 R
"repr(ifBound("x
", "t
", access("e
", "f
"))))repr";
792 EXPECT_EQ(S
->toString(), Expected
);