795 lines · cpp
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-exception6//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 21using namespace clang;22using namespace transformer;23using namespace ast_matchers;24 25namespace {26using ::llvm::Failed;27using ::llvm::HasValue;28using ::llvm::StringError;29using ::testing::AllOf;30using ::testing::HasSubstr;31using MatchResult = MatchFinder::MatchResult;32 33// Create a valid translation-unit from a statement.34static 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 54static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) {55 return varDecl(hasName("stencil_test_snippet"),56 hasDescendant(compoundStmt(hasAnySubstatement(Matcher))));57}58 59struct TestMatch {60 // The AST unit from which `result` is built. We bundle it because it backs61 // 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 the68// result. Handles putting the statement inside a function and modifying the69// matcher correspondingly. `Matcher` should match one of the statements in70// `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.73static 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 92class StencilTest : public ::testing::Test {93protected:94 // Verifies that the given stencil fails when evaluated on a valid match95 // result. Binds a statement to "stmt", a (non-member) ctor-initializer to96 // "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 the132 // 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 139TEST_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 else145 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 159TEST_F(StencilTest, UnboundNode) {160 const std::string Snippet = R"cc(161 if (true)162 return 1;163 else164 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 expected176// string, when `Id` is bound to the expression-statement in `Snippet`.177void 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 185void 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 194TEST_F(StencilTest, SelectionOp) {195 StringRef Id = "id";196 testExpr(Id, "3;", cat(node(std::string(Id))), "3");197}198 199TEST_F(StencilTest, IfBoundOpBound) {200 StringRef Id = "id";201 testExpr(Id, "3;", ifBound(Id, cat("5"), cat("7")), "5");202}203 204TEST_F(StencilTest, IfBoundOpUnbound) {205 StringRef Id = "id";206 testExpr(Id, "3;", ifBound("other", cat("5"), cat("7")), "7");207}208 209static auto selectMatcher() {210 // The `anything` matcher is not bound, to test for none of the cases211 // matching.212 return expr(anyOf(integerLiteral().bind("int"), cxxBoolLiteral().bind("bool"),213 floatLiteral().bind("float"), anything()));214}215 216static auto selectStencil() {217 return selectBound({218 {"int", cat("I")},219 {"bool", cat("B")},220 {"bool", cat("redundant")},221 {"float", cat("F")},222 });223}224 225TEST_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 233TEST_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 241TEST_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 252TEST_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 261TEST_F(StencilTest, ExpressionOpNoParens) {262 StringRef Id = "id";263 testExpr(Id, "3;", expression(Id), "3");264}265 266// Don't parenthesize a parens expression.267TEST_F(StencilTest, ExpressionOpNoParensParens) {268 StringRef Id = "id";269 testExpr(Id, "(3);", expression(Id), "(3)");270}271 272TEST_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 the278// error handling code with this test. If that changes in the future, more error279// tests should be added.280TEST_F(StencilTest, ExpressionOpUnbound) {281 StringRef Id = "id";282 testFailure(Id, "3;", expression("ACACA"),283 AllOf(HasSubstr("ACACA"), HasSubstr("not bound")));284}285 286TEST_F(StencilTest, DerefPointer) {287 StringRef Id = "id";288 testExpr(Id, "int *x; x;", deref(Id), "*x");289}290 291TEST_F(StencilTest, DerefBinOp) {292 StringRef Id = "id";293 testExpr(Id, "int *x; x + 1;", deref(Id), "*(x + 1)");294}295 296TEST_F(StencilTest, DerefAddressExpr) {297 StringRef Id = "id";298 testExpr(Id, "int x; &x;", deref(Id), "x");299}300 301TEST_F(StencilTest, AddressOfValue) {302 StringRef Id = "id";303 testExpr(Id, "int x; x;", addressOf(Id), "&x");304}305 306TEST_F(StencilTest, AddressOfDerefExpr) {307 StringRef Id = "id";308 testExpr(Id, "int *x; *x;", addressOf(Id), "x");309}310 311TEST_F(StencilTest, MaybeDerefValue) {312 StringRef Id = "id";313 testExpr(Id, "int x; x;", maybeDeref(Id), "x");314}315 316TEST_F(StencilTest, MaybeDerefPointer) {317 StringRef Id = "id";318 testExpr(Id, "int *x; x;", maybeDeref(Id), "*x");319}320 321TEST_F(StencilTest, MaybeDerefBinOp) {322 StringRef Id = "id";323 testExpr(Id, "int *x; x + 1;", maybeDeref(Id), "*(x + 1)");324}325 326TEST_F(StencilTest, MaybeDerefAddressExpr) {327 StringRef Id = "id";328 testExpr(Id, "int x; &x;", maybeDeref(Id), "x");329}330 331TEST_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 340TEST_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 350TEST_F(StencilTest, MaybeAddressOfPointer) {351 StringRef Id = "id";352 testExpr(Id, "int *x; x;", maybeAddressOf(Id), "x");353}354 355TEST_F(StencilTest, MaybeAddressOfValue) {356 StringRef Id = "id";357 testExpr(Id, "int x; x;", addressOf(Id), "&x");358}359 360TEST_F(StencilTest, MaybeAddressOfBinOp) {361 StringRef Id = "id";362 testExpr(Id, "int x; x + 1;", maybeAddressOf(Id), "&(x + 1)");363}364 365TEST_F(StencilTest, MaybeAddressOfDerefExpr) {366 StringRef Id = "id";367 testExpr(Id, "int *x; *x;", addressOf(Id), "x");368}369 370TEST_F(StencilTest, MaybeAddressOfSmartPointer) {371 StringRef Id = "id";372 testExpr(Id, "std::unique_ptr<S> x; x;", maybeAddressOf(Id), "x");373}374 375TEST_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 385TEST_F(StencilTest, MaybeAddressOfSmartPointerDerefNoCancel) {386 StringRef Id = "id";387 testExpr(Id, "std::unique_ptr<S> x; *x;", maybeAddressOf(Id), "&*x");388}389 390TEST_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 399TEST_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 408TEST_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 417TEST_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 426TEST_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 435TEST_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 444TEST_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 453TEST_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 466TEST_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 488TEST_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 508TEST_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 518TEST_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 528TEST_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 538TEST_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 548TEST_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 558TEST_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 568TEST_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 577TEST_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 590TEST_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 599TEST_F(StencilTest, CatOfMacroRangeSucceeds) {600 StringRef Snippet = R"cpp(601#define MACRO 3.77602 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 614TEST_F(StencilTest, CatOfMacroArgRangeSucceeds) {615 StringRef Snippet = R"cpp(616#define MACRO(a, b) a + b617 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 626TEST_F(StencilTest, CatOfMacroArgSubRangeSucceeds) {627 StringRef Snippet = R"cpp(628#define MACRO(a, b) a + b629 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 641TEST_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 the662// stencil combinator matches (as best possible) the spelling of the663// combinator's construction. Exceptions include those combinators that have no664// explicit spelling (like raw text) and those supporting non-printable665// arguments (like `run`, `selection`).666 667TEST(StencilToStringTest, RawTextOp) {668 auto S = cat("foo bar baz");669 StringRef Expected = R"("foo bar baz")";670 EXPECT_EQ(S->toString(), Expected);671}672 673TEST(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 679TEST(StencilToStringTest, DescribeOp) {680 auto S = describe("Id");681 StringRef Expected = R"repr(describe("Id"))repr";682 EXPECT_EQ(S->toString(), Expected);683}684 685TEST(StencilToStringTest, DebugPrintNodeOp) {686 auto S = dPrint("Id");687 StringRef Expected = R"repr(dPrint("Id"))repr";688 EXPECT_EQ(S->toString(), Expected);689}690 691TEST(StencilToStringTest, ExpressionOp) {692 auto S = expression("Id");693 StringRef Expected = R"repr(expression("Id"))repr";694 EXPECT_EQ(S->toString(), Expected);695}696 697TEST(StencilToStringTest, DerefOp) {698 auto S = deref("Id");699 StringRef Expected = R"repr(deref("Id"))repr";700 EXPECT_EQ(S->toString(), Expected);701}702 703TEST(StencilToStringTest, AddressOfOp) {704 auto S = addressOf("Id");705 StringRef Expected = R"repr(addressOf("Id"))repr";706 EXPECT_EQ(S->toString(), Expected);707}708 709TEST(StencilToStringTest, SelectionOp) {710 auto S1 = cat(node("node1"));711 EXPECT_EQ(S1->toString(), "selection(...)");712}713 714TEST(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 720TEST(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 726TEST(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 732TEST(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 739TEST(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 748TEST(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 754TEST(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 761TEST(StencilToStringTest, RunOp) {762 auto F1 = [](const MatchResult &R) { return "foo"; };763 auto S1 = run(F1);764 EXPECT_EQ(S1->toString(), "run(...)");765}766 767TEST(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 775TEST(StencilToStringTest, SequenceEmpty) {776 auto S = cat();777 StringRef Expected = "seq()";778 EXPECT_EQ(S->toString(), Expected);779}780 781TEST(StencilToStringTest, SequenceSingle) {782 auto S = cat("foo");783 StringRef Expected = "\"foo\"";784 EXPECT_EQ(S->toString(), Expected);785}786 787TEST(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} // namespace795