brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.2 KiB · 445912a Raw
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