StencilTest.cpp 14 KB

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