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