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

source code of clang/unittests/AST/ASTTraverserTest.cpp