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