• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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