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     OS << S->getStmtClassName();
39     if (auto *E = dyn_cast<DeclRefExpr>(S)) {
40       OS << " '" << E->getDecl()->getDeclName() << "'";
41     }
42   }
43 
Visit(QualType QT)44   void Visit(QualType QT) {
45     OS << "QualType " << QT.split().Quals.getAsString();
46   }
47 
Visit(const Type * T)48   void Visit(const Type *T) { OS << T->getTypeClassName() << "Type"; }
49 
Visit(const comments::Comment * C,const comments::FullComment * FC)50   void Visit(const comments::Comment *C, const comments::FullComment *FC) {
51     OS << C->getCommentKindName();
52   }
53 
Visit(const CXXCtorInitializer * Init)54   void Visit(const CXXCtorInitializer *Init) { OS << "CXXCtorInitializer"; }
55 
Visit(const Attr * A)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 
Visit(const OMPClause * C)67   void Visit(const OMPClause *C) { OS << "OMPClause"; }
Visit(const TemplateArgument & A,SourceRange R={},const Decl * From=nullptr,const char * Label=nullptr)68   void Visit(const TemplateArgument &A, SourceRange R = {},
69              const Decl *From = nullptr, const char *Label = nullptr) {
70     OS << "TemplateArgument";
71   }
72 
Visit(T...)73   template <typename... T> void Visit(T...) {}
74 };
75 
76 class TestASTDumper : public ASTNodeTraverser<TestASTDumper, NodeTreePrinter> {
77 
78   NodeTreePrinter MyNodeRecorder;
79 
80 public:
TestASTDumper(llvm::raw_ostream & OS)81   TestASTDumper(llvm::raw_ostream &OS) : MyNodeRecorder(OS) {}
doGetNodeDelegate()82   NodeTreePrinter &doGetNodeDelegate() { return MyNodeRecorder; }
83 };
84 
dumpASTString(NodeType &&...N)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>
dumpASTString(ast_type_traits::TraversalKind TK,NodeType &&...N)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 
getFunctionNode(clang::ASTUnit * AST,const std::string & Name)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 {
withDynNodeclang::Verifier122   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 *> {
withDynNodeclang::Verifier129   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>
verifyWithDynNode(T Node,const std::string & DumpString)136 void verifyWithDynNode(T Node, const std::string &DumpString) {
137   EXPECT_EQ(dumpASTString(Node), DumpString);
138 
139   Verifier<T>::withDynNode(Node, DumpString);
140 }
141 
TEST(Traverse,Dump)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 
TEST(Traverse,IgnoreUnlessSpelledInSource)263 TEST(Traverse, IgnoreUnlessSpelledInSource) {
264 
265   auto AST = buildASTFromCode(R"cpp(
266 
267 struct A
268 {
269 };
270 
271 struct B
272 {
273   B(int);
274   B(A const& a);
275   B();
276 };
277 
278 struct C
279 {
280   operator B();
281 };
282 
283 B func1() {
284   return 42;
285 }
286 
287 B func2() {
288   return B{42};
289 }
290 
291 B func3() {
292   return B(42);
293 }
294 
295 B func4() {
296   return B();
297 }
298 
299 B func5() {
300   return B{};
301 }
302 
303 B func6() {
304   return C();
305 }
306 
307 B func7() {
308   return A();
309 }
310 
311 B func8() {
312   return C{};
313 }
314 
315 B func9() {
316   return A{};
317 }
318 
319 B func10() {
320   A a;
321   return a;
322 }
323 
324 B func11() {
325   B b;
326   return b;
327 }
328 
329 B func12() {
330   C c;
331   return c;
332 }
333 
334 )cpp");
335 
336   auto getFunctionNode = [&AST](const std::string &name) {
337     auto BN = ast_matchers::match(functionDecl(hasName(name)).bind("fn"),
338                                   AST->getASTContext());
339     EXPECT_EQ(BN.size(), 1u);
340     return BN[0].getNodeAs<Decl>("fn");
341   };
342 
343   {
344     auto FN = getFunctionNode("func1");
345     llvm::StringRef Expected = R"cpp(
346 FunctionDecl 'func1'
347 `-CompoundStmt
348   `-ReturnStmt
349     `-ExprWithCleanups
350       `-CXXConstructExpr
351         `-MaterializeTemporaryExpr
352           `-ImplicitCastExpr
353             `-CXXConstructExpr
354               `-IntegerLiteral
355 )cpp";
356 
357     EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, FN), Expected);
358 
359     Expected = R"cpp(
360 FunctionDecl 'func1'
361 `-CompoundStmt
362   `-ReturnStmt
363     `-IntegerLiteral
364 )cpp";
365     EXPECT_EQ(
366         dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, FN),
367         Expected);
368   }
369 
370   llvm::StringRef Expected = R"cpp(
371 FunctionDecl 'func2'
372 `-CompoundStmt
373   `-ReturnStmt
374     `-CXXTemporaryObjectExpr
375       `-IntegerLiteral
376 )cpp";
377   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
378                           getFunctionNode("func2")),
379             Expected);
380 
381   Expected = R"cpp(
382 FunctionDecl 'func3'
383 `-CompoundStmt
384   `-ReturnStmt
385     `-CXXFunctionalCastExpr
386       `-IntegerLiteral
387 )cpp";
388   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
389                           getFunctionNode("func3")),
390             Expected);
391 
392   Expected = R"cpp(
393 FunctionDecl 'func4'
394 `-CompoundStmt
395   `-ReturnStmt
396     `-CXXTemporaryObjectExpr
397 )cpp";
398   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
399                           getFunctionNode("func4")),
400             Expected);
401 
402   Expected = R"cpp(
403 FunctionDecl 'func5'
404 `-CompoundStmt
405   `-ReturnStmt
406     `-CXXTemporaryObjectExpr
407 )cpp";
408   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
409                           getFunctionNode("func5")),
410             Expected);
411 
412   Expected = R"cpp(
413 FunctionDecl 'func6'
414 `-CompoundStmt
415   `-ReturnStmt
416     `-CXXTemporaryObjectExpr
417 )cpp";
418   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
419                           getFunctionNode("func6")),
420             Expected);
421 
422   Expected = R"cpp(
423 FunctionDecl 'func7'
424 `-CompoundStmt
425   `-ReturnStmt
426     `-CXXTemporaryObjectExpr
427 )cpp";
428   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
429                           getFunctionNode("func7")),
430             Expected);
431 
432   Expected = R"cpp(
433 FunctionDecl 'func8'
434 `-CompoundStmt
435   `-ReturnStmt
436     `-CXXFunctionalCastExpr
437       `-InitListExpr
438 )cpp";
439   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
440                           getFunctionNode("func8")),
441             Expected);
442 
443   Expected = R"cpp(
444 FunctionDecl 'func9'
445 `-CompoundStmt
446   `-ReturnStmt
447     `-CXXFunctionalCastExpr
448       `-InitListExpr
449 )cpp";
450   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
451                           getFunctionNode("func9")),
452             Expected);
453 
454   Expected = R"cpp(
455 FunctionDecl 'func10'
456 `-CompoundStmt
457   |-DeclStmt
458   | `-VarDecl 'a'
459   |   `-CXXConstructExpr
460   `-ReturnStmt
461     `-DeclRefExpr 'a'
462 )cpp";
463   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
464                           getFunctionNode("func10")),
465             Expected);
466 
467   Expected = R"cpp(
468 FunctionDecl 'func11'
469 `-CompoundStmt
470   |-DeclStmt
471   | `-VarDecl 'b'
472   |   `-CXXConstructExpr
473   `-ReturnStmt
474     `-DeclRefExpr 'b'
475 )cpp";
476   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
477                           getFunctionNode("func11")),
478             Expected);
479 
480   Expected = R"cpp(
481 FunctionDecl 'func12'
482 `-CompoundStmt
483   |-DeclStmt
484   | `-VarDecl 'c'
485   |   `-CXXConstructExpr
486   `-ReturnStmt
487     `-DeclRefExpr 'c'
488 )cpp";
489   EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
490                           getFunctionNode("func12")),
491             Expected);
492 }
493 
TEST(Traverse,LambdaUnlessSpelledInSource)494 TEST(Traverse, LambdaUnlessSpelledInSource) {
495 
496   auto AST =
497       buildASTFromCodeWithArgs(R"cpp(
498 
499 void captures() {
500   int a = 0;
501   int b = 0;
502   int d = 0;
503   int f = 0;
504 
505   [a, &b, c = d, &e = f](int g, int h = 42) {};
506 }
507 
508 void templated() {
509   int a = 0;
510   [a]<typename T>(T t) {};
511 }
512 
513 struct SomeStruct {
514     int a = 0;
515     void capture_this() {
516         [this]() {};
517     }
518     void capture_this_copy() {
519         [self = *this]() {};
520     }
521 };
522 )cpp",
523                                {"-Wno-unused-value", "-Wno-c++2a-extensions"});
524 
525   auto getLambdaNode = [&AST](const std::string &name) {
526     auto BN = ast_matchers::match(
527         lambdaExpr(hasAncestor(functionDecl(hasName(name)))).bind("lambda"),
528         AST->getASTContext());
529     EXPECT_EQ(BN.size(), 1u);
530     return BN[0].getNodeAs<LambdaExpr>("lambda");
531   };
532 
533   {
534     auto L = getLambdaNode("captures");
535 
536     llvm::StringRef Expected = R"cpp(
537 LambdaExpr
538 |-DeclRefExpr 'a'
539 |-DeclRefExpr 'b'
540 |-VarDecl 'c'
541 | `-DeclRefExpr 'd'
542 |-VarDecl 'e'
543 | `-DeclRefExpr 'f'
544 |-ParmVarDecl 'g'
545 |-ParmVarDecl 'h'
546 | `-IntegerLiteral
547 `-CompoundStmt
548 )cpp";
549     EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
550               Expected);
551 
552     Expected = R"cpp(
553 LambdaExpr
554 |-CXXRecordDecl ''
555 | |-CXXMethodDecl 'operator()'
556 | | |-ParmVarDecl 'g'
557 | | |-ParmVarDecl 'h'
558 | | | `-IntegerLiteral
559 | | `-CompoundStmt
560 | |-FieldDecl ''
561 | |-FieldDecl ''
562 | |-FieldDecl ''
563 | |-FieldDecl ''
564 | `-CXXDestructorDecl '~'
565 |-ImplicitCastExpr
566 | `-DeclRefExpr 'a'
567 |-DeclRefExpr 'b'
568 |-ImplicitCastExpr
569 | `-DeclRefExpr 'd'
570 |-DeclRefExpr 'f'
571 `-CompoundStmt
572 )cpp";
573     EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, L), Expected);
574   }
575 
576   {
577     auto L = getLambdaNode("templated");
578 
579     llvm::StringRef Expected = R"cpp(
580 LambdaExpr
581 |-DeclRefExpr 'a'
582 |-TemplateTypeParmDecl 'T'
583 |-ParmVarDecl 't'
584 `-CompoundStmt
585 )cpp";
586     EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
587               Expected);
588   }
589 
590   {
591     auto L = getLambdaNode("capture_this");
592 
593     llvm::StringRef Expected = R"cpp(
594 LambdaExpr
595 |-CXXThisExpr
596 `-CompoundStmt
597 )cpp";
598     EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
599               Expected);
600   }
601 
602   {
603     auto L = getLambdaNode("capture_this_copy");
604 
605     llvm::StringRef Expected = R"cpp(
606 LambdaExpr
607 |-VarDecl 'self'
608 | `-UnaryOperator
609 |   `-CXXThisExpr
610 `-CompoundStmt
611 )cpp";
612     EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, L),
613               Expected);
614   }
615 }
616 
617 } // namespace clang
618