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) { OS << D->getDeclKindName() << "Decl"; }
31 
32   void Visit(const Stmt *S) { OS << S->getStmtClassName(); }
33 
34   void Visit(QualType QT) {
35     OS << "QualType " << QT.split().Quals.getAsString();
36   }
37 
38   void Visit(const Type *T) { OS << T->getTypeClassName() << "Type"; }
39 
40   void Visit(const comments::Comment *C, const comments::FullComment *FC) {
41     OS << C->getCommentKindName();
42   }
43 
44   void Visit(const CXXCtorInitializer *Init) { OS << "CXXCtorInitializer"; }
45 
46   void Visit(const Attr *A) {
47     switch (A->getKind()) {
48 #define ATTR(X)                                                                \
49   case attr::X:                                                                \
50     OS << #X;                                                                  \
51     break;
52 #include "clang/Basic/AttrList.inc"
53     }
54     OS << "Attr";
55   }
56 
57   void Visit(const OMPClause *C) { OS << "OMPClause"; }
58   void Visit(const TemplateArgument &A, SourceRange R = {},
59              const Decl *From = nullptr, const char *Label = nullptr) {
60     OS << "TemplateArgument";
61   }
62 
63   template <typename... T> void Visit(T...) {}
64 };
65 
66 class TestASTDumper : public ASTNodeTraverser<TestASTDumper, NodeTreePrinter> {
67 
68   NodeTreePrinter MyNodeRecorder;
69 
70 public:
71   TestASTDumper(llvm::raw_ostream &OS) : MyNodeRecorder(OS) {}
72   NodeTreePrinter &doGetNodeDelegate() { return MyNodeRecorder; }
73 };
74 
75 template <typename... NodeType> std::string dumpASTString(NodeType &&... N) {
76   std::string Buffer;
77   llvm::raw_string_ostream OS(Buffer);
78 
79   TestASTDumper Dumper(OS);
80 
81   OS << "\n";
82 
83   Dumper.Visit(std::forward<NodeType &&>(N)...);
84 
85   return OS.str();
86 }
87 
88 const FunctionDecl *getFunctionNode(clang::ASTUnit *AST,
89                                     const std::string &Name) {
90   auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"),
91                                     AST->getASTContext());
92   EXPECT_EQ(Result.size(), 1u);
93   return Result[0].getNodeAs<FunctionDecl>("fn");
94 }
95 
96 template <typename T> struct Verifier {
97   static void withDynNode(T Node, const std::string &DumpString) {
98     EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(Node)),
99               DumpString);
100   }
101 };
102 
103 template <typename T> struct Verifier<T *> {
104   static void withDynNode(T *Node, const std::string &DumpString) {
105     EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(*Node)),
106               DumpString);
107   }
108 };
109 
110 template <typename T>
111 void verifyWithDynNode(T Node, const std::string &DumpString) {
112   EXPECT_EQ(dumpASTString(Node), DumpString);
113 
114   Verifier<T>::withDynNode(Node, DumpString);
115 }
116 
117 TEST(Traverse, Dump) {
118 
119   auto AST = buildASTFromCode(R"cpp(
120 struct A {
121   int m_number;
122 
123   /// CTor
124   A() : m_number(42) {}
125 
126   [[nodiscard]] const int func() {
127     return 42;
128   }
129 
130 };
131 
132 template<typename T>
133 struct templ
134 {
135 };
136 
137 template<>
138 struct templ<int>
139 {
140 };
141 
142 void parmvardecl_attr(struct A __attribute__((address_space(19)))*);
143 
144 )cpp");
145 
146   const FunctionDecl *Func = getFunctionNode(AST.get(), "func");
147 
148   verifyWithDynNode(Func,
149                     R"cpp(
150 CXXMethodDecl
151 |-CompoundStmt
152 | `-ReturnStmt
153 |   `-IntegerLiteral
154 `-WarnUnusedResultAttr
155 )cpp");
156 
157   Stmt *Body = Func->getBody();
158 
159   verifyWithDynNode(Body,
160                     R"cpp(
161 CompoundStmt
162 `-ReturnStmt
163   `-IntegerLiteral
164 )cpp");
165 
166   QualType QT = Func->getType();
167 
168   verifyWithDynNode(QT,
169                     R"cpp(
170 FunctionProtoType
171 `-QualType const
172   `-BuiltinType
173 )cpp");
174 
175   const FunctionDecl *CTorFunc = getFunctionNode(AST.get(), "A");
176 
177   verifyWithDynNode(CTorFunc->getType(),
178                     R"cpp(
179 FunctionProtoType
180 `-BuiltinType
181 )cpp");
182 
183   Attr *A = *Func->attr_begin();
184 
185   {
186     std::string expectedString = R"cpp(
187 WarnUnusedResultAttr
188 )cpp";
189 
190     EXPECT_EQ(dumpASTString(A), expectedString);
191   }
192 
193   auto *CTor = dyn_cast<CXXConstructorDecl>(CTorFunc);
194   const CXXCtorInitializer *Init = *CTor->init_begin();
195 
196   verifyWithDynNode(Init,
197                     R"cpp(
198 CXXCtorInitializer
199 `-IntegerLiteral
200 )cpp");
201 
202   const comments::FullComment *Comment =
203       AST->getASTContext().getLocalCommentForDeclUncached(CTorFunc);
204   {
205     std::string expectedString = R"cpp(
206 FullComment
207 `-ParagraphComment
208   `-TextComment
209 )cpp";
210     EXPECT_EQ(dumpASTString(Comment, Comment), expectedString);
211   }
212 
213   auto Result = ast_matchers::match(
214       classTemplateSpecializationDecl(hasName("templ")).bind("fn"),
215       AST->getASTContext());
216   EXPECT_EQ(Result.size(), 1u);
217   auto Templ = Result[0].getNodeAs<ClassTemplateSpecializationDecl>("fn");
218 
219   TemplateArgument TA = Templ->getTemplateArgs()[0];
220 
221   verifyWithDynNode(TA,
222                     R"cpp(
223 TemplateArgument
224 )cpp");
225 
226   Func = getFunctionNode(AST.get(), "parmvardecl_attr");
227 
228   const auto *Parm = Func->getParamDecl(0);
229   const auto TL = Parm->getTypeSourceInfo()->getTypeLoc();
230   ASSERT_TRUE(TL.getType()->isPointerType());
231 
232   const auto ATL = TL.getNextTypeLoc().getAs<AttributedTypeLoc>();
233   const auto *AS = cast<AddressSpaceAttr>(ATL.getAttr());
234   EXPECT_EQ(toTargetAddressSpace(static_cast<LangAS>(AS->getAddressSpace())),
235             19u);
236 }
237 } // namespace clang
238