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/ASTMatchers/ASTMatchers.h" 11 #include "clang/Tooling/FixIt.h" 12 #include "clang/Tooling/Tooling.h" 13 #include "llvm/Support/Error.h" 14 #include "llvm/Testing/Support/Error.h" 15 #include "gmock/gmock.h" 16 #include "gtest/gtest.h" 17 18 using namespace clang; 19 using namespace transformer; 20 using namespace ast_matchers; 21 22 namespace { 23 using ::llvm::Failed; 24 using ::llvm::HasValue; 25 using ::llvm::StringError; 26 using ::testing::AllOf; 27 using ::testing::HasSubstr; 28 using MatchResult = MatchFinder::MatchResult; 29 30 // Create a valid translation-unit from a statement. 31 static std::string wrapSnippet(StringRef StatementCode) { 32 return ("struct S { int field; }; auto stencil_test_snippet = []{" + 33 StatementCode + "};") 34 .str(); 35 } 36 37 static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { 38 return varDecl(hasName("stencil_test_snippet"), 39 hasDescendant(compoundStmt(hasAnySubstatement(Matcher)))); 40 } 41 42 struct TestMatch { 43 // The AST unit from which `result` is built. We bundle it because it backs 44 // the result. Users are not expected to access it. 45 std::unique_ptr<ASTUnit> AstUnit; 46 // The result to use in the test. References `ast_unit`. 47 MatchResult Result; 48 }; 49 50 // Matches `Matcher` against the statement `StatementCode` and returns the 51 // result. Handles putting the statement inside a function and modifying the 52 // matcher correspondingly. `Matcher` should match one of the statements in 53 // `StatementCode` exactly -- that is, produce exactly one match. However, 54 // `StatementCode` may contain other statements not described by `Matcher`. 55 static llvm::Optional<TestMatch> matchStmt(StringRef StatementCode, 56 StatementMatcher Matcher) { 57 auto AstUnit = tooling::buildASTFromCode(wrapSnippet(StatementCode)); 58 if (AstUnit == nullptr) { 59 ADD_FAILURE() << "AST construction failed"; 60 return llvm::None; 61 } 62 ASTContext &Context = AstUnit->getASTContext(); 63 auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context); 64 // We expect a single, exact match for the statement. 65 if (Matches.size() != 1) { 66 ADD_FAILURE() << "Wrong number of matches: " << Matches.size(); 67 return llvm::None; 68 } 69 return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)}; 70 } 71 72 class StencilTest : public ::testing::Test { 73 protected: 74 // Verifies that the given stencil fails when evaluated on a valid match 75 // result. Binds a statement to "stmt", a (non-member) ctor-initializer to 76 // "init", an expression to "expr" and a (nameless) declaration to "decl". 77 void testError(const Stencil &Stencil, 78 ::testing::Matcher<std::string> Matcher) { 79 const std::string Snippet = R"cc( 80 struct A {}; 81 class F : public A { 82 public: 83 F(int) {} 84 }; 85 F(1); 86 )cc"; 87 auto StmtMatch = matchStmt( 88 Snippet, 89 stmt(hasDescendant( 90 cxxConstructExpr( 91 hasDeclaration(decl(hasDescendant(cxxCtorInitializer( 92 isBaseInitializer()) 93 .bind("init"))) 94 .bind("decl"))) 95 .bind("expr"))) 96 .bind("stmt")); 97 ASSERT_TRUE(StmtMatch); 98 if (auto ResultOrErr = Stencil->eval(StmtMatch->Result)) { 99 ADD_FAILURE() << "Expected failure but succeeded: " << *ResultOrErr; 100 } else { 101 auto Err = llvm::handleErrors(ResultOrErr.takeError(), 102 [&Matcher](const StringError &Err) { 103 EXPECT_THAT(Err.getMessage(), Matcher); 104 }); 105 if (Err) { 106 ADD_FAILURE() << "Unhandled error: " << llvm::toString(std::move(Err)); 107 } 108 } 109 } 110 111 // Tests failures caused by references to unbound nodes. `unbound_id` is the 112 // id that will cause the failure. 113 void testUnboundNodeError(const Stencil &Stencil, StringRef UnboundId) { 114 testError(Stencil, 115 AllOf(HasSubstr(std::string(UnboundId)), HasSubstr("not bound"))); 116 } 117 }; 118 119 TEST_F(StencilTest, SingleStatement) { 120 StringRef Condition("C"), Then("T"), Else("E"); 121 const std::string Snippet = R"cc( 122 if (true) 123 return 1; 124 else 125 return 0; 126 )cc"; 127 auto StmtMatch = matchStmt( 128 Snippet, ifStmt(hasCondition(expr().bind(Condition)), 129 hasThen(stmt().bind(Then)), hasElse(stmt().bind(Else)))); 130 ASSERT_TRUE(StmtMatch); 131 // Invert the if-then-else. 132 auto Stencil = 133 cat("if (!", node(std::string(Condition)), ") ", 134 statement(std::string(Else)), " else ", statement(std::string(Then))); 135 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), 136 HasValue("if (!true) return 0; else return 1;")); 137 } 138 139 TEST_F(StencilTest, UnboundNode) { 140 const std::string Snippet = R"cc( 141 if (true) 142 return 1; 143 else 144 return 0; 145 )cc"; 146 auto StmtMatch = matchStmt(Snippet, ifStmt(hasCondition(stmt().bind("a1")), 147 hasThen(stmt().bind("a2")))); 148 ASSERT_TRUE(StmtMatch); 149 auto Stencil = cat("if(!", node("a1"), ") ", node("UNBOUND"), ";"); 150 auto ResultOrErr = Stencil->eval(StmtMatch->Result); 151 EXPECT_TRUE(llvm::errorToBool(ResultOrErr.takeError())) 152 << "Expected unbound node, got " << *ResultOrErr; 153 } 154 155 // Tests that a stencil with a single parameter (`Id`) evaluates to the expected 156 // string, when `Id` is bound to the expression-statement in `Snippet`. 157 void testExpr(StringRef Id, StringRef Snippet, const Stencil &Stencil, 158 StringRef Expected) { 159 auto StmtMatch = matchStmt(Snippet, expr().bind(Id)); 160 ASSERT_TRUE(StmtMatch); 161 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), 162 HasValue(std::string(Expected))); 163 } 164 165 void testFailure(StringRef Id, StringRef Snippet, const Stencil &Stencil, 166 testing::Matcher<std::string> MessageMatcher) { 167 auto StmtMatch = matchStmt(Snippet, expr().bind(Id)); 168 ASSERT_TRUE(StmtMatch); 169 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), 170 Failed<StringError>(testing::Property( 171 &StringError::getMessage, MessageMatcher))); 172 } 173 174 TEST_F(StencilTest, SelectionOp) { 175 StringRef Id = "id"; 176 testExpr(Id, "3;", cat(node(std::string(Id))), "3"); 177 } 178 179 TEST_F(StencilTest, IfBoundOpBound) { 180 StringRef Id = "id"; 181 testExpr(Id, "3;", ifBound(Id, text("5"), text("7")), "5"); 182 } 183 184 TEST_F(StencilTest, IfBoundOpUnbound) { 185 StringRef Id = "id"; 186 testExpr(Id, "3;", ifBound("other", text("5"), text("7")), "7"); 187 } 188 189 TEST_F(StencilTest, ExpressionOpNoParens) { 190 StringRef Id = "id"; 191 testExpr(Id, "3;", expression(Id), "3"); 192 } 193 194 // Don't parenthesize a parens expression. 195 TEST_F(StencilTest, ExpressionOpNoParensParens) { 196 StringRef Id = "id"; 197 testExpr(Id, "(3);", expression(Id), "(3)"); 198 } 199 200 TEST_F(StencilTest, ExpressionOpBinaryOpParens) { 201 StringRef Id = "id"; 202 testExpr(Id, "3+4;", expression(Id), "(3+4)"); 203 } 204 205 // `expression` shares code with other ops, so we get sufficient coverage of the 206 // error handling code with this test. If that changes in the future, more error 207 // tests should be added. 208 TEST_F(StencilTest, ExpressionOpUnbound) { 209 StringRef Id = "id"; 210 testFailure(Id, "3;", expression("ACACA"), 211 AllOf(HasSubstr("ACACA"), HasSubstr("not bound"))); 212 } 213 214 TEST_F(StencilTest, DerefPointer) { 215 StringRef Id = "id"; 216 testExpr(Id, "int *x; x;", deref(Id), "*x"); 217 } 218 219 TEST_F(StencilTest, DerefBinOp) { 220 StringRef Id = "id"; 221 testExpr(Id, "int *x; x + 1;", deref(Id), "*(x + 1)"); 222 } 223 224 TEST_F(StencilTest, DerefAddressExpr) { 225 StringRef Id = "id"; 226 testExpr(Id, "int x; &x;", deref(Id), "x"); 227 } 228 229 TEST_F(StencilTest, AddressOfValue) { 230 StringRef Id = "id"; 231 testExpr(Id, "int x; x;", addressOf(Id), "&x"); 232 } 233 234 TEST_F(StencilTest, AddressOfDerefExpr) { 235 StringRef Id = "id"; 236 testExpr(Id, "int *x; *x;", addressOf(Id), "x"); 237 } 238 239 TEST_F(StencilTest, MaybeDerefValue) { 240 StringRef Id = "id"; 241 testExpr(Id, "int x; x;", maybeDeref(Id), "x"); 242 } 243 244 TEST_F(StencilTest, MaybeDerefPointer) { 245 StringRef Id = "id"; 246 testExpr(Id, "int *x; x;", maybeDeref(Id), "*x"); 247 } 248 249 TEST_F(StencilTest, MaybeDerefBinOp) { 250 StringRef Id = "id"; 251 testExpr(Id, "int *x; x + 1;", maybeDeref(Id), "*(x + 1)"); 252 } 253 254 TEST_F(StencilTest, MaybeDerefAddressExpr) { 255 StringRef Id = "id"; 256 testExpr(Id, "int x; &x;", maybeDeref(Id), "x"); 257 } 258 259 TEST_F(StencilTest, MaybeAddressOfPointer) { 260 StringRef Id = "id"; 261 testExpr(Id, "int *x; x;", maybeAddressOf(Id), "x"); 262 } 263 264 TEST_F(StencilTest, MaybeAddressOfValue) { 265 StringRef Id = "id"; 266 testExpr(Id, "int x; x;", addressOf(Id), "&x"); 267 } 268 269 TEST_F(StencilTest, MaybeAddressOfBinOp) { 270 StringRef Id = "id"; 271 testExpr(Id, "int x; x + 1;", maybeAddressOf(Id), "&(x + 1)"); 272 } 273 274 TEST_F(StencilTest, MaybeAddressOfDerefExpr) { 275 StringRef Id = "id"; 276 testExpr(Id, "int *x; *x;", addressOf(Id), "x"); 277 } 278 279 TEST_F(StencilTest, AccessOpValue) { 280 StringRef Snippet = R"cc( 281 S x; 282 x; 283 )cc"; 284 StringRef Id = "id"; 285 testExpr(Id, Snippet, access(Id, "field"), "x.field"); 286 } 287 288 TEST_F(StencilTest, AccessOpValueExplicitText) { 289 StringRef Snippet = R"cc( 290 S x; 291 x; 292 )cc"; 293 StringRef Id = "id"; 294 testExpr(Id, Snippet, access(Id, text("field")), "x.field"); 295 } 296 297 TEST_F(StencilTest, AccessOpValueAddress) { 298 StringRef Snippet = R"cc( 299 S x; 300 &x; 301 )cc"; 302 StringRef Id = "id"; 303 testExpr(Id, Snippet, access(Id, "field"), "x.field"); 304 } 305 306 TEST_F(StencilTest, AccessOpPointer) { 307 StringRef Snippet = R"cc( 308 S *x; 309 x; 310 )cc"; 311 StringRef Id = "id"; 312 testExpr(Id, Snippet, access(Id, "field"), "x->field"); 313 } 314 315 TEST_F(StencilTest, AccessOpPointerDereference) { 316 StringRef Snippet = R"cc( 317 S *x; 318 *x; 319 )cc"; 320 StringRef Id = "id"; 321 testExpr(Id, Snippet, access(Id, "field"), "x->field"); 322 } 323 324 TEST_F(StencilTest, AccessOpExplicitThis) { 325 using clang::ast_matchers::hasObjectExpression; 326 using clang::ast_matchers::memberExpr; 327 328 // Set up the code so we can bind to a use of this. 329 StringRef Snippet = R"cc( 330 class C { 331 public: 332 int x; 333 int foo() { return this->x; } 334 }; 335 )cc"; 336 auto StmtMatch = 337 matchStmt(Snippet, returnStmt(hasReturnValue(ignoringImplicit(memberExpr( 338 hasObjectExpression(expr().bind("obj"))))))); 339 ASSERT_TRUE(StmtMatch); 340 const Stencil Stencil = access("obj", "field"); 341 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), 342 HasValue("this->field")); 343 } 344 345 TEST_F(StencilTest, AccessOpImplicitThis) { 346 using clang::ast_matchers::hasObjectExpression; 347 using clang::ast_matchers::memberExpr; 348 349 // Set up the code so we can bind to a use of (implicit) this. 350 StringRef Snippet = R"cc( 351 class C { 352 public: 353 int x; 354 int foo() { return x; } 355 }; 356 )cc"; 357 auto StmtMatch = 358 matchStmt(Snippet, returnStmt(hasReturnValue(ignoringImplicit(memberExpr( 359 hasObjectExpression(expr().bind("obj"))))))); 360 ASSERT_TRUE(StmtMatch); 361 const Stencil Stencil = access("obj", "field"); 362 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), HasValue("field")); 363 } 364 365 TEST_F(StencilTest, RunOp) { 366 StringRef Id = "id"; 367 auto SimpleFn = [Id](const MatchResult &R) { 368 return std::string(R.Nodes.getNodeAs<Stmt>(Id) != nullptr ? "Bound" 369 : "Unbound"); 370 }; 371 testExpr(Id, "3;", run(SimpleFn), "Bound"); 372 } 373 374 TEST_F(StencilTest, CatOfInvalidRangeFails) { 375 StringRef Snippet = R"cpp( 376 #define MACRO (3.77) 377 double foo(double d); 378 foo(MACRO);)cpp"; 379 380 auto StmtMatch = 381 matchStmt(Snippet, callExpr(callee(functionDecl(hasName("foo"))), 382 argumentCountIs(1), 383 hasArgument(0, expr().bind("arg")))); 384 ASSERT_TRUE(StmtMatch); 385 Stencil S = cat(node("arg")); 386 EXPECT_THAT_EXPECTED(S->eval(StmtMatch->Result), Failed<StringError>()); 387 } 388 389 TEST(StencilToStringTest, RawTextOp) { 390 auto S = cat("foo bar baz"); 391 StringRef Expected = R"("foo bar baz")"; 392 EXPECT_EQ(S->toString(), Expected); 393 } 394 395 TEST(StencilToStringTest, RawTextOpEscaping) { 396 auto S = cat("foo \"bar\" baz\\n"); 397 StringRef Expected = R"("foo \"bar\" baz\\n")"; 398 EXPECT_EQ(S->toString(), Expected); 399 } 400 401 TEST(StencilToStringTest, DebugPrintNodeOp) { 402 auto S = dPrint("Id"); 403 StringRef Expected = R"repr(dPrint("Id"))repr"; 404 EXPECT_EQ(S->toString(), Expected); 405 } 406 407 TEST(StencilToStringTest, ExpressionOp) { 408 auto S = expression("Id"); 409 StringRef Expected = R"repr(expression("Id"))repr"; 410 EXPECT_EQ(S->toString(), Expected); 411 } 412 413 TEST(StencilToStringTest, DerefOp) { 414 auto S = deref("Id"); 415 StringRef Expected = R"repr(deref("Id"))repr"; 416 EXPECT_EQ(S->toString(), Expected); 417 } 418 419 TEST(StencilToStringTest, AddressOfOp) { 420 auto S = addressOf("Id"); 421 StringRef Expected = R"repr(addressOf("Id"))repr"; 422 EXPECT_EQ(S->toString(), Expected); 423 } 424 425 TEST(StencilToStringTest, SelectionOp) { 426 auto S1 = cat(node("node1")); 427 EXPECT_EQ(S1->toString(), "selection(...)"); 428 } 429 430 TEST(StencilToStringTest, AccessOpText) { 431 auto S = access("Id", "memberData"); 432 StringRef Expected = R"repr(access("Id", "memberData"))repr"; 433 EXPECT_EQ(S->toString(), Expected); 434 } 435 436 TEST(StencilToStringTest, AccessOpSelector) { 437 auto S = access("Id", selection(name("otherId"))); 438 StringRef Expected = R"repr(access("Id", selection(...)))repr"; 439 EXPECT_EQ(S->toString(), Expected); 440 } 441 442 TEST(StencilToStringTest, AccessOpStencil) { 443 auto S = access("Id", cat("foo_", "bar")); 444 StringRef Expected = R"repr(access("Id", seq("foo_", "bar")))repr"; 445 EXPECT_EQ(S->toString(), Expected); 446 } 447 448 TEST(StencilToStringTest, IfBoundOp) { 449 auto S = ifBound("Id", text("trueText"), access("exprId", "memberData")); 450 StringRef Expected = 451 R"repr(ifBound("Id", "trueText", access("exprId", "memberData")))repr"; 452 EXPECT_EQ(S->toString(), Expected); 453 } 454 455 TEST(StencilToStringTest, RunOp) { 456 auto F1 = [](const MatchResult &R) { return "foo"; }; 457 auto S1 = run(F1); 458 EXPECT_EQ(S1->toString(), "run(...)"); 459 } 460 461 TEST(StencilToStringTest, Sequence) { 462 auto S = cat("foo", access("x", "m()"), "bar", 463 ifBound("x", text("t"), access("e", "f"))); 464 StringRef Expected = R"repr(seq("foo", access("x", "m()"), "bar", )repr" 465 R"repr(ifBound("x", "t", access("e", "f"))))repr"; 466 EXPECT_EQ(S->toString(), Expected); 467 } 468 469 TEST(StencilToStringTest, SequenceEmpty) { 470 auto S = cat(); 471 StringRef Expected = "seq()"; 472 EXPECT_EQ(S->toString(), Expected); 473 } 474 475 TEST(StencilToStringTest, SequenceSingle) { 476 auto S = cat("foo"); 477 StringRef Expected = "\"foo\""; 478 EXPECT_EQ(S->toString(), Expected); 479 } 480 481 TEST(StencilToStringTest, SequenceFromVector) { 482 auto S = catVector({text("foo"), access("x", "m()"), text("bar"), 483 ifBound("x", text("t"), access("e", "f"))}); 484 StringRef Expected = R"repr(seq("foo", access("x", "m()"), "bar", )repr" 485 R"repr(ifBound("x", "t", access("e", "f"))))repr"; 486 EXPECT_EQ(S->toString(), Expected); 487 } 488 } // namespace 489