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:
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     OS << S->getStmtClassName();
39     if (auto *E = dyn_cast<DeclRefExpr>(S)) {
40       OS << " '" << E->getDecl()->getDeclName() << "'";
41     }
42   }
43 
44   void Visit(QualType QT) {
45     OS << "QualType " << QT.split().Quals.getAsString();
46   }
47 
48   void Visit(const Type *T) { OS << T->getTypeClassName() << "Type"; }
49 
50   void Visit(const comments::Comment *C, const comments::FullComment *FC) {
51     OS << C->getCommentKindName();
52   }
53 
54   void Visit(const CXXCtorInitializer *Init) { OS << "CXXCtorInitializer"; }
55 
56   void Visit(const Attr *A) {
57     switch (A->getKind()) {
58 #define ATTR(X)                                                                \
59   case attr::X:                                                                \
60     OS << #X;                                                                  \
61     break;
62 #include "clang/Basic/AttrList.inc"
63     }
64     OS << "Attr";
65   }
66 
67   void Visit(const OMPClause *C) { OS << "OMPClause"; }
68   void Visit(const TemplateArgument &A, SourceRange R = {},
69              const Decl *From = nullptr, const char *Label = nullptr) {
70     OS << "TemplateArgument";
71   }
72 
73   template <typename... T> void Visit(T...) {}
74 };
75 
76 class TestASTDumper : public ASTNodeTraverser<TestASTDumper, NodeTreePrinter> {
77 
78   NodeTreePrinter MyNodeRecorder;
79 
80 public:
81   TestASTDumper(llvm::raw_ostream &OS) : MyNodeRecorder(OS) {}
82   NodeTreePrinter &doGetNodeDelegate() { return MyNodeRecorder; }
83 };
84 
85 template <typename... NodeType> std::string dumpASTString(NodeType &&... N) {
86   std::string Buffer;
87   llvm::raw_string_ostream OS(Buffer);
88 
89   TestASTDumper Dumper(OS);
90 
91   OS << "\n";
92 
93   Dumper.Visit(std::forward<NodeType &&>(N)...);
94 
95   return OS.str();
96 }
97 
98 template <typename... NodeType>
99 std::string dumpASTString(ast_type_traits::TraversalKind TK, NodeType &&... N) {
100   std::string Buffer;
101   llvm::raw_string_ostream OS(Buffer);
102 
103   TestASTDumper Dumper(OS);
104   Dumper.SetTraversalKind(TK);
105 
106   OS << "\n";
107 
108   Dumper.Visit(std::forward<NodeType &&>(N)...);
109 
110   return OS.str();
111 }
112 
113 const FunctionDecl *getFunctionNode(clang::ASTUnit *AST,
114                                     const std::string &Name) {
115   auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"),
116                                     AST->getASTContext());
117   EXPECT_EQ(Result.size(), 1u);
118   return Result[0].getNodeAs<FunctionDecl>("fn");
119 }
120 
121 template <typename T> struct Verifier {
122   static void withDynNode(T Node, const std::string &DumpString) {
123     EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(Node)),
124               DumpString);
125   }
126 };
127 
128 template <typename T> struct Verifier<T *> {
129   static void withDynNode(T *Node, const std::string &DumpString) {
130     EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(*Node)),
131               DumpString);
132   }
133 };
134 
135 template <typename T>
136 void verifyWithDynNode(T Node, const std::string &DumpString) {
137   EXPECT_EQ(dumpASTString(Node), DumpString);
138 
139   Verifier<T>::withDynNode(Node, DumpString);
140 }
141 
142 TEST(Traverse, Dump) {
143 
144   auto AST = buildASTFromCode(R"cpp(
145 struct A {
146   int m_number;
147 
148   /// CTor
149   A() : m_number(42) {}
150 
151   [[nodiscard]] const int func() {
152     return 42;
153   }
154 
155 };
156 
157 template<typename T>
158 struct templ
159 {
160 };
161 
162 template<>
163 struct templ<int>
164 {
165 };
166 
167 void parmvardecl_attr(struct A __attribute__((address_space(19)))*);
168 
169 )cpp");
170 
171   const FunctionDecl *Func = getFunctionNode(AST.get(), "func");
172 
173   verifyWithDynNode(Func,
174                     R"cpp(
175 CXXMethodDecl 'func'
176 |-CompoundStmt
177 | `-ReturnStmt
178 |   `-IntegerLiteral
179 `-WarnUnusedResultAttr
180 )cpp");
181 
182   Stmt *Body = Func->getBody();
183 
184   verifyWithDynNode(Body,
185                     R"cpp(
186 CompoundStmt
187 `-ReturnStmt
188   `-IntegerLiteral
189 )cpp");
190 
191   QualType QT = Func->getType();
192 
193   verifyWithDynNode(QT,
194                     R"cpp(
195 FunctionProtoType
196 `-QualType const
197   `-BuiltinType
198 )cpp");
199 
200   const FunctionDecl *CTorFunc = getFunctionNode(AST.get(), "A");
201 
202   verifyWithDynNode(CTorFunc->getType(),
203                     R"cpp(
204 FunctionProtoType
205 `-BuiltinType
206 )cpp");
207 
208   Attr *A = *Func->attr_begin();
209 
210   {
211     std::string expectedString = R"cpp(
212 WarnUnusedResultAttr
213 )cpp";
214 
215     EXPECT_EQ(dumpASTString(A), expectedString);
216   }
217 
218   auto *CTor = dyn_cast<CXXConstructorDecl>(CTorFunc);
219   const CXXCtorInitializer *Init = *CTor->init_begin();
220 
221   verifyWithDynNode(Init,
222                     R"cpp(
223 CXXCtorInitializer
224 `-IntegerLiteral
225 )cpp");
226 
227   const comments::FullComment *Comment =
228       AST->getASTContext().getLocalCommentForDeclUncached(CTorFunc);
229   {
230     std::string expectedString = R"cpp(
231 FullComment
232 `-ParagraphComment
233   `-TextComment
234 )cpp";
235     EXPECT_EQ(dumpASTString(Comment, Comment), expectedString);
236   }
237 
238   auto Result = ast_matchers::match(
239       classTemplateSpecializationDecl(hasName("templ")).bind("fn"),
240       AST->getASTContext());
241   EXPECT_EQ(Result.size(), 1u);
242   auto Templ = Result[0].getNodeAs<ClassTemplateSpecializationDecl>("fn");
243 
244   TemplateArgument TA = Templ->getTemplateArgs()[0];
245 
246   verifyWithDynNode(TA,
247                     R"cpp(
248 TemplateArgument
249 )cpp");
250 
251   Func = getFunctionNode(AST.get(), "parmvardecl_attr");
252 
253   const auto *Parm = Func->getParamDecl(0);
254   const auto TL = Parm->getTypeSourceInfo()->getTypeLoc();
255   ASSERT_TRUE(TL.getType()->isPointerType());
256 
257   const auto ATL = TL.getNextTypeLoc().getAs<AttributedTypeLoc>();
258   const auto *AS = cast<AddressSpaceAttr>(ATL.getAttr());
259   EXPECT_EQ(toTargetAddressSpace(static_cast<LangAS>(AS->getAddressSpace())),
260             19u);
261 }
262 
263 TEST(Traverse, IgnoreUnlessSpelledInSourceStructs) {
264   auto AST = buildASTFromCode(R"cpp(
265 
266 struct MyStruct {
267   MyStruct();
268   MyStruct(int i) {
269     MyStruct();
270   }
271   ~MyStruct();
272 };
273 
274 )cpp");
275 
276   auto BN = ast_matchers::match(
277       cxxConstructorDecl(hasName("MyStruct"),
278                          hasParameter(0, parmVarDecl(hasType(isInteger()))))
279           .bind("ctor"),
280       AST->getASTContext());
281   EXPECT_EQ(BN.size(), 1u);
282 
283   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
284                           BN[0].getNodeAs<Decl>("ctor")),
285             R"cpp(
286 CXXConstructorDecl 'MyStruct'
287 |-ParmVarDecl 'i'
288 `-CompoundStmt
289   `-CXXTemporaryObjectExpr
290 )cpp");
291 
292   EXPECT_EQ(
293       dumpASTString(ast_type_traits::TK_AsIs, BN[0].getNodeAs<Decl>("ctor")),
294       R"cpp(
295 CXXConstructorDecl 'MyStruct'
296 |-ParmVarDecl 'i'
297 `-CompoundStmt
298   `-ExprWithCleanups
299     `-CXXBindTemporaryExpr
300       `-CXXTemporaryObjectExpr
301 )cpp");
302 }
303 
304 TEST(Traverse, IgnoreUnlessSpelledInSourceReturnStruct) {
305 
306   auto AST = buildASTFromCode(R"cpp(
307 struct Retval {
308   Retval() {}
309   ~Retval() {}
310 };
311 
312 Retval someFun();
313 
314 void foo()
315 {
316     someFun();
317 }
318 )cpp");
319 
320   auto BN = ast_matchers::match(functionDecl(hasName("foo")).bind("fn"),
321                                 AST->getASTContext());
322   EXPECT_EQ(BN.size(), 1u);
323 
324   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
325                           BN[0].getNodeAs<Decl>("fn")),
326             R"cpp(
327 FunctionDecl 'foo'
328 `-CompoundStmt
329   `-CallExpr
330     `-DeclRefExpr 'someFun'
331 )cpp");
332 
333   EXPECT_EQ(
334       dumpASTString(ast_type_traits::TK_AsIs, BN[0].getNodeAs<Decl>("fn")),
335       R"cpp(
336 FunctionDecl 'foo'
337 `-CompoundStmt
338   `-ExprWithCleanups
339     `-CXXBindTemporaryExpr
340       `-CallExpr
341         `-ImplicitCastExpr
342           `-DeclRefExpr 'someFun'
343 )cpp");
344 }
345 
346 TEST(Traverse, IgnoreUnlessSpelledInSourceReturns) {
347 
348   auto AST = buildASTFromCode(R"cpp(
349 
350 struct A
351 {
352 };
353 
354 struct B
355 {
356   B(int);
357   B(A const& a);
358   B();
359 };
360 
361 struct C
362 {
363   operator B();
364 };
365 
366 B func1() {
367   return 42;
368 }
369 
370 B func2() {
371   return B{42};
372 }
373 
374 B func3() {
375   return B(42);
376 }
377 
378 B func4() {
379   return B();
380 }
381 
382 B func5() {
383   return B{};
384 }
385 
386 B func6() {
387   return C();
388 }
389 
390 B func7() {
391   return A();
392 }
393 
394 B func8() {
395   return C{};
396 }
397 
398 B func9() {
399   return A{};
400 }
401 
402 B func10() {
403   A a;
404   return a;
405 }
406 
407 B func11() {
408   B b;
409   return b;
410 }
411 
412 B func12() {
413   C c;
414   return c;
415 }
416 
417 )cpp");
418 
419   auto getFunctionNode = [&AST](const std::string &name) {
420     auto BN = ast_matchers::match(functionDecl(hasName(name)).bind("fn"),
421                                   AST->getASTContext());
422     EXPECT_EQ(BN.size(), 1u);
423     return BN[0].getNodeAs<Decl>("fn");
424   };
425 
426   {
427     auto FN = getFunctionNode("func1");
428     llvm::StringRef Expected = R"cpp(
429 FunctionDecl 'func1'
430 `-CompoundStmt
431   `-ReturnStmt
432     `-ExprWithCleanups
433       `-CXXConstructExpr
434         `-MaterializeTemporaryExpr
435           `-ImplicitCastExpr
436             `-CXXConstructExpr
437               `-IntegerLiteral
438 )cpp";
439 
440     EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, FN), Expected);
441 
442     Expected = R"cpp(
443 FunctionDecl 'func1'
444 `-CompoundStmt
445   `-ReturnStmt
446     `-IntegerLiteral
447 )cpp";
448     EXPECT_EQ(
449         dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, FN),
450         Expected);
451   }
452 
453   llvm::StringRef Expected = R"cpp(
454 FunctionDecl 'func2'
455 `-CompoundStmt
456   `-ReturnStmt
457     `-CXXTemporaryObjectExpr
458       `-IntegerLiteral
459 )cpp";
460   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
461                           getFunctionNode("func2")),
462             Expected);
463 
464   Expected = R"cpp(
465 FunctionDecl 'func3'
466 `-CompoundStmt
467   `-ReturnStmt
468     `-CXXFunctionalCastExpr
469       `-IntegerLiteral
470 )cpp";
471   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
472                           getFunctionNode("func3")),
473             Expected);
474 
475   Expected = R"cpp(
476 FunctionDecl 'func4'
477 `-CompoundStmt
478   `-ReturnStmt
479     `-CXXTemporaryObjectExpr
480 )cpp";
481   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
482                           getFunctionNode("func4")),
483             Expected);
484 
485   Expected = R"cpp(
486 FunctionDecl 'func5'
487 `-CompoundStmt
488   `-ReturnStmt
489     `-CXXTemporaryObjectExpr
490 )cpp";
491   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
492                           getFunctionNode("func5")),
493             Expected);
494 
495   Expected = R"cpp(
496 FunctionDecl 'func6'
497 `-CompoundStmt
498   `-ReturnStmt
499     `-CXXTemporaryObjectExpr
500 )cpp";
501   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
502                           getFunctionNode("func6")),
503             Expected);
504 
505   Expected = R"cpp(
506 FunctionDecl 'func7'
507 `-CompoundStmt
508   `-ReturnStmt
509     `-CXXTemporaryObjectExpr
510 )cpp";
511   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
512                           getFunctionNode("func7")),
513             Expected);
514 
515   Expected = R"cpp(
516 FunctionDecl 'func8'
517 `-CompoundStmt
518   `-ReturnStmt
519     `-CXXFunctionalCastExpr
520       `-InitListExpr
521 )cpp";
522   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
523                           getFunctionNode("func8")),
524             Expected);
525 
526   Expected = R"cpp(
527 FunctionDecl 'func9'
528 `-CompoundStmt
529   `-ReturnStmt
530     `-CXXFunctionalCastExpr
531       `-InitListExpr
532 )cpp";
533   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
534                           getFunctionNode("func9")),
535             Expected);
536 
537   Expected = R"cpp(
538 FunctionDecl 'func10'
539 `-CompoundStmt
540   |-DeclStmt
541   | `-VarDecl 'a'
542   |   `-CXXConstructExpr
543   `-ReturnStmt
544     `-DeclRefExpr 'a'
545 )cpp";
546   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
547                           getFunctionNode("func10")),
548             Expected);
549 
550   Expected = R"cpp(
551 FunctionDecl 'func11'
552 `-CompoundStmt
553   |-DeclStmt
554   | `-VarDecl 'b'
555   |   `-CXXConstructExpr
556   `-ReturnStmt
557     `-DeclRefExpr 'b'
558 )cpp";
559   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
560                           getFunctionNode("func11")),
561             Expected);
562 
563   Expected = R"cpp(
564 FunctionDecl 'func12'
565 `-CompoundStmt
566   |-DeclStmt
567   | `-VarDecl 'c'
568   |   `-CXXConstructExpr
569   `-ReturnStmt
570     `-DeclRefExpr 'c'
571 )cpp";
572   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
573                           getFunctionNode("func12")),
574             Expected);
575 }
576 
577 TEST(Traverse, LambdaUnlessSpelledInSource) {
578 
579   auto AST =
580       buildASTFromCodeWithArgs(R"cpp(
581 
582 void captures() {
583   int a = 0;
584   int b = 0;
585   int d = 0;
586   int f = 0;
587 
588   [a, &b, c = d, &e = f](int g, int h = 42) {};
589 }
590 
591 void templated() {
592   int a = 0;
593   [a]<typename T>(T t) {};
594 }
595 
596 struct SomeStruct {
597     int a = 0;
598     void capture_this() {
599         [this]() {};
600     }
601     void capture_this_copy() {
602         [self = *this]() {};
603     }
604 };
605 )cpp",
606                                {"-Wno-unused-value", "-Wno-c++2a-extensions"});
607 
608   auto getLambdaNode = [&AST](const std::string &name) {
609     auto BN = ast_matchers::match(
610         lambdaExpr(hasAncestor(functionDecl(hasName(name)))).bind("lambda"),
611         AST->getASTContext());
612     EXPECT_EQ(BN.size(), 1u);
613     return BN[0].getNodeAs<LambdaExpr>("lambda");
614   };
615 
616   {
617     auto L = getLambdaNode("captures");
618 
619     llvm::StringRef Expected = R"cpp(
620 LambdaExpr
621 |-DeclRefExpr 'a'
622 |-DeclRefExpr 'b'
623 |-VarDecl 'c'
624 | `-DeclRefExpr 'd'
625 |-VarDecl 'e'
626 | `-DeclRefExpr 'f'
627 |-ParmVarDecl 'g'
628 |-ParmVarDecl 'h'
629 | `-IntegerLiteral
630 `-CompoundStmt
631 )cpp";
632     EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
633               Expected);
634 
635     Expected = R"cpp(
636 LambdaExpr
637 |-CXXRecordDecl ''
638 | |-CXXMethodDecl 'operator()'
639 | | |-ParmVarDecl 'g'
640 | | |-ParmVarDecl 'h'
641 | | | `-IntegerLiteral
642 | | `-CompoundStmt
643 | |-FieldDecl ''
644 | |-FieldDecl ''
645 | |-FieldDecl ''
646 | |-FieldDecl ''
647 | `-CXXDestructorDecl '~'
648 |-ImplicitCastExpr
649 | `-DeclRefExpr 'a'
650 |-DeclRefExpr 'b'
651 |-ImplicitCastExpr
652 | `-DeclRefExpr 'd'
653 |-DeclRefExpr 'f'
654 `-CompoundStmt
655 )cpp";
656     EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, L), Expected);
657   }
658 
659   {
660     auto L = getLambdaNode("templated");
661 
662     llvm::StringRef Expected = R"cpp(
663 LambdaExpr
664 |-DeclRefExpr 'a'
665 |-TemplateTypeParmDecl 'T'
666 |-ParmVarDecl 't'
667 `-CompoundStmt
668 )cpp";
669     EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
670               Expected);
671   }
672 
673   {
674     auto L = getLambdaNode("capture_this");
675 
676     llvm::StringRef Expected = R"cpp(
677 LambdaExpr
678 |-CXXThisExpr
679 `-CompoundStmt
680 )cpp";
681     EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
682               Expected);
683   }
684 
685   {
686     auto L = getLambdaNode("capture_this_copy");
687 
688     llvm::StringRef Expected = R"cpp(
689 LambdaExpr
690 |-VarDecl 'self'
691 | `-UnaryOperator
692 |   `-CXXThisExpr
693 `-CompoundStmt
694 )cpp";
695     EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
696               Expected);
697   }
698 }
699 
700 } // namespace clang
701