1 //===- unittests/AST/ASTTraverserTest.h------------------------------------===//
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/AST/ASTContext.h"
10 #include "clang/AST/ASTNodeTraverser.h"
11 #include "clang/AST/TextNodeDumper.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Tooling/Tooling.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17
18 using namespace clang::tooling;
19 using namespace clang::ast_matchers;
20
21 namespace clang {
22
23 class NodeTreePrinter : public TextTreeStructure {
24 llvm::raw_ostream &OS;
25
26 public:
NodeTreePrinter(llvm::raw_ostream & OS)27 NodeTreePrinter(llvm::raw_ostream &OS)
28 : TextTreeStructure(OS, /* showColors */ false), OS(OS) {}
29
Visit(const Decl * D)30 void Visit(const Decl *D) {
31 OS << D->getDeclKindName() << "Decl";
32 if (auto *ND = dyn_cast<NamedDecl>(D)) {
33 OS << " '" << ND->getDeclName() << "'";
34 }
35 }
36
Visit(const Stmt * S)37 void Visit(const Stmt *S) {
38 if (!S) {
39 OS << "<<<NULL>>>";
40 return;
41 }
42 OS << S->getStmtClassName();
43 if (auto *E = dyn_cast<DeclRefExpr>(S)) {
44 OS << " '" << E->getDecl()->getDeclName() << "'";
45 }
46 }
47
Visit(QualType QT)48 void Visit(QualType QT) {
49 OS << "QualType " << QT.split().Quals.getAsString();
50 }
51
Visit(const Type * T)52 void Visit(const Type *T) { OS << T->getTypeClassName() << "Type"; }
53
Visit(const comments::Comment * C,const comments::FullComment * FC)54 void Visit(const comments::Comment *C, const comments::FullComment *FC) {
55 OS << C->getCommentKindName();
56 }
57
Visit(const CXXCtorInitializer * Init)58 void Visit(const CXXCtorInitializer *Init) {
59 OS << "CXXCtorInitializer";
60 if (const auto *F = Init->getAnyMember()) {
61 OS << " '" << F->getNameAsString() << "'";
62 } else if (auto const *TSI = Init->getTypeSourceInfo()) {
63 OS << " '" << TSI->getType().getAsString() << "'";
64 }
65 }
66
Visit(const Attr * A)67 void Visit(const Attr *A) {
68 switch (A->getKind()) {
69 #define ATTR(X) \
70 case attr::X: \
71 OS << #X; \
72 break;
73 #include "clang/Basic/AttrList.inc"
74 }
75 OS << "Attr";
76 }
77
Visit(const OMPClause * C)78 void Visit(const OMPClause *C) { OS << "OMPClause"; }
Visit(const TemplateArgument & A,SourceRange R={},const Decl * From=nullptr,const char * Label=nullptr)79 void Visit(const TemplateArgument &A, SourceRange R = {},
80 const Decl *From = nullptr, const char *Label = nullptr) {
81 OS << "TemplateArgument";
82 switch (A.getKind()) {
83 case TemplateArgument::Type: {
84 OS << " type " << A.getAsType().getAsString();
85 break;
86 }
87 default:
88 break;
89 }
90 }
91
Visit(T...)92 template <typename... T> void Visit(T...) {}
93 };
94
95 class TestASTDumper : public ASTNodeTraverser<TestASTDumper, NodeTreePrinter> {
96
97 NodeTreePrinter MyNodeRecorder;
98
99 public:
TestASTDumper(llvm::raw_ostream & OS)100 TestASTDumper(llvm::raw_ostream &OS) : MyNodeRecorder(OS) {}
doGetNodeDelegate()101 NodeTreePrinter &doGetNodeDelegate() { return MyNodeRecorder; }
102 };
103
dumpASTString(NodeType &&...N)104 template <typename... NodeType> std::string dumpASTString(NodeType &&... N) {
105 std::string Buffer;
106 llvm::raw_string_ostream OS(Buffer);
107
108 TestASTDumper Dumper(OS);
109
110 OS << "\n";
111
112 Dumper.Visit(std::forward<NodeType &&>(N)...);
113
114 return OS.str();
115 }
116
117 template <typename... NodeType>
dumpASTString(TraversalKind TK,NodeType &&...N)118 std::string dumpASTString(TraversalKind TK, NodeType &&... N) {
119 std::string Buffer;
120 llvm::raw_string_ostream OS(Buffer);
121
122 TestASTDumper Dumper(OS);
123 Dumper.SetTraversalKind(TK);
124
125 OS << "\n";
126
127 Dumper.Visit(std::forward<NodeType &&>(N)...);
128
129 return OS.str();
130 }
131
getFunctionNode(clang::ASTUnit * AST,const std::string & Name)132 const FunctionDecl *getFunctionNode(clang::ASTUnit *AST,
133 const std::string &Name) {
134 auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"),
135 AST->getASTContext());
136 EXPECT_EQ(Result.size(), 1u);
137 return Result[0].getNodeAs<FunctionDecl>("fn");
138 }
139
140 template <typename T> struct Verifier {
withDynNodeclang::Verifier141 static void withDynNode(T Node, const std::string &DumpString) {
142 EXPECT_EQ(dumpASTString(DynTypedNode::create(Node)), DumpString);
143 }
144 };
145
146 template <typename T> struct Verifier<T *> {
withDynNodeclang::Verifier147 static void withDynNode(T *Node, const std::string &DumpString) {
148 EXPECT_EQ(dumpASTString(DynTypedNode::create(*Node)), DumpString);
149 }
150 };
151
152 template <typename T>
verifyWithDynNode(T Node,const std::string & DumpString)153 void verifyWithDynNode(T Node, const std::string &DumpString) {
154 EXPECT_EQ(dumpASTString(Node), DumpString);
155
156 Verifier<T>::withDynNode(Node, DumpString);
157 }
158
TEST(Traverse,Dump)159 TEST(Traverse, Dump) {
160
161 auto AST = buildASTFromCode(R"cpp(
162 struct A {
163 int m_number;
164
165 /// CTor
166 A() : m_number(42) {}
167
168 [[nodiscard]] const int func() {
169 return 42;
170 }
171
172 };
173
174 template<typename T>
175 struct templ
176 {
177 };
178
179 template<>
180 struct templ<int>
181 {
182 };
183
184 void parmvardecl_attr(struct A __attribute__((address_space(19)))*);
185
186 )cpp");
187
188 const FunctionDecl *Func = getFunctionNode(AST.get(), "func");
189
190 verifyWithDynNode(Func,
191 R"cpp(
192 CXXMethodDecl 'func'
193 |-CompoundStmt
194 | `-ReturnStmt
195 | `-IntegerLiteral
196 `-WarnUnusedResultAttr
197 )cpp");
198
199 Stmt *Body = Func->getBody();
200
201 verifyWithDynNode(Body,
202 R"cpp(
203 CompoundStmt
204 `-ReturnStmt
205 `-IntegerLiteral
206 )cpp");
207
208 QualType QT = Func->getType();
209
210 verifyWithDynNode(QT,
211 R"cpp(
212 FunctionProtoType
213 `-QualType const
214 `-BuiltinType
215 )cpp");
216
217 const FunctionDecl *CTorFunc = getFunctionNode(AST.get(), "A");
218
219 verifyWithDynNode(CTorFunc->getType(),
220 R"cpp(
221 FunctionProtoType
222 `-BuiltinType
223 )cpp");
224
225 Attr *A = *Func->attr_begin();
226
227 {
228 std::string expectedString = R"cpp(
229 WarnUnusedResultAttr
230 )cpp";
231
232 EXPECT_EQ(dumpASTString(A), expectedString);
233 }
234
235 auto *CTor = dyn_cast<CXXConstructorDecl>(CTorFunc);
236 const CXXCtorInitializer *Init = *CTor->init_begin();
237
238 verifyWithDynNode(Init,
239 R"cpp(
240 CXXCtorInitializer 'm_number'
241 `-IntegerLiteral
242 )cpp");
243
244 const comments::FullComment *Comment =
245 AST->getASTContext().getLocalCommentForDeclUncached(CTorFunc);
246 {
247 std::string expectedString = R"cpp(
248 FullComment
249 `-ParagraphComment
250 `-TextComment
251 )cpp";
252 EXPECT_EQ(dumpASTString(Comment, Comment), expectedString);
253 }
254
255 auto Result = ast_matchers::match(
256 classTemplateSpecializationDecl(hasName("templ")).bind("fn"),
257 AST->getASTContext());
258 EXPECT_EQ(Result.size(), 1u);
259 auto Templ = Result[0].getNodeAs<ClassTemplateSpecializationDecl>("fn");
260
261 TemplateArgument TA = Templ->getTemplateArgs()[0];
262
263 verifyWithDynNode(TA,
264 R"cpp(
265 TemplateArgument type int
266 `-BuiltinType
267 )cpp");
268
269 Func = getFunctionNode(AST.get(), "parmvardecl_attr");
270
271 const auto *Parm = Func->getParamDecl(0);
272 const auto TL = Parm->getTypeSourceInfo()->getTypeLoc();
273 ASSERT_TRUE(TL.getType()->isPointerType());
274
275 const auto ATL = TL.getNextTypeLoc().getAs<AttributedTypeLoc>();
276 const auto *AS = cast<AddressSpaceAttr>(ATL.getAttr());
277 EXPECT_EQ(toTargetAddressSpace(static_cast<LangAS>(AS->getAddressSpace())),
278 19u);
279 }
280
TEST(Traverse,IgnoreUnlessSpelledInSourceVars)281 TEST(Traverse, IgnoreUnlessSpelledInSourceVars) {
282
283 auto AST = buildASTFromCode(R"cpp(
284
285 struct String
286 {
287 String(const char*, int = -1) {}
288
289 int overloaded() const;
290 int& overloaded();
291 };
292
293 void stringConstruct()
294 {
295 String s = "foo";
296 s = "bar";
297 }
298
299 void overloadCall()
300 {
301 String s = "foo";
302 (s).overloaded();
303 }
304
305 struct C1 {};
306 struct C2 { operator C1(); };
307
308 void conversionOperator()
309 {
310 C2* c2;
311 C1 c1 = (*c2);
312 }
313
314 template <unsigned alignment>
315 void template_test() {
316 static_assert(alignment, "");
317 }
318 void actual_template_test() {
319 template_test<4>();
320 }
321
322 struct OneParamCtor {
323 explicit OneParamCtor(int);
324 };
325 struct TwoParamCtor {
326 explicit TwoParamCtor(int, int);
327 };
328
329 void varDeclCtors() {
330 {
331 auto var1 = OneParamCtor(5);
332 auto var2 = TwoParamCtor(6, 7);
333 }
334 {
335 OneParamCtor var3(5);
336 TwoParamCtor var4(6, 7);
337 }
338 int i = 0;
339 {
340 auto var5 = OneParamCtor(i);
341 auto var6 = TwoParamCtor(i, 7);
342 }
343 {
344 OneParamCtor var7(i);
345 TwoParamCtor var8(i, 7);
346 }
347 }
348
349 )cpp");
350
351 {
352 auto FN =
353 ast_matchers::match(functionDecl(hasName("stringConstruct")).bind("fn"),
354 AST->getASTContext());
355 EXPECT_EQ(FN.size(), 1u);
356
357 EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("fn")),
358 R"cpp(
359 FunctionDecl 'stringConstruct'
360 `-CompoundStmt
361 |-DeclStmt
362 | `-VarDecl 's'
363 | `-ExprWithCleanups
364 | `-CXXConstructExpr
365 | `-MaterializeTemporaryExpr
366 | `-ImplicitCastExpr
367 | `-CXXConstructExpr
368 | |-ImplicitCastExpr
369 | | `-StringLiteral
370 | `-CXXDefaultArgExpr
371 `-ExprWithCleanups
372 `-CXXOperatorCallExpr
373 |-ImplicitCastExpr
374 | `-DeclRefExpr 'operator='
375 |-DeclRefExpr 's'
376 `-MaterializeTemporaryExpr
377 `-CXXConstructExpr
378 |-ImplicitCastExpr
379 | `-StringLiteral
380 `-CXXDefaultArgExpr
381 )cpp");
382
383 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
384 FN[0].getNodeAs<Decl>("fn")),
385 R"cpp(
386 FunctionDecl 'stringConstruct'
387 `-CompoundStmt
388 |-DeclStmt
389 | `-VarDecl 's'
390 | `-StringLiteral
391 `-CXXOperatorCallExpr
392 |-DeclRefExpr 'operator='
393 |-DeclRefExpr 's'
394 `-StringLiteral
395 )cpp");
396 }
397
398 {
399 auto FN =
400 ast_matchers::match(functionDecl(hasName("overloadCall")).bind("fn"),
401 AST->getASTContext());
402 EXPECT_EQ(FN.size(), 1u);
403
404 EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("fn")),
405 R"cpp(
406 FunctionDecl 'overloadCall'
407 `-CompoundStmt
408 |-DeclStmt
409 | `-VarDecl 's'
410 | `-ExprWithCleanups
411 | `-CXXConstructExpr
412 | `-MaterializeTemporaryExpr
413 | `-ImplicitCastExpr
414 | `-CXXConstructExpr
415 | |-ImplicitCastExpr
416 | | `-StringLiteral
417 | `-CXXDefaultArgExpr
418 `-CXXMemberCallExpr
419 `-MemberExpr
420 `-ParenExpr
421 `-DeclRefExpr 's'
422 )cpp");
423
424 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
425 FN[0].getNodeAs<Decl>("fn")),
426 R"cpp(
427 FunctionDecl 'overloadCall'
428 `-CompoundStmt
429 |-DeclStmt
430 | `-VarDecl 's'
431 | `-StringLiteral
432 `-CXXMemberCallExpr
433 `-MemberExpr
434 `-DeclRefExpr 's'
435 )cpp");
436 }
437
438 {
439 auto FN = ast_matchers::match(
440 functionDecl(hasName("conversionOperator"),
441 hasDescendant(varDecl(hasName("c1")).bind("var"))),
442 AST->getASTContext());
443 EXPECT_EQ(FN.size(), 1u);
444
445 EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("var")),
446 R"cpp(
447 VarDecl 'c1'
448 `-ExprWithCleanups
449 `-CXXConstructExpr
450 `-MaterializeTemporaryExpr
451 `-ImplicitCastExpr
452 `-CXXMemberCallExpr
453 `-MemberExpr
454 `-ParenExpr
455 `-UnaryOperator
456 `-ImplicitCastExpr
457 `-DeclRefExpr 'c2'
458 )cpp");
459
460 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
461 FN[0].getNodeAs<Decl>("var")),
462 R"cpp(
463 VarDecl 'c1'
464 `-UnaryOperator
465 `-DeclRefExpr 'c2'
466 )cpp");
467 }
468
469 {
470 auto FN = ast_matchers::match(
471 functionDecl(hasName("template_test"),
472 hasDescendant(staticAssertDecl().bind("staticAssert"))),
473 AST->getASTContext());
474 EXPECT_EQ(FN.size(), 2u);
475
476 EXPECT_EQ(dumpASTString(TK_AsIs, FN[1].getNodeAs<Decl>("staticAssert")),
477 R"cpp(
478 StaticAssertDecl
479 |-ImplicitCastExpr
480 | `-SubstNonTypeTemplateParmExpr
481 | |-NonTypeTemplateParmDecl 'alignment'
482 | `-IntegerLiteral
483 `-StringLiteral
484 )cpp");
485
486 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
487 FN[1].getNodeAs<Decl>("staticAssert")),
488 R"cpp(
489 StaticAssertDecl
490 |-IntegerLiteral
491 `-StringLiteral
492 )cpp");
493 }
494
495 auto varChecker = [&AST](StringRef varName, StringRef SemanticDump,
496 StringRef SyntacticDump) {
497 auto FN = ast_matchers::match(
498 functionDecl(
499 hasName("varDeclCtors"),
500 forEachDescendant(varDecl(hasName(varName)).bind("varDeclCtor"))),
501 AST->getASTContext());
502 EXPECT_EQ(FN.size(), 1u);
503
504 EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("varDeclCtor")),
505 SemanticDump);
506
507 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
508 FN[0].getNodeAs<Decl>("varDeclCtor")),
509 SyntacticDump);
510 };
511
512 varChecker("var1",
513 R"cpp(
514 VarDecl 'var1'
515 `-ExprWithCleanups
516 `-CXXConstructExpr
517 `-MaterializeTemporaryExpr
518 `-CXXFunctionalCastExpr
519 `-CXXConstructExpr
520 `-IntegerLiteral
521 )cpp",
522 R"cpp(
523 VarDecl 'var1'
524 `-CXXConstructExpr
525 `-IntegerLiteral
526 )cpp");
527
528 varChecker("var2",
529 R"cpp(
530 VarDecl 'var2'
531 `-ExprWithCleanups
532 `-CXXConstructExpr
533 `-MaterializeTemporaryExpr
534 `-CXXTemporaryObjectExpr
535 |-IntegerLiteral
536 `-IntegerLiteral
537 )cpp",
538 R"cpp(
539 VarDecl 'var2'
540 `-CXXTemporaryObjectExpr
541 |-IntegerLiteral
542 `-IntegerLiteral
543 )cpp");
544
545 varChecker("var3",
546 R"cpp(
547 VarDecl 'var3'
548 `-CXXConstructExpr
549 `-IntegerLiteral
550 )cpp",
551 R"cpp(
552 VarDecl 'var3'
553 `-CXXConstructExpr
554 `-IntegerLiteral
555 )cpp");
556
557 varChecker("var4",
558 R"cpp(
559 VarDecl 'var4'
560 `-CXXConstructExpr
561 |-IntegerLiteral
562 `-IntegerLiteral
563 )cpp",
564 R"cpp(
565 VarDecl 'var4'
566 `-CXXConstructExpr
567 |-IntegerLiteral
568 `-IntegerLiteral
569 )cpp");
570
571 varChecker("var5",
572 R"cpp(
573 VarDecl 'var5'
574 `-ExprWithCleanups
575 `-CXXConstructExpr
576 `-MaterializeTemporaryExpr
577 `-CXXFunctionalCastExpr
578 `-CXXConstructExpr
579 `-ImplicitCastExpr
580 `-DeclRefExpr 'i'
581 )cpp",
582 R"cpp(
583 VarDecl 'var5'
584 `-CXXConstructExpr
585 `-DeclRefExpr 'i'
586 )cpp");
587
588 varChecker("var6",
589 R"cpp(
590 VarDecl 'var6'
591 `-ExprWithCleanups
592 `-CXXConstructExpr
593 `-MaterializeTemporaryExpr
594 `-CXXTemporaryObjectExpr
595 |-ImplicitCastExpr
596 | `-DeclRefExpr 'i'
597 `-IntegerLiteral
598 )cpp",
599 R"cpp(
600 VarDecl 'var6'
601 `-CXXTemporaryObjectExpr
602 |-DeclRefExpr 'i'
603 `-IntegerLiteral
604 )cpp");
605
606 varChecker("var7",
607 R"cpp(
608 VarDecl 'var7'
609 `-CXXConstructExpr
610 `-ImplicitCastExpr
611 `-DeclRefExpr 'i'
612 )cpp",
613 R"cpp(
614 VarDecl 'var7'
615 `-CXXConstructExpr
616 `-DeclRefExpr 'i'
617 )cpp");
618
619 varChecker("var8",
620 R"cpp(
621 VarDecl 'var8'
622 `-CXXConstructExpr
623 |-ImplicitCastExpr
624 | `-DeclRefExpr 'i'
625 `-IntegerLiteral
626 )cpp",
627 R"cpp(
628 VarDecl 'var8'
629 `-CXXConstructExpr
630 |-DeclRefExpr 'i'
631 `-IntegerLiteral
632 )cpp");
633 }
634
TEST(Traverse,IgnoreUnlessSpelledInSourceStructs)635 TEST(Traverse, IgnoreUnlessSpelledInSourceStructs) {
636 auto AST = buildASTFromCode(R"cpp(
637
638 struct MyStruct {
639 MyStruct();
640 MyStruct(int i) {
641 MyStruct();
642 }
643 ~MyStruct();
644 };
645
646 )cpp");
647
648 auto BN = ast_matchers::match(
649 cxxConstructorDecl(hasName("MyStruct"),
650 hasParameter(0, parmVarDecl(hasType(isInteger()))))
651 .bind("ctor"),
652 AST->getASTContext());
653 EXPECT_EQ(BN.size(), 1u);
654
655 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
656 BN[0].getNodeAs<Decl>("ctor")),
657 R"cpp(
658 CXXConstructorDecl 'MyStruct'
659 |-ParmVarDecl 'i'
660 `-CompoundStmt
661 `-CXXTemporaryObjectExpr
662 )cpp");
663
664 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("ctor")),
665 R"cpp(
666 CXXConstructorDecl 'MyStruct'
667 |-ParmVarDecl 'i'
668 `-CompoundStmt
669 `-ExprWithCleanups
670 `-CXXBindTemporaryExpr
671 `-CXXTemporaryObjectExpr
672 )cpp");
673 }
674
TEST(Traverse,IgnoreUnlessSpelledInSourceReturnStruct)675 TEST(Traverse, IgnoreUnlessSpelledInSourceReturnStruct) {
676
677 auto AST = buildASTFromCode(R"cpp(
678 struct Retval {
679 Retval() {}
680 ~Retval() {}
681 };
682
683 Retval someFun();
684
685 void foo()
686 {
687 someFun();
688 }
689 )cpp");
690
691 auto BN = ast_matchers::match(functionDecl(hasName("foo")).bind("fn"),
692 AST->getASTContext());
693 EXPECT_EQ(BN.size(), 1u);
694
695 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
696 BN[0].getNodeAs<Decl>("fn")),
697 R"cpp(
698 FunctionDecl 'foo'
699 `-CompoundStmt
700 `-CallExpr
701 `-DeclRefExpr 'someFun'
702 )cpp");
703
704 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("fn")),
705 R"cpp(
706 FunctionDecl 'foo'
707 `-CompoundStmt
708 `-ExprWithCleanups
709 `-CXXBindTemporaryExpr
710 `-CallExpr
711 `-ImplicitCastExpr
712 `-DeclRefExpr 'someFun'
713 )cpp");
714 }
715
TEST(Traverse,IgnoreUnlessSpelledInSourceReturns)716 TEST(Traverse, IgnoreUnlessSpelledInSourceReturns) {
717
718 auto AST = buildASTFromCode(R"cpp(
719
720 struct A
721 {
722 };
723
724 struct B
725 {
726 B(int);
727 B(A const& a);
728 B();
729 };
730
731 struct C
732 {
733 operator B();
734 };
735
736 B func1() {
737 return 42;
738 }
739
740 B func2() {
741 return B{42};
742 }
743
744 B func3() {
745 return B(42);
746 }
747
748 B func4() {
749 return B();
750 }
751
752 B func5() {
753 return B{};
754 }
755
756 B func6() {
757 return C();
758 }
759
760 B func7() {
761 return A();
762 }
763
764 B func8() {
765 return C{};
766 }
767
768 B func9() {
769 return A{};
770 }
771
772 B func10() {
773 A a;
774 return a;
775 }
776
777 B func11() {
778 B b;
779 return b;
780 }
781
782 B func12() {
783 C c;
784 return c;
785 }
786
787 )cpp");
788
789 auto getFunctionNode = [&AST](const std::string &name) {
790 auto BN = ast_matchers::match(functionDecl(hasName(name)).bind("fn"),
791 AST->getASTContext());
792 EXPECT_EQ(BN.size(), 1u);
793 return BN[0].getNodeAs<Decl>("fn");
794 };
795
796 {
797 auto FN = getFunctionNode("func1");
798 llvm::StringRef Expected = R"cpp(
799 FunctionDecl 'func1'
800 `-CompoundStmt
801 `-ReturnStmt
802 `-ExprWithCleanups
803 `-CXXConstructExpr
804 `-MaterializeTemporaryExpr
805 `-ImplicitCastExpr
806 `-CXXConstructExpr
807 `-IntegerLiteral
808 )cpp";
809
810 EXPECT_EQ(dumpASTString(TK_AsIs, FN), Expected);
811
812 Expected = R"cpp(
813 FunctionDecl 'func1'
814 `-CompoundStmt
815 `-ReturnStmt
816 `-IntegerLiteral
817 )cpp";
818 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, FN), Expected);
819 }
820
821 llvm::StringRef Expected = R"cpp(
822 FunctionDecl 'func2'
823 `-CompoundStmt
824 `-ReturnStmt
825 `-CXXTemporaryObjectExpr
826 `-IntegerLiteral
827 )cpp";
828 EXPECT_EQ(
829 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func2")),
830 Expected);
831
832 Expected = R"cpp(
833 FunctionDecl 'func3'
834 `-CompoundStmt
835 `-ReturnStmt
836 `-CXXConstructExpr
837 `-IntegerLiteral
838 )cpp";
839 EXPECT_EQ(
840 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func3")),
841 Expected);
842
843 Expected = R"cpp(
844 FunctionDecl 'func4'
845 `-CompoundStmt
846 `-ReturnStmt
847 `-CXXTemporaryObjectExpr
848 )cpp";
849 EXPECT_EQ(
850 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func4")),
851 Expected);
852
853 Expected = R"cpp(
854 FunctionDecl 'func5'
855 `-CompoundStmt
856 `-ReturnStmt
857 `-CXXTemporaryObjectExpr
858 )cpp";
859 EXPECT_EQ(
860 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func5")),
861 Expected);
862
863 Expected = R"cpp(
864 FunctionDecl 'func6'
865 `-CompoundStmt
866 `-ReturnStmt
867 `-CXXTemporaryObjectExpr
868 )cpp";
869 EXPECT_EQ(
870 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func6")),
871 Expected);
872
873 Expected = R"cpp(
874 FunctionDecl 'func7'
875 `-CompoundStmt
876 `-ReturnStmt
877 `-CXXTemporaryObjectExpr
878 )cpp";
879 EXPECT_EQ(
880 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func7")),
881 Expected);
882
883 Expected = R"cpp(
884 FunctionDecl 'func8'
885 `-CompoundStmt
886 `-ReturnStmt
887 `-CXXFunctionalCastExpr
888 `-InitListExpr
889 )cpp";
890 EXPECT_EQ(
891 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func8")),
892 Expected);
893
894 Expected = R"cpp(
895 FunctionDecl 'func9'
896 `-CompoundStmt
897 `-ReturnStmt
898 `-CXXFunctionalCastExpr
899 `-InitListExpr
900 )cpp";
901 EXPECT_EQ(
902 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func9")),
903 Expected);
904
905 Expected = R"cpp(
906 FunctionDecl 'func10'
907 `-CompoundStmt
908 |-DeclStmt
909 | `-VarDecl 'a'
910 | `-CXXConstructExpr
911 `-ReturnStmt
912 `-DeclRefExpr 'a'
913 )cpp";
914 EXPECT_EQ(
915 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func10")),
916 Expected);
917
918 Expected = R"cpp(
919 FunctionDecl 'func11'
920 `-CompoundStmt
921 |-DeclStmt
922 | `-VarDecl 'b'
923 | `-CXXConstructExpr
924 `-ReturnStmt
925 `-DeclRefExpr 'b'
926 )cpp";
927 EXPECT_EQ(
928 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func11")),
929 Expected);
930
931 Expected = R"cpp(
932 FunctionDecl 'func12'
933 `-CompoundStmt
934 |-DeclStmt
935 | `-VarDecl 'c'
936 | `-CXXConstructExpr
937 `-ReturnStmt
938 `-DeclRefExpr 'c'
939 )cpp";
940 EXPECT_EQ(
941 dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func12")),
942 Expected);
943 }
944
TEST(Traverse,LambdaUnlessSpelledInSource)945 TEST(Traverse, LambdaUnlessSpelledInSource) {
946
947 auto AST =
948 buildASTFromCodeWithArgs(R"cpp(
949
950 void captures() {
951 int a = 0;
952 int b = 0;
953 int d = 0;
954 int f = 0;
955
956 [a, &b, c = d, &e = f](int g, int h = 42) {};
957 }
958
959 void templated() {
960 int a = 0;
961 [a]<typename T>(T t) {};
962 }
963
964 struct SomeStruct {
965 int a = 0;
966 void capture_this() {
967 [this]() {};
968 }
969 void capture_this_copy() {
970 [self = *this]() {};
971 }
972 };
973 )cpp",
974 {"-Wno-unused-value", "-Wno-c++2a-extensions"});
975
976 auto getLambdaNode = [&AST](const std::string &name) {
977 auto BN = ast_matchers::match(
978 lambdaExpr(hasAncestor(functionDecl(hasName(name)))).bind("lambda"),
979 AST->getASTContext());
980 EXPECT_EQ(BN.size(), 1u);
981 return BN[0].getNodeAs<LambdaExpr>("lambda");
982 };
983
984 {
985 auto L = getLambdaNode("captures");
986
987 llvm::StringRef Expected = R"cpp(
988 LambdaExpr
989 |-DeclRefExpr 'a'
990 |-DeclRefExpr 'b'
991 |-VarDecl 'c'
992 | `-DeclRefExpr 'd'
993 |-VarDecl 'e'
994 | `-DeclRefExpr 'f'
995 |-ParmVarDecl 'g'
996 |-ParmVarDecl 'h'
997 | `-IntegerLiteral
998 `-CompoundStmt
999 )cpp";
1000 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected);
1001
1002 Expected = R"cpp(
1003 LambdaExpr
1004 |-CXXRecordDecl ''
1005 | |-CXXMethodDecl 'operator()'
1006 | | |-ParmVarDecl 'g'
1007 | | |-ParmVarDecl 'h'
1008 | | | `-IntegerLiteral
1009 | | `-CompoundStmt
1010 | |-FieldDecl ''
1011 | |-FieldDecl ''
1012 | |-FieldDecl ''
1013 | |-FieldDecl ''
1014 | `-CXXDestructorDecl '~'
1015 |-ImplicitCastExpr
1016 | `-DeclRefExpr 'a'
1017 |-DeclRefExpr 'b'
1018 |-ImplicitCastExpr
1019 | `-DeclRefExpr 'd'
1020 |-DeclRefExpr 'f'
1021 `-CompoundStmt
1022 )cpp";
1023 EXPECT_EQ(dumpASTString(TK_AsIs, L), Expected);
1024 }
1025
1026 {
1027 auto L = getLambdaNode("templated");
1028
1029 llvm::StringRef Expected = R"cpp(
1030 LambdaExpr
1031 |-DeclRefExpr 'a'
1032 |-TemplateTypeParmDecl 'T'
1033 |-ParmVarDecl 't'
1034 `-CompoundStmt
1035 )cpp";
1036 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected);
1037 }
1038
1039 {
1040 auto L = getLambdaNode("capture_this");
1041
1042 llvm::StringRef Expected = R"cpp(
1043 LambdaExpr
1044 |-CXXThisExpr
1045 `-CompoundStmt
1046 )cpp";
1047 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected);
1048 }
1049
1050 {
1051 auto L = getLambdaNode("capture_this_copy");
1052
1053 llvm::StringRef Expected = R"cpp(
1054 LambdaExpr
1055 |-VarDecl 'self'
1056 | `-UnaryOperator
1057 | `-CXXThisExpr
1058 `-CompoundStmt
1059 )cpp";
1060 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected);
1061 }
1062 }
1063
TEST(Traverse,IgnoreUnlessSpelledInSourceImplicit)1064 TEST(Traverse, IgnoreUnlessSpelledInSourceImplicit) {
1065 {
1066 auto AST = buildASTFromCode(R"cpp(
1067 int i = 0;
1068 )cpp");
1069 const auto *TUDecl = AST->getASTContext().getTranslationUnitDecl();
1070
1071 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, TUDecl),
1072 R"cpp(
1073 TranslationUnitDecl
1074 `-VarDecl 'i'
1075 `-IntegerLiteral
1076 )cpp");
1077 }
1078
1079 auto AST2 = buildASTFromCodeWithArgs(R"cpp(
1080 struct Simple {
1081 };
1082 struct Other {
1083 };
1084
1085 struct Record : Simple, Other {
1086 Record() : Simple(), m_i(42) {}
1087 private:
1088 int m_i;
1089 int m_i2 = 42;
1090 Simple m_s;
1091 };
1092
1093 struct NonTrivial {
1094 NonTrivial() {}
1095 NonTrivial(NonTrivial&) {}
1096 NonTrivial& operator=(NonTrivial&) { return *this; }
1097
1098 ~NonTrivial() {}
1099 };
1100
1101 struct ContainsArray {
1102 NonTrivial arr[2];
1103 int irr[2];
1104 ContainsArray& operator=(ContainsArray &) = default;
1105 };
1106
1107 void copyIt()
1108 {
1109 ContainsArray ca;
1110 ContainsArray ca2;
1111 ca2 = ca;
1112 }
1113
1114 void forLoop()
1115 {
1116 int arr[2];
1117 for (auto i : arr)
1118 {
1119
1120 }
1121 for (auto& a = arr; auto i : a)
1122 {
1123
1124 }
1125 }
1126
1127 struct DefaultedAndDeleted {
1128 NonTrivial nt;
1129 DefaultedAndDeleted() = default;
1130 ~DefaultedAndDeleted() = default;
1131 DefaultedAndDeleted(DefaultedAndDeleted &) = default;
1132 DefaultedAndDeleted& operator=(DefaultedAndDeleted &) = default;
1133 DefaultedAndDeleted(DefaultedAndDeleted &&) = delete;
1134 DefaultedAndDeleted& operator=(DefaultedAndDeleted &&) = delete;
1135 };
1136
1137 void copyIt2()
1138 {
1139 DefaultedAndDeleted ca;
1140 DefaultedAndDeleted ca2;
1141 ca2 = ca;
1142 }
1143
1144 void hasDefaultArg(int i, int j = 0)
1145 {
1146 }
1147 void callDefaultArg()
1148 {
1149 hasDefaultArg(42);
1150 }
1151 )cpp",
1152 {"-std=c++20"});
1153
1154 {
1155 auto BN = ast_matchers::match(
1156 cxxRecordDecl(hasName("Record"), unless(isImplicit())).bind("rec"),
1157 AST2->getASTContext());
1158 EXPECT_EQ(BN.size(), 1u);
1159
1160 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
1161 R"cpp(
1162 CXXRecordDecl 'Record'
1163 |-CXXRecordDecl 'Record'
1164 |-CXXConstructorDecl 'Record'
1165 | |-CXXCtorInitializer 'struct Simple'
1166 | | `-CXXConstructExpr
1167 | |-CXXCtorInitializer 'struct Other'
1168 | | `-CXXConstructExpr
1169 | |-CXXCtorInitializer 'm_i'
1170 | | `-IntegerLiteral
1171 | |-CXXCtorInitializer 'm_i2'
1172 | | `-CXXDefaultInitExpr
1173 | |-CXXCtorInitializer 'm_s'
1174 | | `-CXXConstructExpr
1175 | `-CompoundStmt
1176 |-AccessSpecDecl
1177 |-FieldDecl 'm_i'
1178 |-FieldDecl 'm_i2'
1179 | `-IntegerLiteral
1180 `-FieldDecl 'm_s'
1181 )cpp");
1182
1183 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1184 BN[0].getNodeAs<Decl>("rec")),
1185 R"cpp(
1186 CXXRecordDecl 'Record'
1187 |-CXXConstructorDecl 'Record'
1188 | |-CXXCtorInitializer 'struct Simple'
1189 | | `-CXXConstructExpr
1190 | |-CXXCtorInitializer 'm_i'
1191 | | `-IntegerLiteral
1192 | `-CompoundStmt
1193 |-AccessSpecDecl
1194 |-FieldDecl 'm_i'
1195 |-FieldDecl 'm_i2'
1196 | `-IntegerLiteral
1197 `-FieldDecl 'm_s'
1198 )cpp");
1199 }
1200 {
1201 auto BN = ast_matchers::match(
1202 cxxRecordDecl(hasName("ContainsArray"), unless(isImplicit()))
1203 .bind("rec"),
1204 AST2->getASTContext());
1205 EXPECT_EQ(BN.size(), 1u);
1206
1207 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
1208 R"cpp(
1209 CXXRecordDecl 'ContainsArray'
1210 |-CXXRecordDecl 'ContainsArray'
1211 |-FieldDecl 'arr'
1212 |-FieldDecl 'irr'
1213 |-CXXMethodDecl 'operator='
1214 | |-ParmVarDecl ''
1215 | `-CompoundStmt
1216 | |-ForStmt
1217 | | |-DeclStmt
1218 | | | `-VarDecl '__i0'
1219 | | | `-IntegerLiteral
1220 | | |-<<<NULL>>>
1221 | | |-BinaryOperator
1222 | | | |-ImplicitCastExpr
1223 | | | | `-DeclRefExpr '__i0'
1224 | | | `-IntegerLiteral
1225 | | |-UnaryOperator
1226 | | | `-DeclRefExpr '__i0'
1227 | | `-CXXMemberCallExpr
1228 | | |-MemberExpr
1229 | | | `-ArraySubscriptExpr
1230 | | | |-ImplicitCastExpr
1231 | | | | `-MemberExpr
1232 | | | | `-CXXThisExpr
1233 | | | `-ImplicitCastExpr
1234 | | | `-DeclRefExpr '__i0'
1235 | | `-ArraySubscriptExpr
1236 | | |-ImplicitCastExpr
1237 | | | `-MemberExpr
1238 | | | `-DeclRefExpr ''
1239 | | `-ImplicitCastExpr
1240 | | `-DeclRefExpr '__i0'
1241 | |-CallExpr
1242 | | |-ImplicitCastExpr
1243 | | | `-DeclRefExpr '__builtin_memcpy'
1244 | | |-ImplicitCastExpr
1245 | | | `-UnaryOperator
1246 | | | `-MemberExpr
1247 | | | `-CXXThisExpr
1248 | | |-ImplicitCastExpr
1249 | | | `-UnaryOperator
1250 | | | `-MemberExpr
1251 | | | `-DeclRefExpr ''
1252 | | `-IntegerLiteral
1253 | `-ReturnStmt
1254 | `-UnaryOperator
1255 | `-CXXThisExpr
1256 |-CXXConstructorDecl 'ContainsArray'
1257 | `-ParmVarDecl ''
1258 |-CXXDestructorDecl '~ContainsArray'
1259 | `-CompoundStmt
1260 `-CXXConstructorDecl 'ContainsArray'
1261 |-CXXCtorInitializer 'arr'
1262 | `-CXXConstructExpr
1263 `-CompoundStmt
1264 )cpp");
1265
1266 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1267 BN[0].getNodeAs<Decl>("rec")),
1268 R"cpp(
1269 CXXRecordDecl 'ContainsArray'
1270 |-FieldDecl 'arr'
1271 |-FieldDecl 'irr'
1272 `-CXXMethodDecl 'operator='
1273 `-ParmVarDecl ''
1274 )cpp");
1275 }
1276 {
1277 auto BN = ast_matchers::match(functionDecl(hasName("forLoop")).bind("func"),
1278 AST2->getASTContext());
1279 EXPECT_EQ(BN.size(), 1u);
1280
1281 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("func")),
1282 R"cpp(
1283 FunctionDecl 'forLoop'
1284 `-CompoundStmt
1285 |-DeclStmt
1286 | `-VarDecl 'arr'
1287 |-CXXForRangeStmt
1288 | |-<<<NULL>>>
1289 | |-DeclStmt
1290 | | `-VarDecl '__range1'
1291 | | `-DeclRefExpr 'arr'
1292 | |-DeclStmt
1293 | | `-VarDecl '__begin1'
1294 | | `-ImplicitCastExpr
1295 | | `-DeclRefExpr '__range1'
1296 | |-DeclStmt
1297 | | `-VarDecl '__end1'
1298 | | `-BinaryOperator
1299 | | |-ImplicitCastExpr
1300 | | | `-DeclRefExpr '__range1'
1301 | | `-IntegerLiteral
1302 | |-BinaryOperator
1303 | | |-ImplicitCastExpr
1304 | | | `-DeclRefExpr '__begin1'
1305 | | `-ImplicitCastExpr
1306 | | `-DeclRefExpr '__end1'
1307 | |-UnaryOperator
1308 | | `-DeclRefExpr '__begin1'
1309 | |-DeclStmt
1310 | | `-VarDecl 'i'
1311 | | `-ImplicitCastExpr
1312 | | `-UnaryOperator
1313 | | `-ImplicitCastExpr
1314 | | `-DeclRefExpr '__begin1'
1315 | `-CompoundStmt
1316 `-CXXForRangeStmt
1317 |-DeclStmt
1318 | `-VarDecl 'a'
1319 | `-DeclRefExpr 'arr'
1320 |-DeclStmt
1321 | `-VarDecl '__range1'
1322 | `-DeclRefExpr 'a'
1323 |-DeclStmt
1324 | `-VarDecl '__begin1'
1325 | `-ImplicitCastExpr
1326 | `-DeclRefExpr '__range1'
1327 |-DeclStmt
1328 | `-VarDecl '__end1'
1329 | `-BinaryOperator
1330 | |-ImplicitCastExpr
1331 | | `-DeclRefExpr '__range1'
1332 | `-IntegerLiteral
1333 |-BinaryOperator
1334 | |-ImplicitCastExpr
1335 | | `-DeclRefExpr '__begin1'
1336 | `-ImplicitCastExpr
1337 | `-DeclRefExpr '__end1'
1338 |-UnaryOperator
1339 | `-DeclRefExpr '__begin1'
1340 |-DeclStmt
1341 | `-VarDecl 'i'
1342 | `-ImplicitCastExpr
1343 | `-UnaryOperator
1344 | `-ImplicitCastExpr
1345 | `-DeclRefExpr '__begin1'
1346 `-CompoundStmt
1347 )cpp");
1348
1349 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1350 BN[0].getNodeAs<Decl>("func")),
1351 R"cpp(
1352 FunctionDecl 'forLoop'
1353 `-CompoundStmt
1354 |-DeclStmt
1355 | `-VarDecl 'arr'
1356 |-CXXForRangeStmt
1357 | |-<<<NULL>>>
1358 | |-VarDecl 'i'
1359 | |-DeclRefExpr 'arr'
1360 | `-CompoundStmt
1361 `-CXXForRangeStmt
1362 |-DeclStmt
1363 | `-VarDecl 'a'
1364 | `-DeclRefExpr 'arr'
1365 |-VarDecl 'i'
1366 |-DeclRefExpr 'a'
1367 `-CompoundStmt
1368 )cpp");
1369 }
1370 {
1371 auto BN = ast_matchers::match(
1372 cxxRecordDecl(hasName("DefaultedAndDeleted"), unless(isImplicit()))
1373 .bind("rec"),
1374 AST2->getASTContext());
1375 EXPECT_EQ(BN.size(), 1u);
1376
1377 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
1378 R"cpp(
1379 CXXRecordDecl 'DefaultedAndDeleted'
1380 |-CXXRecordDecl 'DefaultedAndDeleted'
1381 |-FieldDecl 'nt'
1382 |-CXXConstructorDecl 'DefaultedAndDeleted'
1383 | |-CXXCtorInitializer 'nt'
1384 | | `-CXXConstructExpr
1385 | `-CompoundStmt
1386 |-CXXDestructorDecl '~DefaultedAndDeleted'
1387 | `-CompoundStmt
1388 |-CXXConstructorDecl 'DefaultedAndDeleted'
1389 | `-ParmVarDecl ''
1390 |-CXXMethodDecl 'operator='
1391 | |-ParmVarDecl ''
1392 | `-CompoundStmt
1393 | |-CXXMemberCallExpr
1394 | | |-MemberExpr
1395 | | | `-MemberExpr
1396 | | | `-CXXThisExpr
1397 | | `-MemberExpr
1398 | | `-DeclRefExpr ''
1399 | `-ReturnStmt
1400 | `-UnaryOperator
1401 | `-CXXThisExpr
1402 |-CXXConstructorDecl 'DefaultedAndDeleted'
1403 | `-ParmVarDecl ''
1404 `-CXXMethodDecl 'operator='
1405 `-ParmVarDecl ''
1406 )cpp");
1407
1408 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1409 BN[0].getNodeAs<Decl>("rec")),
1410 R"cpp(
1411 CXXRecordDecl 'DefaultedAndDeleted'
1412 |-FieldDecl 'nt'
1413 |-CXXConstructorDecl 'DefaultedAndDeleted'
1414 |-CXXDestructorDecl '~DefaultedAndDeleted'
1415 |-CXXConstructorDecl 'DefaultedAndDeleted'
1416 | `-ParmVarDecl ''
1417 |-CXXMethodDecl 'operator='
1418 | `-ParmVarDecl ''
1419 |-CXXConstructorDecl 'DefaultedAndDeleted'
1420 | `-ParmVarDecl ''
1421 `-CXXMethodDecl 'operator='
1422 `-ParmVarDecl ''
1423 )cpp");
1424 }
1425 {
1426 auto BN = ast_matchers::match(
1427 callExpr(callee(functionDecl(hasName("hasDefaultArg"))))
1428 .bind("funcCall"),
1429 AST2->getASTContext());
1430 EXPECT_EQ(BN.size(), 1u);
1431
1432 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<CallExpr>("funcCall")),
1433 R"cpp(
1434 CallExpr
1435 |-ImplicitCastExpr
1436 | `-DeclRefExpr 'hasDefaultArg'
1437 |-IntegerLiteral
1438 `-CXXDefaultArgExpr
1439 )cpp");
1440 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1441 BN[0].getNodeAs<CallExpr>("funcCall")),
1442 R"cpp(
1443 CallExpr
1444 |-DeclRefExpr 'hasDefaultArg'
1445 `-IntegerLiteral
1446 )cpp");
1447 }
1448 }
1449
TEST(Traverse,IgnoreUnlessSpelledInSourceTemplateInstantiations)1450 TEST(Traverse, IgnoreUnlessSpelledInSourceTemplateInstantiations) {
1451
1452 auto AST = buildASTFromCode(R"cpp(
1453 template<typename T>
1454 struct TemplStruct {
1455 TemplStruct() {}
1456 ~TemplStruct() {}
1457
1458 private:
1459 T m_t;
1460 };
1461
1462 template<typename T>
1463 T timesTwo(T input)
1464 {
1465 return input * 2;
1466 }
1467
1468 void instantiate()
1469 {
1470 TemplStruct<int> ti;
1471 TemplStruct<double> td;
1472 (void)timesTwo<int>(2);
1473 (void)timesTwo<double>(2);
1474 }
1475
1476 template class TemplStruct<float>;
1477
1478 extern template class TemplStruct<long>;
1479
1480 template<> class TemplStruct<bool> {
1481 TemplStruct() {}
1482 ~TemplStruct() {}
1483
1484 void foo() {}
1485 private:
1486 bool m_t;
1487 };
1488
1489 // Explicit instantiation of template functions do not appear in the AST
1490 template float timesTwo(float);
1491
1492 template<> bool timesTwo<bool>(bool) {
1493 return true;
1494 }
1495 )cpp");
1496 {
1497 auto BN = ast_matchers::match(
1498 classTemplateDecl(hasName("TemplStruct")).bind("rec"),
1499 AST->getASTContext());
1500 EXPECT_EQ(BN.size(), 1u);
1501
1502 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1503 BN[0].getNodeAs<Decl>("rec")),
1504 R"cpp(
1505 ClassTemplateDecl 'TemplStruct'
1506 |-TemplateTypeParmDecl 'T'
1507 `-CXXRecordDecl 'TemplStruct'
1508 |-CXXConstructorDecl 'TemplStruct<T>'
1509 | `-CompoundStmt
1510 |-CXXDestructorDecl '~TemplStruct<T>'
1511 | `-CompoundStmt
1512 |-AccessSpecDecl
1513 `-FieldDecl 'm_t'
1514 )cpp");
1515
1516 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
1517 R"cpp(
1518 ClassTemplateDecl 'TemplStruct'
1519 |-TemplateTypeParmDecl 'T'
1520 |-CXXRecordDecl 'TemplStruct'
1521 | |-CXXRecordDecl 'TemplStruct'
1522 | |-CXXConstructorDecl 'TemplStruct<T>'
1523 | | `-CompoundStmt
1524 | |-CXXDestructorDecl '~TemplStruct<T>'
1525 | | `-CompoundStmt
1526 | |-AccessSpecDecl
1527 | `-FieldDecl 'm_t'
1528 |-ClassTemplateSpecializationDecl 'TemplStruct'
1529 | |-TemplateArgument type int
1530 | | `-BuiltinType
1531 | |-CXXRecordDecl 'TemplStruct'
1532 | |-CXXConstructorDecl 'TemplStruct'
1533 | | `-CompoundStmt
1534 | |-CXXDestructorDecl '~TemplStruct'
1535 | | `-CompoundStmt
1536 | |-AccessSpecDecl
1537 | |-FieldDecl 'm_t'
1538 | `-CXXConstructorDecl 'TemplStruct'
1539 | `-ParmVarDecl ''
1540 |-ClassTemplateSpecializationDecl 'TemplStruct'
1541 | |-TemplateArgument type double
1542 | | `-BuiltinType
1543 | |-CXXRecordDecl 'TemplStruct'
1544 | |-CXXConstructorDecl 'TemplStruct'
1545 | | `-CompoundStmt
1546 | |-CXXDestructorDecl '~TemplStruct'
1547 | | `-CompoundStmt
1548 | |-AccessSpecDecl
1549 | |-FieldDecl 'm_t'
1550 | `-CXXConstructorDecl 'TemplStruct'
1551 | `-ParmVarDecl ''
1552 |-ClassTemplateSpecializationDecl 'TemplStruct'
1553 | |-TemplateArgument type float
1554 | | `-BuiltinType
1555 | |-CXXRecordDecl 'TemplStruct'
1556 | |-CXXConstructorDecl 'TemplStruct'
1557 | | `-CompoundStmt
1558 | |-CXXDestructorDecl '~TemplStruct'
1559 | | `-CompoundStmt
1560 | |-AccessSpecDecl
1561 | `-FieldDecl 'm_t'
1562 |-ClassTemplateSpecializationDecl 'TemplStruct'
1563 | |-TemplateArgument type long
1564 | | `-BuiltinType
1565 | |-CXXRecordDecl 'TemplStruct'
1566 | |-CXXConstructorDecl 'TemplStruct'
1567 | |-CXXDestructorDecl '~TemplStruct'
1568 | |-AccessSpecDecl
1569 | `-FieldDecl 'm_t'
1570 `-ClassTemplateSpecializationDecl 'TemplStruct'
1571 |-TemplateArgument type _Bool
1572 | `-BuiltinType
1573 |-CXXRecordDecl 'TemplStruct'
1574 |-CXXConstructorDecl 'TemplStruct'
1575 | `-CompoundStmt
1576 |-CXXDestructorDecl '~TemplStruct'
1577 | `-CompoundStmt
1578 |-CXXMethodDecl 'foo'
1579 | `-CompoundStmt
1580 |-AccessSpecDecl
1581 `-FieldDecl 'm_t'
1582 )cpp");
1583 }
1584 {
1585 auto BN = ast_matchers::match(
1586 classTemplateSpecializationDecl(
1587 hasTemplateArgument(
1588 0, templateArgument(refersToType(asString("_Bool")))))
1589 .bind("templSpec"),
1590 AST->getASTContext());
1591 EXPECT_EQ(BN.size(), 1u);
1592
1593 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("templSpec")),
1594 R"cpp(
1595 ClassTemplateSpecializationDecl 'TemplStruct'
1596 |-TemplateArgument type _Bool
1597 | `-BuiltinType
1598 |-CXXRecordDecl 'TemplStruct'
1599 |-CXXConstructorDecl 'TemplStruct'
1600 | `-CompoundStmt
1601 |-CXXDestructorDecl '~TemplStruct'
1602 | `-CompoundStmt
1603 |-CXXMethodDecl 'foo'
1604 | `-CompoundStmt
1605 |-AccessSpecDecl
1606 `-FieldDecl 'm_t'
1607 )cpp");
1608
1609 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1610 BN[0].getNodeAs<Decl>("templSpec")),
1611 R"cpp(
1612 ClassTemplateSpecializationDecl 'TemplStruct'
1613 |-TemplateArgument type _Bool
1614 | `-BuiltinType
1615 |-CXXConstructorDecl 'TemplStruct'
1616 | `-CompoundStmt
1617 |-CXXDestructorDecl '~TemplStruct'
1618 | `-CompoundStmt
1619 |-CXXMethodDecl 'foo'
1620 | `-CompoundStmt
1621 |-AccessSpecDecl
1622 `-FieldDecl 'm_t'
1623 )cpp");
1624 }
1625 {
1626 auto BN = ast_matchers::match(
1627 functionTemplateDecl(hasName("timesTwo")).bind("fn"),
1628 AST->getASTContext());
1629 EXPECT_EQ(BN.size(), 1u);
1630
1631 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1632 BN[0].getNodeAs<Decl>("fn")),
1633 R"cpp(
1634 FunctionTemplateDecl 'timesTwo'
1635 |-TemplateTypeParmDecl 'T'
1636 `-FunctionDecl 'timesTwo'
1637 |-ParmVarDecl 'input'
1638 `-CompoundStmt
1639 `-ReturnStmt
1640 `-BinaryOperator
1641 |-DeclRefExpr 'input'
1642 `-IntegerLiteral
1643 )cpp");
1644
1645 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("fn")),
1646 R"cpp(
1647 FunctionTemplateDecl 'timesTwo'
1648 |-TemplateTypeParmDecl 'T'
1649 |-FunctionDecl 'timesTwo'
1650 | |-ParmVarDecl 'input'
1651 | `-CompoundStmt
1652 | `-ReturnStmt
1653 | `-BinaryOperator
1654 | |-DeclRefExpr 'input'
1655 | `-IntegerLiteral
1656 |-FunctionDecl 'timesTwo'
1657 | |-TemplateArgument type int
1658 | | `-BuiltinType
1659 | |-ParmVarDecl 'input'
1660 | `-CompoundStmt
1661 | `-ReturnStmt
1662 | `-BinaryOperator
1663 | |-ImplicitCastExpr
1664 | | `-DeclRefExpr 'input'
1665 | `-IntegerLiteral
1666 |-FunctionDecl 'timesTwo'
1667 | |-TemplateArgument type double
1668 | | `-BuiltinType
1669 | |-ParmVarDecl 'input'
1670 | `-CompoundStmt
1671 | `-ReturnStmt
1672 | `-BinaryOperator
1673 | |-ImplicitCastExpr
1674 | | `-DeclRefExpr 'input'
1675 | `-ImplicitCastExpr
1676 | `-IntegerLiteral
1677 |-FunctionDecl 'timesTwo'
1678 | |-TemplateArgument type float
1679 | | `-BuiltinType
1680 | |-ParmVarDecl 'input'
1681 | `-CompoundStmt
1682 | `-ReturnStmt
1683 | `-BinaryOperator
1684 | |-ImplicitCastExpr
1685 | | `-DeclRefExpr 'input'
1686 | `-ImplicitCastExpr
1687 | `-IntegerLiteral
1688 |-FunctionDecl 'timesTwo'
1689 | |-TemplateArgument type _Bool
1690 | | `-BuiltinType
1691 | |-ParmVarDecl ''
1692 | `-CompoundStmt
1693 | `-ReturnStmt
1694 | `-CXXBoolLiteralExpr
1695 `-FunctionDecl 'timesTwo'
1696 |-TemplateArgument type _Bool
1697 | `-BuiltinType
1698 `-ParmVarDecl 'input'
1699 )cpp");
1700 }
1701 {
1702 auto BN = ast_matchers::match(
1703 classTemplateSpecializationDecl(
1704 hasName("TemplStruct"),
1705 hasTemplateArgument(
1706 0, templateArgument(refersToType(asString("float")))),
1707 hasParent(translationUnitDecl()))
1708 .bind("rec"),
1709 AST->getASTContext());
1710 EXPECT_EQ(BN.size(), 1u);
1711
1712 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1713 BN[0].getNodeAs<Decl>("rec")),
1714 R"cpp(
1715 ClassTemplateSpecializationDecl 'TemplStruct'
1716 `-TemplateArgument type float
1717 `-BuiltinType
1718 )cpp");
1719
1720 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
1721 R"cpp(
1722 ClassTemplateSpecializationDecl 'TemplStruct'
1723 |-TemplateArgument type float
1724 | `-BuiltinType
1725 |-CXXRecordDecl 'TemplStruct'
1726 |-CXXConstructorDecl 'TemplStruct'
1727 | `-CompoundStmt
1728 |-CXXDestructorDecl '~TemplStruct'
1729 | `-CompoundStmt
1730 |-AccessSpecDecl
1731 `-FieldDecl 'm_t'
1732 )cpp");
1733 }
1734 }
1735
TEST(Traverse,CXXRewrittenBinaryOperator)1736 TEST(Traverse, CXXRewrittenBinaryOperator) {
1737
1738 auto AST = buildASTFromCodeWithArgs(R"cpp(
1739 namespace std {
1740 struct strong_ordering {
1741 int n;
1742 constexpr operator int() const { return n; }
1743 static const strong_ordering equal, greater, less;
1744 };
1745 constexpr strong_ordering strong_ordering::equal = {0};
1746 constexpr strong_ordering strong_ordering::greater = {1};
1747 constexpr strong_ordering strong_ordering::less = {-1};
1748 }
1749
1750 struct HasSpaceshipMem {
1751 int a;
1752 constexpr auto operator<=>(const HasSpaceshipMem&) const = default;
1753 };
1754
1755 void binop()
1756 {
1757 HasSpaceshipMem hs1, hs2;
1758 if (hs1 < hs2)
1759 return;
1760 }
1761 )cpp",
1762 {"-std=c++20"});
1763 {
1764 auto BN = ast_matchers::match(cxxRewrittenBinaryOperator().bind("binop"),
1765 AST->getASTContext());
1766 EXPECT_EQ(BN.size(), 1u);
1767
1768 EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Stmt>("binop")),
1769 R"cpp(
1770 CXXRewrittenBinaryOperator
1771 `-BinaryOperator
1772 |-ImplicitCastExpr
1773 | `-CXXMemberCallExpr
1774 | `-MemberExpr
1775 | `-ImplicitCastExpr
1776 | `-MaterializeTemporaryExpr
1777 | `-CXXOperatorCallExpr
1778 | |-ImplicitCastExpr
1779 | | `-DeclRefExpr 'operator<=>'
1780 | |-ImplicitCastExpr
1781 | | `-DeclRefExpr 'hs1'
1782 | `-ImplicitCastExpr
1783 | `-DeclRefExpr 'hs2'
1784 `-IntegerLiteral
1785 )cpp");
1786 EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
1787 BN[0].getNodeAs<Stmt>("binop")),
1788 R"cpp(
1789 CXXRewrittenBinaryOperator
1790 |-DeclRefExpr 'hs1'
1791 `-DeclRefExpr 'hs2'
1792 )cpp");
1793 }
1794 }
1795
1796 } // namespace clang
1797