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
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(Name: "stencil_test_snippet"),
56 hasDescendant(compoundStmt(hasAnySubstatement(InnerMatcher: Matcher))));
57}
58
59struct 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.
73static std::optional<TestMatch> matchStmt(StringRef StatementCode,
74 StatementMatcher Matcher,
75 StringRef ExtraPreface = "") {
76 auto AstUnit = tooling::buildASTFromCodeWithArgs(
77 Code: wrapSnippet(ExtraPreface, StatementCode), Args: {"-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(Matcher: 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{.AstUnit: std::move(AstUnit), .Result: MatchResult(Matches[0], &Context)};
90}
91
92class StencilTest : public ::testing::Test {
93protected:
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 StatementCode: Snippet,
109 Matcher: stmt(hasDescendant(
110 cxxConstructExpr(
111 hasDeclaration(InnerMatcher: decl(hasDescendant(cxxCtorInitializer(
112 isBaseInitializer())
113 .bind(ID: "init")))
114 .bind(ID: "decl")))
115 .bind(ID: "expr")))
116 .bind(ID: "stmt"));
117 ASSERT_TRUE(StmtMatch);
118 if (auto ResultOrErr = Stencil->eval(R: StmtMatch->Result)) {
119 ADD_FAILURE() << "Expected failure but succeeded: " << *ResultOrErr;
120 } else {
121 auto Err = llvm::handleErrors(E: ResultOrErr.takeError(),
122 Hs: [&Matcher](const StringError &Err) {
123 EXPECT_THAT(Err.getMessage(), Matcher);
124 });
125 if (Err) {
126 ADD_FAILURE() << "Unhandled error: " << llvm::toString(E: 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 Matcher: AllOf(matchers: HasSubstr(substring: std::string(UnboundId)), matchers: HasSubstr(substring: "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 else
145 return 0;
146 )cc";
147 auto StmtMatch = matchStmt(
148 StatementCode: Snippet, Matcher: ifStmt(hasCondition(InnerMatcher: expr().bind(ID: Condition)),
149 hasThen(InnerMatcher: stmt().bind(ID: Then)), hasElse(InnerMatcher: stmt().bind(ID: Else))));
150 ASSERT_TRUE(StmtMatch);
151 // Invert the if-then-else.
152 auto Stencil =
153 cat(Parts: "if (!", Parts: node(ID: std::string(Condition)), Parts: ") ",
154 Parts: statement(ID: std::string(Else)), Parts: " else ", Parts: statement(ID: 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 else
164 return 0;
165 )cc";
166 auto StmtMatch = matchStmt(StatementCode: Snippet, Matcher: ifStmt(hasCondition(InnerMatcher: stmt().bind(ID: "a1")),
167 hasThen(InnerMatcher: stmt().bind(ID: "a2"))));
168 ASSERT_TRUE(StmtMatch);
169 auto Stencil = cat(Parts: "if(!", Parts: node(ID: "a1"), Parts: ") ", Parts: node(ID: "UNBOUND"), Parts: ";");
170 auto ResultOrErr = Stencil->eval(R: 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`.
177void testExpr(StringRef Id, StringRef Snippet, const Stencil &Stencil,
178 StringRef Expected) {
179 auto StmtMatch = matchStmt(StatementCode: Snippet, Matcher: expr().bind(ID: 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(StatementCode: Snippet, Matcher: expr().bind(ID: 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, Snippet: "3;", Stencil: cat(Parts: node(ID: std::string(Id))), Expected: "3");
197}
198
199TEST_F(StencilTest, IfBoundOpBound) {
200 StringRef Id = "id";
201 testExpr(Id, Snippet: "3;", Stencil: ifBound(Id, TrueStencil: cat(Parts: "5"), FalseStencil: cat(Parts: "7")), Expected: "5");
202}
203
204TEST_F(StencilTest, IfBoundOpUnbound) {
205 StringRef Id = "id";
206 testExpr(Id, Snippet: "3;", Stencil: ifBound(Id: "other", TrueStencil: cat(Parts: "5"), FalseStencil: cat(Parts: "7")), Expected: "7");
207}
208
209static auto selectMatcher() {
210 // The `anything` matcher is not bound, to test for none of the cases
211 // matching.
212 return expr(anyOf(integerLiteral().bind(ID: "int"), cxxBoolLiteral().bind(ID: "bool"),
213 floatLiteral().bind(ID: "float"), anything()));
214}
215
216static auto selectStencil() {
217 return selectBound(CaseStencils: {
218 {"int", cat(Parts: "I")},
219 {"bool", cat(Parts: "B")},
220 {"bool", cat(Parts: "redundant")},
221 {"float", cat(Parts: "F")},
222 });
223}
224
225TEST_F(StencilTest, SelectBoundChooseDetectedMatch) {
226 std::string Input = "3;";
227 auto StmtMatch = matchStmt(StatementCode: Input, Matcher: 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(StatementCode: Input, Matcher: 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(StatementCode: Input, Matcher: 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(StatementCode: Input, Matcher: selectMatcher());
255 ASSERT_TRUE(StmtMatch);
256 auto Stencil = selectBound(CaseStencils: {{"int", cat(Parts: "I")}}, DefaultStencil: cat(Parts: "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, Snippet: "3;", Stencil: expression(Id), Expected: "3");
264}
265
266// Don't parenthesize a parens expression.
267TEST_F(StencilTest, ExpressionOpNoParensParens) {
268 StringRef Id = "id";
269 testExpr(Id, Snippet: "(3);", Stencil: expression(Id), Expected: "(3)");
270}
271
272TEST_F(StencilTest, ExpressionOpBinaryOpParens) {
273 StringRef Id = "id";
274 testExpr(Id, Snippet: "3+4;", Stencil: expression(Id), Expected: "(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.
280TEST_F(StencilTest, ExpressionOpUnbound) {
281 StringRef Id = "id";
282 testFailure(Id, Snippet: "3;", Stencil: expression(Id: "ACACA"),
283 MessageMatcher: AllOf(matchers: HasSubstr(substring: "ACACA"), matchers: HasSubstr(substring: "not bound")));
284}
285
286TEST_F(StencilTest, DerefPointer) {
287 StringRef Id = "id";
288 testExpr(Id, Snippet: "int *x; x;", Stencil: deref(ExprId: Id), Expected: "*x");
289}
290
291TEST_F(StencilTest, DerefBinOp) {
292 StringRef Id = "id";
293 testExpr(Id, Snippet: "int *x; x + 1;", Stencil: deref(ExprId: Id), Expected: "*(x + 1)");
294}
295
296TEST_F(StencilTest, DerefAddressExpr) {
297 StringRef Id = "id";
298 testExpr(Id, Snippet: "int x; &x;", Stencil: deref(ExprId: Id), Expected: "x");
299}
300
301TEST_F(StencilTest, AddressOfValue) {
302 StringRef Id = "id";
303 testExpr(Id, Snippet: "int x; x;", Stencil: addressOf(ExprId: Id), Expected: "&x");
304}
305
306TEST_F(StencilTest, AddressOfDerefExpr) {
307 StringRef Id = "id";
308 testExpr(Id, Snippet: "int *x; *x;", Stencil: addressOf(ExprId: Id), Expected: "x");
309}
310
311TEST_F(StencilTest, MaybeDerefValue) {
312 StringRef Id = "id";
313 testExpr(Id, Snippet: "int x; x;", Stencil: maybeDeref(ExprId: Id), Expected: "x");
314}
315
316TEST_F(StencilTest, MaybeDerefPointer) {
317 StringRef Id = "id";
318 testExpr(Id, Snippet: "int *x; x;", Stencil: maybeDeref(ExprId: Id), Expected: "*x");
319}
320
321TEST_F(StencilTest, MaybeDerefBinOp) {
322 StringRef Id = "id";
323 testExpr(Id, Snippet: "int *x; x + 1;", Stencil: maybeDeref(ExprId: Id), Expected: "*(x + 1)");
324}
325
326TEST_F(StencilTest, MaybeDerefAddressExpr) {
327 StringRef Id = "id";
328 testExpr(Id, Snippet: "int x; &x;", Stencil: maybeDeref(ExprId: Id), Expected: "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, Stencil: maybeDeref(ExprId: Id), Expected: "*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(StatementCode: Snippet, Matcher: memberExpr(hasObjectExpression(InnerMatcher: expr().bind(ID: Id))));
345 ASSERT_TRUE(StmtMatch);
346 const Stencil Stencil = maybeDeref(ExprId: Id);
347 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), HasValue("*x"));
348}
349
350TEST_F(StencilTest, MaybeAddressOfPointer) {
351 StringRef Id = "id";
352 testExpr(Id, Snippet: "int *x; x;", Stencil: maybeAddressOf(ExprId: Id), Expected: "x");
353}
354
355TEST_F(StencilTest, MaybeAddressOfValue) {
356 StringRef Id = "id";
357 testExpr(Id, Snippet: "int x; x;", Stencil: addressOf(ExprId: Id), Expected: "&x");
358}
359
360TEST_F(StencilTest, MaybeAddressOfBinOp) {
361 StringRef Id = "id";
362 testExpr(Id, Snippet: "int x; x + 1;", Stencil: maybeAddressOf(ExprId: Id), Expected: "&(x + 1)");
363}
364
365TEST_F(StencilTest, MaybeAddressOfDerefExpr) {
366 StringRef Id = "id";
367 testExpr(Id, Snippet: "int *x; *x;", Stencil: addressOf(ExprId: Id), Expected: "x");
368}
369
370TEST_F(StencilTest, MaybeAddressOfSmartPointer) {
371 StringRef Id = "id";
372 testExpr(Id, Snippet: "std::unique_ptr<S> x; x;", Stencil: maybeAddressOf(ExprId: Id), Expected: "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(StatementCode: Snippet, Matcher: memberExpr(hasObjectExpression(InnerMatcher: expr().bind(ID: Id))));
380 ASSERT_TRUE(StmtMatch);
381 const Stencil Stencil = maybeAddressOf(ExprId: Id);
382 EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), HasValue("x"));
383}
384
385TEST_F(StencilTest, MaybeAddressOfSmartPointerDerefNoCancel) {
386 StringRef Id = "id";
387 testExpr(Id, Snippet: "std::unique_ptr<S> x; *x;", Stencil: maybeAddressOf(ExprId: Id), Expected: "&*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, Stencil: access(BaseId: Id, Member: "field"), Expected: "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, Stencil: access(BaseId: Id, Member: cat(Parts: "field")), Expected: "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, Stencil: access(BaseId: Id, Member: "field"), Expected: "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, Stencil: access(BaseId: Id, Member: "field"), Expected: "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, Stencil: access(BaseId: Id, Member: "field"), Expected: "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, Stencil: access(BaseId: Id, Member: "field"), Expected: "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, Stencil: access(BaseId: Id, Member: "field"), Expected: "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(StatementCode: Snippet, Matcher: memberExpr(hasObjectExpression(InnerMatcher: expr().bind(ID: 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 StatementCode: Snippet,
480 Matcher: traverse(TK: TK_AsIs, InnerMatcher: returnStmt(hasReturnValue(InnerMatcher: ignoringImplicit(InnerMatcher: memberExpr(
481 hasObjectExpression(InnerMatcher: expr().bind(ID: "obj"))))))));
482 ASSERT_TRUE(StmtMatch);
483 const Stencil Stencil = access(BaseId: "obj", Member: "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(StatementCode: Snippet, Matcher: returnStmt(hasReturnValue(InnerMatcher: ignoringImplicit(InnerMatcher: memberExpr(
502 hasObjectExpression(InnerMatcher: expr().bind(ID: "obj")))))));
503 ASSERT_TRUE(StmtMatch);
504 const Stencil Stencil = access(BaseId: "obj", Member: "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(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "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(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "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(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "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(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "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(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "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(StatementCode: Snippet, Matcher: declRefExpr(hasType(InnerMatcher: qualType().bind(ID: "type"))));
563 ASSERT_TRUE(StmtMatch);
564 EXPECT_THAT_EXPECTED(describe("type")->eval(StmtMatch->Result),
565 HasValue(std::string(Expected)));
566}
567
568TEST_F(StencilTest, RunOp) {
569 StringRef Id = "id";
570 auto SimpleFn = [Id](const MatchResult &R) {
571 return std::string(R.Nodes.getNodeAs<Stmt>(ID: Id) != nullptr ? "Bound"
572 : "Unbound");
573 };
574 testExpr(Id, Snippet: "3;", Stencil: run(C: SimpleFn), Expected: "Bound");
575}
576
577TEST_F(StencilTest, CatOfMacroRangeSucceeds) {
578 StringRef Snippet = R"cpp(
579#define MACRO 3.77
580 double foo(double d);
581 foo(MACRO);)cpp";
582
583 auto StmtMatch =
584 matchStmt(StatementCode: Snippet, Matcher: callExpr(callee(InnerMatcher: functionDecl(hasName(Name: "foo"))),
585 argumentCountIs(N: 1),
586 hasArgument(N: 0, InnerMatcher: expr().bind(ID: "arg"))));
587 ASSERT_TRUE(StmtMatch);
588 Stencil S = cat(Parts: node(ID: "arg"));
589 EXPECT_THAT_EXPECTED(S->eval(StmtMatch->Result), HasValue("MACRO"));
590}
591
592TEST_F(StencilTest, CatOfMacroArgRangeSucceeds) {
593 StringRef Snippet = R"cpp(
594#define MACRO(a, b) a + b
595 MACRO(2, 3);)cpp";
596
597 auto StmtMatch =
598 matchStmt(StatementCode: Snippet, Matcher: binaryOperator(hasRHS(InnerMatcher: expr().bind(ID: "rhs"))));
599 ASSERT_TRUE(StmtMatch);
600 Stencil S = cat(Parts: node(ID: "rhs"));
601 EXPECT_THAT_EXPECTED(S->eval(StmtMatch->Result), HasValue("3"));
602}
603
604TEST_F(StencilTest, CatOfMacroArgSubRangeSucceeds) {
605 StringRef Snippet = R"cpp(
606#define MACRO(a, b) a + b
607 int foo(int);
608 MACRO(2, foo(3));)cpp";
609
610 auto StmtMatch = matchStmt(
611 StatementCode: Snippet, Matcher: binaryOperator(hasRHS(InnerMatcher: callExpr(
612 callee(InnerMatcher: functionDecl(hasName(Name: "foo"))), argumentCountIs(N: 1),
613 hasArgument(N: 0, InnerMatcher: expr().bind(ID: "arg"))))));
614 ASSERT_TRUE(StmtMatch);
615 Stencil S = cat(Parts: node(ID: "arg"));
616 EXPECT_THAT_EXPECTED(S->eval(StmtMatch->Result), HasValue("3"));
617}
618
619TEST_F(StencilTest, CatOfInvalidRangeFails) {
620 StringRef Snippet = R"cpp(
621#define MACRO (3.77)
622 double foo(double d);
623 foo(MACRO);)cpp";
624
625 auto StmtMatch =
626 matchStmt(StatementCode: Snippet, Matcher: callExpr(callee(InnerMatcher: functionDecl(hasName(Name: "foo"))),
627 argumentCountIs(N: 1),
628 hasArgument(N: 0, InnerMatcher: expr().bind(ID: "arg"))));
629 ASSERT_TRUE(StmtMatch);
630 Stencil S = cat(Parts: node(ID: "arg"));
631 Expected<std::string> Result = S->eval(R: StmtMatch->Result);
632 ASSERT_FALSE(Result);
633 llvm::handleAllErrors(E: Result.takeError(), Handlers: [](const llvm::StringError &E) {
634 EXPECT_THAT(E.getMessage(), AllOf(HasSubstr("selected range"),
635 HasSubstr("macro expansion")));
636 });
637}
638
639// The `StencilToStringTest` tests verify that the string representation of the
640// stencil combinator matches (as best possible) the spelling of the
641// combinator's construction. Exceptions include those combinators that have no
642// explicit spelling (like raw text) and those supporting non-printable
643// arguments (like `run`, `selection`).
644
645TEST(StencilToStringTest, RawTextOp) {
646 auto S = cat(Parts: "foo bar baz");
647 StringRef Expected = R"("foo bar baz")";
648 EXPECT_EQ(S->toString(), Expected);
649}
650
651TEST(StencilToStringTest, RawTextOpEscaping) {
652 auto S = cat(Parts: "foo \"bar\" baz\\n");
653 StringRef Expected = R"("foo \"bar\" baz\\n")";
654 EXPECT_EQ(S->toString(), Expected);
655}
656
657TEST(StencilToStringTest, DescribeOp) {
658 auto S = describe(Id: "Id");
659 StringRef Expected = R"repr(describe("Id"))repr";
660 EXPECT_EQ(S->toString(), Expected);
661}
662
663TEST(StencilToStringTest, DebugPrintNodeOp) {
664 auto S = dPrint(Id: "Id");
665 StringRef Expected = R"repr(dPrint("Id"))repr";
666 EXPECT_EQ(S->toString(), Expected);
667}
668
669TEST(StencilToStringTest, ExpressionOp) {
670 auto S = expression(Id: "Id");
671 StringRef Expected = R"repr(expression("Id"))repr";
672 EXPECT_EQ(S->toString(), Expected);
673}
674
675TEST(StencilToStringTest, DerefOp) {
676 auto S = deref(ExprId: "Id");
677 StringRef Expected = R"repr(deref("Id"))repr";
678 EXPECT_EQ(S->toString(), Expected);
679}
680
681TEST(StencilToStringTest, AddressOfOp) {
682 auto S = addressOf(ExprId: "Id");
683 StringRef Expected = R"repr(addressOf("Id"))repr";
684 EXPECT_EQ(S->toString(), Expected);
685}
686
687TEST(StencilToStringTest, SelectionOp) {
688 auto S1 = cat(Parts: node(ID: "node1"));
689 EXPECT_EQ(S1->toString(), "selection(...)");
690}
691
692TEST(StencilToStringTest, AccessOpText) {
693 auto S = access(BaseId: "Id", Member: "memberData");
694 StringRef Expected = R"repr(access("Id", "memberData"))repr";
695 EXPECT_EQ(S->toString(), Expected);
696}
697
698TEST(StencilToStringTest, AccessOpSelector) {
699 auto S = access(BaseId: "Id", Member: cat(Parts: name(ID: "otherId")));
700 StringRef Expected = R"repr(access("Id", selection(...)))repr";
701 EXPECT_EQ(S->toString(), Expected);
702}
703
704TEST(StencilToStringTest, AccessOpStencil) {
705 auto S = access(BaseId: "Id", Member: cat(Parts: "foo_", Parts: "bar"));
706 StringRef Expected = R"repr(access("Id", seq("foo_", "bar")))repr";
707 EXPECT_EQ(S->toString(), Expected);
708}
709
710TEST(StencilToStringTest, IfBoundOp) {
711 auto S = ifBound(Id: "Id", TrueStencil: cat(Parts: "trueText"), FalseStencil: access(BaseId: "exprId", Member: "memberData"));
712 StringRef Expected =
713 R"repr(ifBound("Id", "trueText", access("exprId", "memberData")))repr";
714 EXPECT_EQ(S->toString(), Expected);
715}
716
717TEST(StencilToStringTest, SelectBoundOp) {
718 auto S = selectBound(CaseStencils: {
719 {"int", cat(Parts: "I")},
720 {"float", cat(Parts: "F")},
721 });
722 StringRef Expected = R"repr(selectBound({{"int", "I"}, {"float", "F"}}))repr";
723 EXPECT_EQ(S->toString(), Expected);
724}
725
726TEST(StencilToStringTest, SelectBoundOpWithOneCase) {
727 auto S = selectBound(CaseStencils: {{"int", cat(Parts: "I")}});
728 StringRef Expected = R"repr(selectBound({{"int", "I"}}))repr";
729 EXPECT_EQ(S->toString(), Expected);
730}
731
732TEST(StencilToStringTest, SelectBoundOpWithDefault) {
733 auto S = selectBound(CaseStencils: {{"int", cat(Parts: "I")}, {"float", cat(Parts: "F")}}, DefaultStencil: cat(Parts: "D"));
734 StringRef Expected =
735 R"cc(selectBound({{"int", "I"}, {"float", "F"}}, "D"))cc";
736 EXPECT_EQ(S->toString(), Expected);
737}
738
739TEST(StencilToStringTest, RunOp) {
740 auto F1 = [](const MatchResult &R) { return "foo"; };
741 auto S1 = run(C: F1);
742 EXPECT_EQ(S1->toString(), "run(...)");
743}
744
745TEST(StencilToStringTest, Sequence) {
746 auto S = cat(Parts: "foo", Parts: access(BaseId: "x", Member: "m()"), Parts: "bar",
747 Parts: ifBound(Id: "x", TrueStencil: cat(Parts: "t"), FalseStencil: access(BaseId: "e", Member: "f")));
748 StringRef Expected = R"repr(seq("foo", access("x", "m()"), "bar", )repr"
749 R"repr(ifBound("x", "t", access("e", "f"))))repr";
750 EXPECT_EQ(S->toString(), Expected);
751}
752
753TEST(StencilToStringTest, SequenceEmpty) {
754 auto S = cat();
755 StringRef Expected = "seq()";
756 EXPECT_EQ(S->toString(), Expected);
757}
758
759TEST(StencilToStringTest, SequenceSingle) {
760 auto S = cat(Parts: "foo");
761 StringRef Expected = "\"foo\"";
762 EXPECT_EQ(S->toString(), Expected);
763}
764
765TEST(StencilToStringTest, SequenceFromVector) {
766 auto S = catVector(Parts: {cat(Parts: "foo"), access(BaseId: "x", Member: "m()"), cat(Parts: "bar"),
767 ifBound(Id: "x", TrueStencil: cat(Parts: "t"), FalseStencil: access(BaseId: "e", Member: "f"))});
768 StringRef Expected = R"repr(seq("foo", access("x", "m()"), "bar", )repr"
769 R"repr(ifBound("x", "t", access("e", "f"))))repr";
770 EXPECT_EQ(S->toString(), Expected);
771}
772} // namespace
773

source code of clang/unittests/Tooling/StencilTest.cpp