Index: cfe/trunk/include/clang/AST/ASTNodeTraverser.h =================================================================== --- cfe/trunk/include/clang/AST/ASTNodeTraverser.h +++ cfe/trunk/include/clang/AST/ASTNodeTraverser.h @@ -205,6 +205,24 @@ }); } + void Visit(const ast_type_traits::DynTypedNode &N) { + // FIXME: Improve this with a switch or a visitor pattern. + if (const auto *D = N.get()) + Visit(D); + else if (const auto *S = N.get()) + Visit(S); + else if (const auto *QT = N.get()) + Visit(*QT); + else if (const auto *T = N.get()) + Visit(T); + else if (const auto *C = N.get()) + Visit(C); + else if (const auto *C = N.get()) + Visit(C); + else if (const auto *T = N.get()) + Visit(*T); + } + void dumpDeclContext(const DeclContext *DC) { if (!DC) return; Index: cfe/trunk/unittests/AST/ASTTraverserTest.cpp =================================================================== --- cfe/trunk/unittests/AST/ASTTraverserTest.cpp +++ cfe/trunk/unittests/AST/ASTTraverserTest.cpp @@ -0,0 +1,220 @@ +//===- unittests/AST/ASTTraverserTest.h------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTNodeTraverser.h" +#include "clang/AST/TextNodeDumper.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Tooling.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang::tooling; +using namespace clang::ast_matchers; + +namespace clang { + +class NodeTreePrinter : public TextTreeStructure { + llvm::raw_ostream &OS; + +public: + NodeTreePrinter(llvm::raw_ostream &OS) + : TextTreeStructure(OS, /* showColors */ false), OS(OS) {} + + void Visit(const Decl *D) { OS << D->getDeclKindName() << "Decl"; } + + void Visit(const Stmt *S) { OS << S->getStmtClassName(); } + + void Visit(QualType QT) { + OS << "QualType " << QT.split().Quals.getAsString(); + } + + void Visit(const Type *T) { OS << T->getTypeClassName() << "Type"; } + + void Visit(const comments::Comment *C, const comments::FullComment *FC) { + OS << C->getCommentKindName(); + } + + void Visit(const CXXCtorInitializer *Init) { OS << "CXXCtorInitializer"; } + + void Visit(const Attr *A) { + switch (A->getKind()) { +#define ATTR(X) \ + case attr::X: \ + OS << #X; \ + break; +#include "clang/Basic/AttrList.inc" + } + OS << "Attr"; + } + + void Visit(const OMPClause *C) { OS << "OMPClause"; } + void Visit(const TemplateArgument &A, SourceRange R = {}, + const Decl *From = nullptr, const char *Label = nullptr) { + OS << "TemplateArgument"; + } + + template void Visit(T...) {} +}; + +class TestASTDumper : public ASTNodeTraverser { + + NodeTreePrinter MyNodeRecorder; + +public: + TestASTDumper(llvm::raw_ostream &OS) : MyNodeRecorder(OS) {} + NodeTreePrinter &doGetNodeDelegate() { return MyNodeRecorder; } +}; + +template std::string dumpASTString(NodeType &&... N) { + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + + TestASTDumper Dumper(OS); + + OS << "\n"; + + Dumper.Visit(std::forward(N)...); + + return OS.str(); +} + +const FunctionDecl *getFunctionNode(clang::ASTUnit *AST, + const std::string &Name) { + auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"), + AST->getASTContext()); + EXPECT_EQ(Result.size(), 1u); + return Result[0].getNodeAs("fn"); +} + +template struct Verifier { + static void withDynNode(T Node, const std::string &DumpString) { + EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(Node)), + DumpString); + } +}; + +template struct Verifier { + static void withDynNode(T *Node, const std::string &DumpString) { + EXPECT_EQ(dumpASTString(ast_type_traits::DynTypedNode::create(*Node)), + DumpString); + } +}; + +template +void verifyWithDynNode(T Node, const std::string &DumpString) { + EXPECT_EQ(dumpASTString(Node), DumpString); + + Verifier::withDynNode(Node, DumpString); +} + +TEST(Traverse, Dump) { + + auto AST = buildASTFromCode(R"cpp( +struct A { + int m_number; + + /// CTor + A() : m_number(42) {} + + [[nodiscard]] const int func() { + return 42; + } + +}; + +template +struct templ +{ +}; + +template<> +struct templ +{ +}; + +)cpp"); + + const FunctionDecl *Func = getFunctionNode(AST.get(), "func"); + + verifyWithDynNode(Func, + R"cpp( +CXXMethodDecl +|-CompoundStmt +| `-ReturnStmt +| `-IntegerLiteral +`-WarnUnusedResultAttr +)cpp"); + + Stmt *Body = Func->getBody(); + + verifyWithDynNode(Body, + R"cpp( +CompoundStmt +`-ReturnStmt + `-IntegerLiteral +)cpp"); + + QualType QT = Func->getType(); + + verifyWithDynNode(QT, + R"cpp( +FunctionProtoType +`-QualType const + `-BuiltinType +)cpp"); + + const FunctionDecl *CTorFunc = getFunctionNode(AST.get(), "A"); + + verifyWithDynNode(CTorFunc->getType(), + R"cpp( +FunctionProtoType +`-BuiltinType +)cpp"); + + Attr *A = *Func->attr_begin(); + + EXPECT_EQ(dumpASTString(A), + R"cpp( +WarnUnusedResultAttr +)cpp"); + + auto *CTor = dyn_cast(CTorFunc); + const CXXCtorInitializer *Init = *CTor->init_begin(); + + verifyWithDynNode(Init, + R"cpp( +CXXCtorInitializer +`-IntegerLiteral +)cpp"); + + const comments::FullComment *Comment = + AST->getASTContext().getLocalCommentForDeclUncached(CTorFunc); + + EXPECT_EQ(dumpASTString(Comment, Comment), + R"cpp( +FullComment +`-ParagraphComment + `-TextComment +)cpp"); + + auto Result = ast_matchers::match( + classTemplateSpecializationDecl(hasName("templ")).bind("fn"), + AST->getASTContext()); + EXPECT_EQ(Result.size(), 1u); + auto Templ = Result[0].getNodeAs("fn"); + + TemplateArgument TA = Templ->getTemplateArgs()[0]; + + verifyWithDynNode(TA, + R"cpp( +TemplateArgument +)cpp"); +} +} // namespace clang Index: cfe/trunk/unittests/AST/CMakeLists.txt =================================================================== --- cfe/trunk/unittests/AST/CMakeLists.txt +++ cfe/trunk/unittests/AST/CMakeLists.txt @@ -12,6 +12,7 @@ ASTImporterTest.cpp ASTImporterGenericRedeclTest.cpp ASTImporterVisibilityTest.cpp + ASTTraverserTest.cpp ASTTypeTraitsTest.cpp ASTVectorTest.cpp CommentLexer.cpp