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 #include <optional> 20 21 using namespace clang; 22 using namespace transformer; 23 using namespace ast_matchers; 24 25 namespace { 26 using ::llvm::Failed; 27 using ::llvm::HasValue; 28 using ::llvm::StringError; 29 using ::testing::AllOf; 30 using ::testing::HasSubstr; 31 using MatchResult = MatchFinder::MatchResult; 32 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; }; 40 namespace std { 41 template <typename T> 42 struct unique_ptr { 43 T* operator->() const; 44 T& operator*() const; 45 }; 46 } 47 template<class T> T desugar() { return T(); }; 48 )cc"; 49 return (Preface + ExtraPreface + "auto stencil_test_snippet = []{" + 50 StatementCode + "};") 51 .str(); 52 } 53 54 static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { 55 return varDecl(hasName("stencil_test_snippet"), 56 hasDescendant(compoundStmt(hasAnySubstatement(Matcher)))); 57 } 58 59 struct TestMatch { 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`. 64 MatchResult Result; 65 }; 66 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"; 80 return std::nullopt; 81 } 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(); 87 return std::nullopt; 88 } 89 return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)}; 90 } 91 92 class StencilTest : public ::testing::Test { 93 protected: 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( 100 struct A {}; 101 class F : public A { 102 public: 103 F(int) {} 104 }; 105 F(1); 106 )cc"; 107 auto StmtMatch = matchStmt( 108 Snippet, 109 stmt(hasDescendant( 110 cxxConstructExpr( 111 hasDeclaration(decl(hasDescendant(cxxCtorInitializer( 112 isBaseInitializer()) 113 .bind("init"))) 114 .bind("decl"))) 115 .bind("expr"))) 116 .bind("stmt")); 117 ASSERT_TRUE(StmtMatch); 118 if (auto ResultOrErr = Stencil->eval(StmtMatch->Result)) { 119 ADD_FAILURE() << "Expected failure but succeeded: " << *ResultOrErr; 120 } else { 121 auto Err = llvm::handleErrors(ResultOrErr.takeError(), 122 [&Matcher](const StringError &Err) { 123 EXPECT_THAT(Err.getMessage(), Matcher); 124 }); 125 if (Err) { 126 ADD_FAILURE() << "Unhandled error: " << llvm::toString(std::move(Err)); 127 } 128 } 129 } 130 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) { 134 testError(Stencil, 135 AllOf(HasSubstr(std::string(UnboundId)), HasSubstr("not bound"))); 136 } 137 }; 138 139 TEST_F(StencilTest, SingleStatement) { 140 StringRef Condition("C"), Then("T"), Else("E"); 141 const std::string Snippet = R"cc( 142 if (true) 143 return 1; 144 else 145 return 0; 146 )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. 152 auto Stencil = 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;")); 157 } 158 159 TEST_F(StencilTest, UnboundNode) { 160 const std::string Snippet = R"cc( 161 if (true) 162 return 1; 163 else 164 return 0; 165 )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; 173 } 174 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))); 183 } 184 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))); 192 } 193 194 TEST_F(StencilTest, SelectionOp) { 195 StringRef Id = "id"; 196 testExpr(Id, "3;", cat(node(std::string(Id))), "3"); 197 } 198 199 TEST_F(StencilTest, IfBoundOpBound) { 200 StringRef Id = "id"; 201 testExpr(Id, "3;", ifBound(Id, cat("5"), cat("7")), "5"); 202 } 203 204 TEST_F(StencilTest, IfBoundOpUnbound) { 205 StringRef Id = "id"; 206 testExpr(Id, "3;", ifBound("other", cat("5"), cat("7")), "7"); 207 } 208 209 static auto selectMatcher() { 210 // The `anything` matcher is not bound, to test for none of the cases 211 // matching. 212 return expr(anyOf(integerLiteral().bind("int"), cxxBoolLiteral().bind("bool"), 213 floatLiteral().bind("float"), anything())); 214 } 215 216 static auto selectStencil() { 217 return selectBound({ 218 {"int", cat("I")}, 219 {"bool", cat("B")}, 220 {"bool", cat("redundant")}, 221 {"float", cat("F")}, 222 }); 223 } 224 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"))); 231 } 232 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"))); 239 } 240 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"))))); 250 } 251 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"))); 259 } 260 261 TEST_F(StencilTest, ExpressionOpNoParens) { 262 StringRef Id = "id"; 263 testExpr(Id, "3;", expression(Id), "3"); 264 } 265 266 // Don't parenthesize a parens expression. 267 TEST_F(StencilTest, ExpressionOpNoParensParens) { 268 StringRef Id = "id"; 269 testExpr(Id, "(3);", expression(Id), "(3)"); 270 } 271 272 TEST_F(StencilTest, ExpressionOpBinaryOpParens) { 273 StringRef Id = "id"; 274 testExpr(Id, "3+4;", expression(Id), "(3+4)"); 275 } 276 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) { 281 StringRef Id = "id"; 282 testFailure(Id, "3;", expression("ACACA"), 283 AllOf(HasSubstr("ACACA"), HasSubstr("not bound"))); 284 } 285 286 TEST_F(StencilTest, DerefPointer) { 287 StringRef Id = "id"; 288 testExpr(Id, "int *x; x;", deref(Id), "*x"); 289 } 290 291 TEST_F(StencilTest, DerefBinOp) { 292 StringRef Id = "id"; 293 testExpr(Id, "int *x; x + 1;", deref(Id), "*(x + 1)"); 294 } 295 296 TEST_F(StencilTest, DerefAddressExpr) { 297 StringRef Id = "id"; 298 testExpr(Id, "int x; &x;", deref(Id), "x"); 299 } 300 301 TEST_F(StencilTest, AddressOfValue) { 302 StringRef Id = "id"; 303 testExpr(Id, "int x; x;", addressOf(Id), "&x"); 304 } 305 306 TEST_F(StencilTest, AddressOfDerefExpr) { 307 StringRef Id = "id"; 308 testExpr(Id, "int *x; *x;", addressOf(Id), "x"); 309 } 310 311 TEST_F(StencilTest, MaybeDerefValue) { 312 StringRef Id = "id"; 313 testExpr(Id, "int x; x;", maybeDeref(Id), "x"); 314 } 315 316 TEST_F(StencilTest, MaybeDerefPointer) { 317 StringRef Id = "id"; 318 testExpr(Id, "int *x; x;", maybeDeref(Id), "*x"); 319 } 320 321 TEST_F(StencilTest, MaybeDerefBinOp) { 322 StringRef Id = "id"; 323 testExpr(Id, "int *x; x + 1;", maybeDeref(Id), "*(x + 1)"); 324 } 325 326 TEST_F(StencilTest, MaybeDerefAddressExpr) { 327 StringRef Id = "id"; 328 testExpr(Id, "int x; &x;", maybeDeref(Id), "x"); 329 } 330 331 TEST_F(StencilTest, MaybeDerefSmartPointer) { 332 StringRef Id = "id"; 333 std::string Snippet = R"cc( 334 std::unique_ptr<S> x; 335 x; 336 )cc"; 337 testExpr(Id, Snippet, maybeDeref(Id), "*x"); 338 } 339 340 TEST_F(StencilTest, MaybeDerefSmartPointerFromMemberExpr) { 341 StringRef Id = "id"; 342 std::string Snippet = "std::unique_ptr<S> x; x->Field;"; 343 auto StmtMatch = 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")); 348 } 349 350 TEST_F(StencilTest, MaybeAddressOfPointer) { 351 StringRef Id = "id"; 352 testExpr(Id, "int *x; x;", maybeAddressOf(Id), "x"); 353 } 354 355 TEST_F(StencilTest, MaybeAddressOfValue) { 356 StringRef Id = "id"; 357 testExpr(Id, "int x; x;", addressOf(Id), "&x"); 358 } 359 360 TEST_F(StencilTest, MaybeAddressOfBinOp) { 361 StringRef Id = "id"; 362 testExpr(Id, "int x; x + 1;", maybeAddressOf(Id), "&(x + 1)"); 363 } 364 365 TEST_F(StencilTest, MaybeAddressOfDerefExpr) { 366 StringRef Id = "id"; 367 testExpr(Id, "int *x; *x;", addressOf(Id), "x"); 368 } 369 370 TEST_F(StencilTest, MaybeAddressOfSmartPointer) { 371 StringRef Id = "id"; 372 testExpr(Id, "std::unique_ptr<S> x; x;", maybeAddressOf(Id), "x"); 373 } 374 375 TEST_F(StencilTest, MaybeAddressOfSmartPointerFromMemberCall) { 376 StringRef Id = "id"; 377 std::string Snippet = "std::unique_ptr<S> x; x->Field;"; 378 auto StmtMatch = 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")); 383 } 384 385 TEST_F(StencilTest, MaybeAddressOfSmartPointerDerefNoCancel) { 386 StringRef Id = "id"; 387 testExpr(Id, "std::unique_ptr<S> x; *x;", maybeAddressOf(Id), "&*x"); 388 } 389 390 TEST_F(StencilTest, AccessOpValue) { 391 StringRef Snippet = R"cc( 392 S x; 393 x; 394 )cc"; 395 StringRef Id = "id"; 396 testExpr(Id, Snippet, access(Id, "field"), "x.field"); 397 } 398 399 TEST_F(StencilTest, AccessOpValueExplicitText) { 400 StringRef Snippet = R"cc( 401 S x; 402 x; 403 )cc"; 404 StringRef Id = "id"; 405 testExpr(Id, Snippet, access(Id, cat("field")), "x.field"); 406 } 407 408 TEST_F(StencilTest, AccessOpValueAddress) { 409 StringRef Snippet = R"cc( 410 S x; 411 &x; 412 )cc"; 413 StringRef Id = "id"; 414 testExpr(Id, Snippet, access(Id, "field"), "x.field"); 415 } 416 417 TEST_F(StencilTest, AccessOpPointer) { 418 StringRef Snippet = R"cc( 419 S *x; 420 x; 421 )cc"; 422 StringRef Id = "id"; 423 testExpr(Id, Snippet, access(Id, "field"), "x->field"); 424 } 425 426 TEST_F(StencilTest, AccessOpPointerDereference) { 427 StringRef Snippet = R"cc( 428 S *x; 429 *x; 430 )cc"; 431 StringRef Id = "id"; 432 testExpr(Id, Snippet, access(Id, "field"), "x->field"); 433 } 434 435 TEST_F(StencilTest, AccessOpSmartPointer) { 436 StringRef Snippet = R"cc( 437 std::unique_ptr<S> x; 438 x; 439 )cc"; 440 StringRef Id = "id"; 441 testExpr(Id, Snippet, access(Id, "field"), "x->field"); 442 } 443 444 TEST_F(StencilTest, AccessOpSmartPointerDereference) { 445 StringRef Snippet = R"cc( 446 std::unique_ptr<S> x; 447 *x; 448 )cc"; 449 StringRef Id = "id"; 450 testExpr(Id, Snippet, access(Id, "field"), "x->field"); 451 } 452 453 TEST_F(StencilTest, AccessOpSmartPointerMemberCall) { 454 StringRef Snippet = R"cc( 455 std::unique_ptr<S> x; 456 x->Field; 457 )cc"; 458 StringRef Id = "id"; 459 auto StmtMatch = 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")); 464 } 465 466 TEST_F(StencilTest, AccessOpExplicitThis) { 467 using clang::ast_matchers::hasObjectExpression; 468 using clang::ast_matchers::memberExpr; 469 470 // Set up the code so we can bind to a use of this. 471 StringRef Snippet = R"cc( 472 class C { 473 public: 474 int x; 475 int foo() { return this->x; } 476 }; 477 )cc"; 478 auto StmtMatch = matchStmt( 479 Snippet, 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")); 486 } 487 488 TEST_F(StencilTest, AccessOpImplicitThis) { 489 using clang::ast_matchers::hasObjectExpression; 490 using clang::ast_matchers::memberExpr; 491 492 // Set up the code so we can bind to a use of (implicit) this. 493 StringRef Snippet = R"cc( 494 class C { 495 public: 496 int x; 497 int foo() { return x; } 498 }; 499 )cc"; 500 auto StmtMatch = 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")); 506 } 507 508 TEST_F(StencilTest, DescribeType) { 509 std::string Snippet = "int *x; x;"; 510 std::string Expected = "int *"; 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, DescribeSugaredType) { 519 std::string Snippet = "using Ty = int; Ty *x; x;"; 520 std::string Expected = "Ty *"; 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, DescribeDeclType) { 529 std::string Snippet = "S s; s;"; 530 std::string Expected = "S"; 531 auto StmtMatch = 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))); 536 } 537 538 TEST_F(StencilTest, DescribeQualifiedType) { 539 std::string Snippet = "N::C c; c;"; 540 std::string Expected = "N::C"; 541 auto StmtMatch = 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))); 546 } 547 548 TEST_F(StencilTest, DescribeUnqualifiedType) { 549 std::string Snippet = "using N::C; C c; c;"; 550 std::string Expected = "C"; 551 auto StmtMatch = 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))); 556 } 557 558 TEST_F(StencilTest, DescribeAnonNamespaceType) { 559 std::string Snippet = "auto c = desugar<AnonC>(); c;"; 560 std::string Expected = "(anonymous namespace)::AnonC"; 561 auto StmtMatch = 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))); 566 } 567 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))); 575 } 576 577 TEST_F(StencilTest, DescribeImplicitOperator) { 578 std::string Snippet = "struct Tag {}; [](Tag){};"; 579 std::string Expected = "operator()"; 580 auto StmtMatch = matchStmt( 581 Snippet, 582 stmt(hasDescendant( 583 cxxMethodDecl(hasParameter(0, hasType(namedDecl(hasName("Tag"))))) 584 .bind("fn")))); 585 ASSERT_TRUE(StmtMatch); 586 EXPECT_THAT_EXPECTED(describe("fn")->eval(StmtMatch->Result), 587 HasValue(std::string(Expected))); 588 } 589 590 TEST_F(StencilTest, RunOp) { 591 StringRef Id = "id"; 592 auto SimpleFn = [Id](const MatchResult &R) { 593 return std::string(R.Nodes.getNodeAs<Stmt>(Id) != nullptr ? "Bound" 594 : "Unbound"); 595 }; 596 testExpr(Id, "3;", run(SimpleFn), "Bound"); 597 } 598 599 TEST_F(StencilTest, CatOfMacroRangeSucceeds) { 600 StringRef Snippet = R"cpp( 601 #define MACRO 3.77 602 double foo(double d); 603 foo(MACRO);)cpp"; 604 605 auto StmtMatch = 606 matchStmt(Snippet, callExpr(callee(functionDecl(hasName("foo"))), 607 argumentCountIs(1), 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")); 612 } 613 614 TEST_F(StencilTest, CatOfMacroArgRangeSucceeds) { 615 StringRef Snippet = R"cpp( 616 #define MACRO(a, b) a + b 617 MACRO(2, 3);)cpp"; 618 619 auto StmtMatch = 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")); 624 } 625 626 TEST_F(StencilTest, CatOfMacroArgSubRangeSucceeds) { 627 StringRef Snippet = R"cpp( 628 #define MACRO(a, b) a + b 629 int foo(int); 630 MACRO(2, foo(3));)cpp"; 631 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")); 639 } 640 641 TEST_F(StencilTest, CatOfInvalidRangeFails) { 642 StringRef Snippet = R"cpp( 643 #define MACRO (3.77) 644 double foo(double d); 645 foo(MACRO);)cpp"; 646 647 auto StmtMatch = 648 matchStmt(Snippet, callExpr(callee(functionDecl(hasName("foo"))), 649 argumentCountIs(1), 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"))); 658 }); 659 } 660 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`). 666 667 TEST(StencilToStringTest, RawTextOp) { 668 auto S = cat("foo bar baz"); 669 StringRef Expected = R"("foo bar baz")"; 670 EXPECT_EQ(S->toString(), Expected); 671 } 672 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); 677 } 678 679 TEST(StencilToStringTest, DescribeOp) { 680 auto S = describe("Id"); 681 StringRef Expected = R"repr(describe("Id"))repr"; 682 EXPECT_EQ(S->toString(), Expected); 683 } 684 685 TEST(StencilToStringTest, DebugPrintNodeOp) { 686 auto S = dPrint("Id"); 687 StringRef Expected = R"repr(dPrint("Id"))repr"; 688 EXPECT_EQ(S->toString(), Expected); 689 } 690 691 TEST(StencilToStringTest, ExpressionOp) { 692 auto S = expression("Id"); 693 StringRef Expected = R"repr(expression("Id"))repr"; 694 EXPECT_EQ(S->toString(), Expected); 695 } 696 697 TEST(StencilToStringTest, DerefOp) { 698 auto S = deref("Id"); 699 StringRef Expected = R"repr(deref("Id"))repr"; 700 EXPECT_EQ(S->toString(), Expected); 701 } 702 703 TEST(StencilToStringTest, AddressOfOp) { 704 auto S = addressOf("Id"); 705 StringRef Expected = R"repr(addressOf("Id"))repr"; 706 EXPECT_EQ(S->toString(), Expected); 707 } 708 709 TEST(StencilToStringTest, SelectionOp) { 710 auto S1 = cat(node("node1")); 711 EXPECT_EQ(S1->toString(), "selection(...)"); 712 } 713 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); 718 } 719 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); 724 } 725 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); 730 } 731 732 TEST(StencilToStringTest, IfBoundOp) { 733 auto S = ifBound("Id", cat("trueText"), access("exprId", "memberData")); 734 StringRef Expected = 735 R"repr(ifBound("Id", "trueText", access("exprId", "memberData")))repr"; 736 EXPECT_EQ(S->toString(), Expected); 737 } 738 739 TEST(StencilToStringTest, SelectBoundOp) { 740 auto S = selectBound({ 741 {"int", cat("I")}, 742 {"float", cat("F")}, 743 }); 744 StringRef Expected = R"repr(selectBound({{"int", "I"}, {"float", "F"}}))repr"; 745 EXPECT_EQ(S->toString(), Expected); 746 } 747 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); 752 } 753 754 TEST(StencilToStringTest, SelectBoundOpWithDefault) { 755 auto S = selectBound({{"int", cat("I")}, {"float", cat("F")}}, cat("D")); 756 StringRef Expected = 757 R"cc(selectBound({{"int", "I"}, {"float", "F"}}, "D"))cc"; 758 EXPECT_EQ(S->toString(), Expected); 759 } 760 761 TEST(StencilToStringTest, RunOp) { 762 auto F1 = [](const MatchResult &R) { return "foo"; }; 763 auto S1 = run(F1); 764 EXPECT_EQ(S1->toString(), "run(...)"); 765 } 766 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); 773 } 774 775 TEST(StencilToStringTest, SequenceEmpty) { 776 auto S = cat(); 777 StringRef Expected = "seq()"; 778 EXPECT_EQ(S->toString(), Expected); 779 } 780 781 TEST(StencilToStringTest, SequenceSingle) { 782 auto S = cat("foo"); 783 StringRef Expected = "\"foo\""; 784 EXPECT_EQ(S->toString(), Expected); 785 } 786 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); 793 } 794 } // namespace 795