Index: test/Tooling/clang-doc-basic.cpp =================================================================== --- test/Tooling/clang-doc-basic.cpp +++ test/Tooling/clang-doc-basic.cpp @@ -1,8 +1,8 @@ // RUN: rm -rf %t // RUN: mkdir %t -// RUN: echo '[{"directory":"%t","command":"clang++ -c %t/test.cpp","file":"%t/test.cpp"}]' | sed -e 's/\\/\//g' > %t/compile_commands.json +// RUN: echo "" > %t/compile_flags.txt // RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -p %t %t/test.cpp | FileCheck %s +// RUN: clang-doc --emit-yaml --dump --omit-filenames -p %t %t/test.cpp | FileCheck %s /// Test namespace namespace A { @@ -18,55 +18,64 @@ int i; }; -// CHECK-DAG: --- -// CHECK-DAG: Namespaces: -// CHECK-DAG: - Qualified Name: '' -// CHECK-DAG: Name: '' -// CHECK-DAG: Namespace: '' -// CHECK-DAG: - Qualified Name: A -// CHECK-DAG: Name: A -// CHECK-DAG: Namespace: '' -// CHECK-DAG: Descriptions: -// CHECK-DAG: - Kind: FullComment -// CHECK-DAG: Children: -// CHECK-DAG: - Kind: ParagraphComment -// CHECK-DAG: Children: -// CHECK-DAG: - Kind: TextComment -// CHECK-DAG: Text: ' Test namespace' -// CHECK-DAG: Locations: -// CHECK-DAG: - LineNumber: 8 -// CHECK-DAG: Functions: -// CHECK-DAG: - Mangled Name: _ZN1A1fEi -// CHECK-DAG: Qualified Name: 'A::f' -// CHECK-DAG: Name: f -// CHECK-DAG: Namespace: A -// CHECK-DAG: Descriptions: -// CHECK-DAG: - Kind: FullComment -// CHECK-DAG: Children: -// CHECK-DAG: - Kind: ParagraphComment -// CHECK-DAG: Children: -// CHECK-DAG: - Kind: TextComment -// CHECK-DAG: Text: ' A function' -// CHECK-DAG: Locations: -// CHECK-DAG: - LineNumber: 11 -// CHECK-DAG: ReturnType: void -// CHECK-DAG: Access: None -// CHECK-DAG: Types: -// CHECK-DAG: - Qualified Name: C -// CHECK-DAG: Name: C -// CHECK-DAG: Namespace: '' -// CHECK-DAG: Descriptions: -// CHECK-DAG: - Kind: FullComment -// CHECK-DAG: Children: -// CHECK-DAG: - Kind: ParagraphComment -// CHECK-DAG: Children: -// CHECK-DAG: - Kind: TextComment -// CHECK-DAG: Text: ' A C++ class' -// CHECK-DAG: TagType: Class -// CHECK-DAG: Locations: -// CHECK-DAG: - LineNumber: 16 -// CHECK-DAG: Members: -// CHECK-DAG: - Type: int -// CHECK-DAG: Name: 'C::i' -// CHECK-DAG: Access: None -// CHECK-DAG: ... + // CHECK: --- + // CHECK: Qualified Name: A + // CHECK: Name: A + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment + // CHECK: Children: + // CHECK: - Kind: TextComment + // CHECK: Text: ' Test namespace' + // CHECK: Locations: + // CHECK: - LineNumber: 8 + // CHECK: Filename: '' + // CHECK: ... + // CHECK: --- + // CHECK: Mangled Name: _ZN1A1fEi + // CHECK: Qualified Name: 'A::f' + // CHECK: Name: f + // CHECK: Namespace: A + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment + // CHECK: Children: + // CHECK: - Kind: TextComment + // CHECK: Text: ' A function' + // CHECK: Locations: + // CHECK: - LineNumber: 11 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Params: + // CHECK: - Type: int + // CHECK: Name: x + // CHECK: Access: None + // CHECK: ReturnType: void + // CHECK: Access: None + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: C + // CHECK: Name: C + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment + // CHECK: Children: + // CHECK: - Kind: TextComment + // CHECK: Text: ' A C++ class' + // CHECK: TagType: Class + // CHECK: Locations: + // CHECK: - LineNumber: 16 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Members: + // CHECK: - Type: int + // CHECK: Name: 'C::i' + // CHECK: Access: None + // CHECK: Parents: + // CHECK: VirtualParents: + // CHECK: ... Index: test/Tooling/clang-doc-namespace.cpp =================================================================== --- test/Tooling/clang-doc-namespace.cpp +++ test/Tooling/clang-doc-namespace.cpp @@ -1,8 +1,8 @@ // RUN: rm -rf %t // RUN: mkdir %t -// RUN: echo '[{"directory":"%t","command":"clang++ -c %t/test.cpp","file":"%t/test.cpp"}]' | sed -e 's/\\/\//g' > %t/compile_commands.json +// RUN: echo "" > %t/compile_flags.txt // RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -p %t %t/test.cpp | FileCheck %s +// RUN: clang-doc --emit-yaml --dump --omit-filenames -p %t %t/test.cpp | FileCheck %s /// Test namespace namespace A {} @@ -17,52 +17,61 @@ void f() {}; } - // CHECK: Namespaces: - // CHECK: - Qualified Name: '' - // CHECK: Name: '' - // CHECK: Namespace: '' - // CHECK: - Qualified Name: A - // CHECK: Name: A - // CHECK: Namespace: '' - // CHECK: Descriptions: - // CHECK: - Kind: FullComment + // CHECK: --- + // CHECK: Qualified Name: A + // CHECK: Name: A + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment // CHECK: Children: - // CHECK: - Kind: ParagraphComment - // CHECK: Children: - // CHECK: - Kind: TextComment - // CHECK: Text: ' Test namespace' - // CHECK: Locations: - // CHECK: - LineNumber: 8 - // CHECK: Filename: '' - // CHECK: - LineNumber: 16 - // CHECK: Filename: '' - // CHECK: Functions: - // CHECK: - Mangled Name: _ZN1A1fEv - // CHECK: Qualified Name: 'A::f' - // CHECK: Name: f - // CHECK: Namespace: A - // CHECK: Locations: - // CHECK: - LineNumber: 17 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: ReturnType: void - // CHECK: Access: None - // CHECK: - Qualified Name: B - // CHECK: Name: B - // CHECK: Namespace: '' - // CHECK: Locations: - // CHECK: - LineNumber: 10 - // CHECK: Filename: '' - // CHECK: - Qualified Name: 'B::C::D' - // CHECK: Name: D - // CHECK: Namespace: 'B::C' - // CHECK: Locations: - // CHECK: - LineNumber: 12 - // CHECK: Filename: '' - // CHECK: - Qualified Name: 'B::C' - // CHECK: Name: C - // CHECK: Namespace: B - // CHECK: Locations: - // CHECK: - LineNumber: 11 - // CHECK: Filename: '' + // CHECK: - Kind: TextComment + // CHECK: Text: ' Test namespace' + // CHECK: Locations: + // CHECK: - LineNumber: 8 + // CHECK: Filename: '' + // CHECK: - LineNumber: 16 + // CHECK: Filename: '' + // CHECK: ... + // CHECK: --- + // CHECK: Mangled Name: _ZN1A1fEv + // CHECK: Qualified Name: 'A::f' + // CHECK: Name: f + // CHECK: Namespace: A + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: - LineNumber: 17 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Params: + // CHECK: ReturnType: void + // CHECK: Access: None + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: B + // CHECK: Name: B + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: - LineNumber: 10 + // CHECK: Filename: '' + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: 'B::C::D' + // CHECK: Name: D + // CHECK: Namespace: 'B::C' + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: - LineNumber: 12 + // CHECK: Filename: '' + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: 'B::C' + // CHECK: Name: C + // CHECK: Namespace: B + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: - LineNumber: 11 + // CHECK: Filename: '' // CHECK: ... Index: test/Tooling/clang-doc-type.cpp =================================================================== --- test/Tooling/clang-doc-type.cpp +++ test/Tooling/clang-doc-type.cpp @@ -1,8 +1,8 @@ // RUN: rm -rf %t // RUN: mkdir %t -// RUN: echo '[{"directory":"%t","command":"clang++ -c %t/test.cpp","file":"%t/test.cpp"}]' | sed -e 's/\\/\//g' > %t/compile_commands.json +// RUN: echo "" > %t/compile_flags.txt // RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -p %t %t/test.cpp | FileCheck %s +// RUN: clang-doc --emit-yaml --dump --omit-filenames -p %t %t/test.cpp | FileCheck %s /// A union. union A { int X; int Y; }; @@ -21,14 +21,8 @@ public: E() {} ~E() {} - int PublicVal; - protected: - int ProtectedVal; void ProtectedMethod(); - -private: - int PrivateVal(); }; void E::ProtectedMethod() {} @@ -38,171 +32,197 @@ class G : virtual private D, public E {}; // CHECK: --- - // CHECK: Namespaces: - // CHECK: - Qualified Name: '' - // CHECK: Name: '' - // CHECK: Namespace: '' - // CHECK: Types: - // CHECK: - Qualified Name: A - // CHECK: Name: A - // CHECK: Namespace: '' - // CHECK: Descriptions: - // CHECK: - Kind: FullComment + // CHECK: Qualified Name: A + // CHECK: Name: A + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment // CHECK: Children: - // CHECK: - Kind: ParagraphComment - // CHECK: Children: - // CHECK: - Kind: TextComment - // CHECK: Text: ' A union.' - // CHECK: TagType: Union - // CHECK: Locations: - // CHECK: - LineNumber: 8 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: Members: - // CHECK: - Type: int - // CHECK: Name: 'A::X' - // CHECK: Access: None - // CHECK: - Type: int - // CHECK: Name: 'A::Y' - // CHECK: Access: None - // CHECK: - Qualified Name: C - // CHECK: Name: C - // CHECK: Namespace: '' - // CHECK: Descriptions: - // CHECK: - Kind: FullComment + // CHECK: - Kind: TextComment + // CHECK: Text: ' A union.' + // CHECK: TagType: Union + // CHECK: Locations: + // CHECK: - LineNumber: 8 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Members: + // CHECK: - Type: int + // CHECK: Name: 'A::X' + // CHECK: Access: None + // CHECK: - Type: int + // CHECK: Name: 'A::Y' + // CHECK: Access: None + // CHECK: Parents: + // CHECK: VirtualParents: + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: C + // CHECK: Name: C + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment // CHECK: Children: - // CHECK: - Kind: ParagraphComment - // CHECK: Children: - // CHECK: - Kind: TextComment - // CHECK: Text: ' A struct.' - // CHECK: TagType: Struct - // CHECK: Locations: - // CHECK: - LineNumber: 14 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: Members: - // CHECK: - Type: int - // CHECK: Name: 'C::i' - // CHECK: Access: None - // CHECK: - Qualified Name: D - // CHECK: Name: D - // CHECK: Namespace: '' - // CHECK: Descriptions: - // CHECK: - Kind: FullComment + // CHECK: - Kind: TextComment + // CHECK: Text: ' A struct.' + // CHECK: TagType: Struct + // CHECK: Locations: + // CHECK: - LineNumber: 14 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Members: + // CHECK: - Type: int + // CHECK: Name: 'C::i' + // CHECK: Access: None + // CHECK: Parents: + // CHECK: VirtualParents: + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: D + // CHECK: Name: D + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment // CHECK: Children: - // CHECK: - Kind: ParagraphComment - // CHECK: Children: - // CHECK: - Kind: TextComment - // CHECK: Text: ' An empty class.' - // CHECK: TagType: Class - // CHECK: Locations: - // CHECK: - LineNumber: 17 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: - Qualified Name: E - // CHECK: Name: E - // CHECK: Namespace: '' - // CHECK: Descriptions: - // CHECK: - Kind: FullComment + // CHECK: - Kind: TextComment + // CHECK: Text: ' An empty class.' + // CHECK: TagType: Class + // CHECK: Locations: + // CHECK: - LineNumber: 17 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Members: + // CHECK: Parents: + // CHECK: VirtualParents: + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: E + // CHECK: Name: E + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment // CHECK: Children: - // CHECK: - Kind: ParagraphComment - // CHECK: Children: - // CHECK: - Kind: TextComment - // CHECK: Text: ' An example class.' - // CHECK: TagType: Class - // CHECK: Locations: - // CHECK: - LineNumber: 20 - // CHECK: Filename: '' - // CHECK: Functions: - // CHECK: - Mangled Name: _ZN1E15ProtectedMethodEv - // CHECK: Qualified Name: 'E::ProtectedMethod' - // CHECK: Name: ProtectedMethod - // CHECK: Namespace: E - // CHECK: Locations: - // CHECK: - LineNumber: 34 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: ReturnType: void - // CHECK: Access: Protected - // CHECK: - Mangled Name: _ZN1EC1Ev - // CHECK: Qualified Name: 'E::E' - // CHECK: Name: E - // CHECK: Namespace: E - // CHECK: Locations: - // CHECK: - LineNumber: 22 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: ReturnType: void - // CHECK: Access: Public - // CHECK: - Mangled Name: _ZN1ED1Ev - // CHECK: Qualified Name: 'E::~E' - // CHECK: Name: '~E' - // CHECK: Namespace: E - // CHECK: Locations: - // CHECK: - LineNumber: 23 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: ReturnType: void - // CHECK: Access: Public - // CHECK: DefinitionFile: '' - // CHECK: Members: - // CHECK: - Type: int - // CHECK: Name: 'E::PublicVal' - // CHECK: Access: None - // CHECK: - Type: int - // CHECK: Name: 'E::ProtectedVal' - // CHECK: Access: None - // CHECK: - Qualified Name: F - // CHECK: Name: F - // CHECK: Namespace: '' - // CHECK: Descriptions: - // CHECK: - Kind: FullComment + // CHECK: - Kind: TextComment + // CHECK: Text: ' An example class.' + // CHECK: TagType: Class + // CHECK: Locations: + // CHECK: - LineNumber: 20 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Members: + // CHECK: Parents: + // CHECK: VirtualParents: + // CHECK: ... + // CHECK: --- + // CHECK: Mangled Name: _ZN1E15ProtectedMethodEv + // CHECK: Qualified Name: 'E::ProtectedMethod' + // CHECK: Name: ProtectedMethod + // CHECK: Namespace: E + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: - LineNumber: 25 + // CHECK: Filename: '' + // CHECK: - LineNumber: 28 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Params: + // CHECK: ReturnType: void + // CHECK: Access: Protected + // CHECK: ... + // CHECK: --- + // CHECK: Mangled Name: _ZN1EC1Ev + // CHECK: Qualified Name: 'E::E' + // CHECK: Name: E + // CHECK: Namespace: E + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: - LineNumber: 22 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Params: + // CHECK: ReturnType: void + // CHECK: Access: Public + // CHECK: ... + // CHECK: --- + // CHECK: Mangled Name: _ZN1ED1Ev + // CHECK: Qualified Name: 'E::~E' + // CHECK: Name: '~E' + // CHECK: Namespace: E + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: - LineNumber: 23 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Params: + // CHECK: ReturnType: void + // CHECK: Access: Public + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: F + // CHECK: Name: F + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment // CHECK: Children: - // CHECK: - Kind: ParagraphComment - // CHECK: Children: - // CHECK: - Kind: TextComment - // CHECK: Text: ' An inherited class.' - // CHECK: TagType: Class - // CHECK: Locations: - // CHECK: - LineNumber: 37 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: Parents: - // CHECK: - class D - // CHECK: - class E - // CHECK: - Qualified Name: G - // CHECK: Name: G - // CHECK: Namespace: '' - // CHECK: TagType: Class - // CHECK: Locations: - // CHECK: - LineNumber: 38 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: Parents: - // CHECK: - class E - // CHECK: VirtualParents: - // CHECK: - class D - // CHECK: Enums: - // CHECK: - Qualified Name: B - // CHECK: Name: B - // CHECK: Namespace: '' - // CHECK: Descriptions: - // CHECK: - Kind: FullComment + // CHECK: - Kind: TextComment + // CHECK: Text: ' An inherited class.' + // CHECK: TagType: Class + // CHECK: Locations: + // CHECK: - LineNumber: 31 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Members: + // CHECK: Parents: + // CHECK: - class D + // CHECK: - class E + // CHECK: VirtualParents: + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: G + // CHECK: Name: G + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: TagType: Class + // CHECK: Locations: + // CHECK: - LineNumber: 32 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Members: + // CHECK: Parents: + // CHECK: - class E + // CHECK: VirtualParents: + // CHECK: - class D + // CHECK: ... + // CHECK: --- + // CHECK: Qualified Name: B + // CHECK: Name: B + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: - Kind: FullComment + // CHECK: Children: + // CHECK: - Kind: ParagraphComment // CHECK: Children: - // CHECK: - Kind: ParagraphComment - // CHECK: Children: - // CHECK: - Kind: TextComment - // CHECK: Text: ' An enum.' - // CHECK: Locations: - // CHECK: - LineNumber: 11 - // CHECK: Filename: '' - // CHECK: DefinitionFile: '' - // CHECK: Scoped: false - // CHECK: Members: - // CHECK: - Type: X - // CHECK: Name: '' - // CHECK: Access: None - // CHECK: - Type: Y - // CHECK: Name: '' - // CHECK: Access: None + // CHECK: - Kind: TextComment + // CHECK: Text: ' An enum.' + // CHECK: Locations: + // CHECK: - LineNumber: 11 + // CHECK: Filename: '' + // CHECK: DefinitionFile: '' + // CHECK: Scoped: false + // CHECK: Members: + // CHECK: - Type: X + // CHECK: Name: '' + // CHECK: Access: None + // CHECK: - Type: Y + // CHECK: Name: '' + // CHECK: Access: None // CHECK: ... Index: tools/clang-doc/ClangDoc.h =================================================================== --- tools/clang-doc/ClangDoc.h +++ tools/clang-doc/ClangDoc.h @@ -14,81 +14,81 @@ #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Comment.h" +#include "clang/AST/Mangle.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" #include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Tooling/Tooling.h" #include #include +using namespace clang::ast_matchers; + namespace clang { namespace doc { -// A Context which contains extra options which are used in ClangMoveTool. -struct ClangDocContext { - // Which format in which to emit representation. - OutFormat EmitFormat; -}; - -class ClangDocVisitor : public RecursiveASTVisitor { +/// Callback function for matcher. +/// Parses each match and sends it along to the reporter for serialization. +class ClangDocCallback : public MatchFinder::MatchCallback { public: - explicit ClangDocVisitor(ASTContext *Ctx, ClangDocReporter &Reporter) - : Context(Ctx), Manager(Ctx->getSourceManager()), - MC(Ctx->createMangleContext()), Reporter(Reporter) {} + ClangDocCallback(std::unique_ptr &Reporter, + StringRef BoundName) + : Reporter(Reporter), BoundName(BoundName) {} - bool VisitTagDecl(const TagDecl *D); - bool VisitNamespaceDecl(const NamespaceDecl *D); - bool VisitFunctionDecl(const FunctionDecl *D); + virtual void run(const MatchFinder::MatchResult &Result) override; void parseUnattachedComments(); private: + template + void processMatchedDecl(const T *D, const MatchFinder::MatchResult &Result); + bool isUnparsed(SourceLocation Loc) const; int getLine(const Decl *D) const; - std::string getFile(const Decl *D) const; - comments::FullComment* getComment(const Decl *D); - std::string mangleName(const FunctionDecl *D); + StringRef getFile(const Decl *D) const; + comments::FullComment *getComment(const Decl *D); + std::string mangleName(const FunctionDecl *D) const; ASTContext *Context; - SourceManager &Manager; - MangleContext *MC; - ClangDocReporter &Reporter; + mutable MangleContext *MC; + std::unique_ptr &Reporter; + StringRef BoundName; }; +/// AST consumer for the clang-doc action. +/// Runs the given matchers on the TU, triggering the callback for each match. class ClangDocConsumer : public clang::ASTConsumer { public: - explicit ClangDocConsumer(ASTContext *Ctx, ClangDocReporter &Reporter) - : Visitor(Ctx, Reporter), Reporter(Reporter) {} - - virtual void HandleTranslationUnit(clang::ASTContext &Context); + explicit ClangDocConsumer(std::unique_ptr Finder, + std::unique_ptr &Reporter) + : Finder(std::move(Finder)), Reporter(Reporter) {} -private: - ClangDocVisitor Visitor; - ClangDocReporter &Reporter; -}; - -class ClangDocAction : public clang::ASTFrontendAction { -public: - ClangDocAction(ClangDocReporter &Reporter) : Reporter(Reporter) {} - - virtual std::unique_ptr - CreateASTConsumer(clang::CompilerInstance &C, llvm::StringRef InFile); + virtual void HandleTranslationUnit(clang::ASTContext &Context) { + Finder->matchAST(Context); + } private: - ClangDocReporter &Reporter; + std::unique_ptr Finder; + std::unique_ptr &Reporter; }; -class ClangDocActionFactory : public tooling::FrontendActionFactory { +/// Frontend action to initiate a clang-doc run on a source file. +class ClangDocAction { public: - ClangDocActionFactory(ClangDocContext &Ctx, ClangDocReporter &Reporter) - : Context(Ctx), Reporter(Reporter) {} + ClangDocAction(std::unique_ptr Finder, + std::unique_ptr &Reporter) + : Finder(std::move(Finder)), Reporter(Reporter) {} - clang::FrontendAction *create() override { - return new ClangDocAction(Reporter); + std::unique_ptr newASTConsumer() { + return llvm::make_unique(std::move(Finder), Reporter); } private: - ClangDocContext &Context; - ClangDocReporter &Reporter; + std::unique_ptr Finder; + std::unique_ptr &Reporter; }; } // namespace doc Index: tools/clang-doc/ClangDoc.cpp =================================================================== --- tools/clang-doc/ClangDoc.cpp +++ tools/clang-doc/ClangDoc.cpp @@ -8,56 +8,64 @@ //===----------------------------------------------------------------------===// #include "ClangDoc.h" +#include "clang/AST/AST.h" #include "clang/AST/Comment.h" #include "clang/AST/Mangle.h" -#include "clang/Frontend/CompilerInstance.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" using namespace clang; +using namespace clang::ast_matchers; using namespace clang::tooling; using namespace llvm; namespace clang { namespace doc { -bool ClangDocVisitor::VisitTagDecl(const TagDecl *D) { +template +void ClangDocCallback::processMatchedDecl( + const T *D, const MatchFinder::MatchResult &Result) { if (!isUnparsed(D->getLocation())) - return true; - - if (const auto *E = dyn_cast(D)) { - Reporter.createEnumInfo(E, getComment(E), getLine(E), getFile(E)); - return true; - } - if (const auto *R = dyn_cast(D)) { - Reporter.createTypeInfo(R, getComment(R), getLine(R), getFile(R)); - return true; - } - - // Error? - return true; + return; + Reporter->createInfoForDecl(D, getComment(D), D->getQualifiedNameAsString(), + getLine(D), getFile(D)); } -bool ClangDocVisitor::VisitNamespaceDecl(const NamespaceDecl *D) { +template <> +void ClangDocCallback::processMatchedDecl( + const CXXMethodDecl *D, const MatchFinder::MatchResult &Result) { + MC = Context->createMangleContext(); if (!isUnparsed(D->getLocation())) - return true; - Reporter.createNamespaceInfo(D, getComment(D), getLine(D), getFile(D)); - return true; + return; + Reporter->createInfoForDecl(D, getComment(D), mangleName(D), getLine(D), + getFile(D)); } -bool ClangDocVisitor::VisitFunctionDecl(const FunctionDecl *D) { +template <> +void ClangDocCallback::processMatchedDecl( + const FunctionDecl *D, const MatchFinder::MatchResult &Result) { + MC = Context->createMangleContext(); if (!isUnparsed(D->getLocation())) - return true; - if (const auto *C = dyn_cast(D)) { - Reporter.createMethodInfo(C, getComment(C), getLine(C), getFile(C), - mangleName(C)); - return true; - } - - Reporter.createFunctionInfo(D, getComment(D), getLine(D), getFile(D), - mangleName(D)); - return true; + return; + Reporter->createInfoForDecl(D, getComment(D), mangleName(D), getLine(D), + getFile(D)); } -comments::FullComment *ClangDocVisitor::getComment(const Decl *D) { +void ClangDocCallback::run(const MatchFinder::MatchResult &Result) { + Context = Result.Context; + if (const auto *D = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(D, Result); + else if (const auto *D = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(D, Result); + else if (const auto *D = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(D, Result); + else if (const auto *D = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(D, Result); + else if (const auto *D = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(D, Result); +} + +comments::FullComment *ClangDocCallback::getComment(const Decl *D) { RawComment *Comment = Context->getRawCommentForDeclNoCache(D); // FIXME: Move setAttached to the initial comment parsing. @@ -68,40 +76,42 @@ return nullptr; } -int ClangDocVisitor::getLine(const Decl *D) const { - PresumedLoc PLoc = Manager.getPresumedLoc(D->getLocStart()); - return PLoc.getLine(); +int ClangDocCallback::getLine(const Decl *D) const { + return Context->getSourceManager().getPresumedLoc(D->getLocStart()).getLine(); } -std::string ClangDocVisitor::getFile(const Decl *D) const { - PresumedLoc PLoc = Manager.getPresumedLoc(D->getLocStart()); - return PLoc.getFilename(); +StringRef ClangDocCallback::getFile(const Decl *D) const { + return Context->getSourceManager() + .getPresumedLoc(D->getLocStart()) + .getFilename(); } -void ClangDocVisitor::parseUnattachedComments() { +void ClangDocCallback::parseUnattachedComments() { for (RawComment *Comment : Context->getRawCommentList().getComments()) { if (!isUnparsed(Comment->getLocStart()) || Comment->isAttached()) continue; - CommentInfo CI; - Reporter.parseFullComment(Comment->parse(*Context, nullptr, nullptr), CI); - Reporter.addUnattachedComment(Manager.getFilename(Comment->getLocStart()), - CI); + Reporter->addUnattachedComment( + Context->getSourceManager().getFilename(Comment->getLocStart()), + Comment->parse(*Context, nullptr, nullptr)); } } -bool ClangDocVisitor::isUnparsed(SourceLocation Loc) const { +// Function to check if the particular file has already been parsed. +// TODO: Move this check to the consumer, not the callback. +bool ClangDocCallback::isUnparsed(SourceLocation Loc) const { if (!Loc.isValid()) return false; - const std::string &Filename = Manager.getFilename(Loc); + const std::string &Filename = Context->getSourceManager().getFilename(Loc); - if (!Reporter.hasFile(Filename)) + if (!Reporter->hasFile(Filename)) return false; - if (Manager.isInSystemHeader(Loc) || Manager.isInExternCSystemHeader(Loc)) + if (Context->getSourceManager().isInSystemHeader(Loc) || + Context->getSourceManager().isInExternCSystemHeader(Loc)) return false; return true; } -std::string ClangDocVisitor::mangleName(const FunctionDecl *D) { +std::string ClangDocCallback::mangleName(const FunctionDecl *D) const { std::string S; llvm::raw_string_ostream MangledName(S); if (const auto *Ctor = dyn_cast(D)) @@ -110,17 +120,7 @@ MC->mangleCXXDtor(Dtor, CXXDtorType::Dtor_Complete, MangledName); else MC->mangleName(D, MangledName); - return MangledName.str(); -} - -void ClangDocConsumer::HandleTranslationUnit(ASTContext &Context) { - Visitor.TraverseDecl(Context.getTranslationUnitDecl()); - Visitor.parseUnattachedComments(); -} - -std::unique_ptr -ClangDocAction::CreateASTConsumer(CompilerInstance &C, StringRef InFile) { - return make_unique(&C.getASTContext(), Reporter); + return S; } } // namespace doc Index: tools/clang-doc/ClangDocReporter.h =================================================================== --- tools/clang-doc/ClangDocReporter.h +++ tools/clang-doc/ClangDocReporter.h @@ -1,4 +1,4 @@ -//===-- Doc.cpp - ClangDoc --------------------------------------*- C++ -*-===// +//===-- ClangDocReporter.cpp - ClangDocReporter -----------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -30,7 +30,10 @@ namespace clang { namespace doc { -enum class OutFormat { YAML, LLVM }; +/// Intermediate representations for the data gathered by the ClangDocReporter +/// YAML indicates a YAML output format, and BIN indicates the clang-doc binary +/// format. +enum class OutFormat { YAML, BIN }; // Info for named types (parameters, members). struct NamedType { @@ -57,14 +60,14 @@ llvm::SmallVector Attrs; llvm::SmallVector Args; llvm::SmallVector Position; - std::vector Children; + std::vector> Children; }; /// A source file in the project. struct File { std::string Filename; - CommentInfo Description; - llvm::SmallVector UnattachedComments; + std::unique_ptr Description; + llvm::SmallVector, 8> UnattachedComments; }; /// A base struct for Infos. @@ -73,7 +76,7 @@ std::string FullyQualifiedName; std::string SimpleName; std::string Namespace; - std::vector Descriptions; + std::vector> Descriptions; llvm::SmallVector Locations; }; @@ -88,7 +91,7 @@ }; struct NamespaceInfo : public Info { - llvm::StringMap Functions; + llvm::StringMap> Functions; }; // TODO: Expand to allow for documenting templating, inheritance access, @@ -100,7 +103,7 @@ llvm::SmallVector Members; llvm::SmallVector Parents; llvm::SmallVector VirtualParents; - llvm::StringMap Functions; + llvm::StringMap> Functions; }; // TODO: Expand to allow for documenting templating. @@ -114,37 +117,20 @@ /// A struct encapsulating all documentation information for the project. struct Documentation { bool OmitFilenames = false; - llvm::StringMap Files; - llvm::StringMap Namespaces; - llvm::StringMap Types; - llvm::StringMap Enums; + llvm::StringMap> Files; + llvm::StringMap> Namespaces; + llvm::StringMap> Types; + llvm::StringMap> Enums; // TODO: Add functionality to include separate markdown pages. }; -/// Struct with a key and a value to allow for serialization. -template struct Pair { - std::string Key; - T Value; -}; - -class ClangDocReporter : public ConstCommentVisitor { +class ClangDocCommentVisitor + : public ConstCommentVisitor { public: - ClangDocReporter() {} - ClangDocReporter(const std::vector &Sources, bool OmitFilenames); + ClangDocCommentVisitor() {} - void addUnattachedComment(StringRef Filename, const CommentInfo &CI); - void addFile(StringRef Filename); + std::unique_ptr parseComment(const comments::Comment *C); - void createNamespaceInfo(const NamespaceDecl *D, const FullComment *C, - int LineNumber, StringRef File); - void createTypeInfo(const RecordDecl *D, const FullComment *C, int LineNumber, StringRef File); - void createEnumInfo(const EnumDecl *D, const FullComment *C, int LineNumber, StringRef File); - void createFunctionInfo(const FunctionDecl *D, const FullComment *C, - int LineNumber, StringRef File, StringRef MangledName); - void createMethodInfo(const CXXMethodDecl *D, const FullComment *C, - int LineNumber, StringRef File, StringRef MangledName); - - void parseFullComment(const FullComment *C, CommentInfo &CI); void visitTextComment(const TextComment *C); void visitInlineCommandComment(const InlineCommandComment *C); void visitHTMLStartTagComment(const HTMLStartTagComment *C); @@ -156,32 +142,53 @@ void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); void visitVerbatimLineComment(const VerbatimLineComment *C); +private: + StringRef getCommandName(unsigned CommandID) const; + bool isWhitespaceOnly(StringRef S) const; + + CommentInfo *CurrentCI; +}; + +class ClangDocReporter { +public: + ClangDocReporter() {} + ClangDocReporter(const std::vector &Sources, bool OmitFilenames); + + void addUnattachedComment(StringRef Filename, const FullComment *C); + void addFile(StringRef Filename); bool hasFile(StringRef Filename) const; - void serialize(clang::doc::OutFormat Format, llvm::raw_ostream &OS) const; + void serialize(clang::doc::OutFormat Format, StringRef RootDir); + + template + void createInfoForDecl(const C *D, const FullComment *FC, StringRef Key, + int LineNumber, StringRef File); + std::unique_ptr parseFullComment(const FullComment *C); private: + template + void createInfo(llvm::StringMap> &InfoMap, const C *D, + const FullComment *FC, StringRef Key, int LineNumber, + StringRef File); + template + void populateInfo(T &I, const C *D, StringRef Name, StringRef File); + void addComment(Info &I, const FullComment *C); void addLocation(Info &I, int LineNumber, StringRef File) const; - - void populateBasicInfo(Info &I, StringRef Name, StringRef SimpleName, StringRef Namespace); - void populateTypeInfo(TypeInfo &I, const RecordDecl *D, StringRef Name, StringRef File); - void populateEnumInfo(EnumInfo &I, const EnumDecl *D, StringRef Name, StringRef File); - void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, StringRef Name, StringRef File, AccessSpecifier AS); - - void parseComment(CommentInfo *CI, const comments::Comment *C); + void populateBasicInfo(Info &I, StringRef Name, StringRef SimpleName, + StringRef Namespace); void parseFields(TypeInfo &I, const RecordDecl *D) const; void parseEnumerators(EnumInfo &I, const EnumDecl *D) const; void parseBases(TypeInfo &I, const CXXRecordDecl *D) const; void parseParameters(FunctionInfo &I, const FunctionDecl *D) const; - void serializeYAML(llvm::raw_ostream &OS) const; - void serializeLLVM(llvm::raw_ostream &OS) const; + template void printMap(raw_ostream &OS, llvm::StringMap &Map); + template + void printMapPlusFunctions(raw_ostream &OS, llvm::StringMap &Map); + void serializeYAML(StringRef RootDir); + void serializeBIN(StringRef RootDir); - const char *getCommandName(unsigned CommandID) const; - bool isWhitespaceOnly(StringRef S) const; std::string getParentNamespace(const DeclContext *D) const; - CommentInfo *CurrentCI; Documentation Docs; }; Index: tools/clang-doc/ClangDocReporter.cpp =================================================================== --- tools/clang-doc/ClangDocReporter.cpp +++ tools/clang-doc/ClangDocReporter.cpp @@ -10,8 +10,8 @@ #include "ClangDocReporter.h" #include "ClangDocYAML.h" #include "llvm/Support/Path.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" using namespace llvm; using clang::comments::FullComment; @@ -19,215 +19,179 @@ namespace clang { namespace doc { -ClangDocReporter::ClangDocReporter(const std::vector &Sources, bool OF) { +ClangDocReporter::ClangDocReporter(const std::vector &Sources, + bool OF) { for (const std::string &Path : Sources) { assert(sys::path::is_absolute(Path) && "clang-doc expects absolute paths."); addFile(Path); } // Create base namespace - NamespaceInfo I; - I.isDefined = true; - Docs.Namespaces[""] = I; + Docs.Namespaces[""] = llvm::make_unique(); Docs.OmitFilenames = OF; } void ClangDocReporter::addUnattachedComment(StringRef Filename, - const CommentInfo &CI) { - Docs.Files[Filename].UnattachedComments.push_back(CI); + const FullComment *C) { + Docs.Files[Filename]->UnattachedComments.push_back( + std::move(parseFullComment(C))); } void ClangDocReporter::addFile(StringRef Filename) { - File F; - F.Filename = Filename; - Docs.Files.insert(std::make_pair(Filename, F)); -} - -void ClangDocReporter::createNamespaceInfo(const NamespaceDecl *D, - const FullComment *C, - int LineNumber, StringRef File) { - std::string Name = D->getQualifiedNameAsString(); - llvm::StringMapIterator Pair = Docs.Namespaces.find(Name); - if (Pair == Docs.Namespaces.end()) { - NamespaceInfo I; - Docs.Namespaces[Name] = I; - populateBasicInfo(Docs.Namespaces[Name], Name, D->getNameAsString(), getParentNamespace(D)); - } - - addLocation(Docs.Namespaces[Name], LineNumber, File); - addComment(Docs.Namespaces[Name], C); + Docs.Files[Filename] = llvm::make_unique(); + Docs.Files[Filename]->Filename = Filename; } -void ClangDocReporter::createTypeInfo(const RecordDecl *D, const FullComment *C, - int LineNumber, StringRef File) { - - std::string Name = D->getQualifiedNameAsString(); - llvm::StringMapIterator Pair = Docs.Types.find(Name); - if (Pair == Docs.Types.end()) { - TypeInfo I; - Docs.Types[Name] = I; - } - - if (D->isThisDeclarationADefinition()) - populateTypeInfo(Docs.Types[Name], D, Name, File); - - addLocation(Docs.Types[Name], LineNumber, File); - addComment(Docs.Types[Name], C); -} - -void ClangDocReporter::createEnumInfo(const EnumDecl *D, const FullComment *C, - int LineNumber, StringRef File) { - std::string Name = D->getQualifiedNameAsString(); - llvm::StringMapIterator Pair = Docs.Enums.find(Name); - if (Pair == Docs.Enums.end()) { - EnumInfo I; - Docs.Enums[Name] = I; - } - - if (D->isThisDeclarationADefinition()) - populateEnumInfo(Docs.Enums[Name], D, Name, File); - - addLocation(Docs.Enums[Name], LineNumber, File); - addComment(Docs.Enums[Name], C); +template <> +void ClangDocReporter::populateInfo(NamespaceInfo &I, const NamespaceDecl *D, + StringRef Name, StringRef File) { + populateBasicInfo(I, Name, D->getNameAsString(), getParentNamespace(D)); } -void ClangDocReporter::createFunctionInfo(const FunctionDecl *D, - const FullComment *C, int LineNumber, StringRef File, - StringRef MangledName) { - std::string Namespace = getParentNamespace(D); - llvm::StringMapIterator NS = Docs.Namespaces.find(Namespace); - if (NS == Docs.Namespaces.end()) { - NamespaceInfo NI;; - NI.FullyQualifiedName = Namespace; - Docs.Namespaces[Namespace] = NI; - } - - llvm::StringMapIterator Pair = Docs.Namespaces[Namespace].Functions.find(MangledName); - if (Pair != Docs.Namespaces[Namespace].Functions.end()) { - FunctionInfo I; - Docs.Namespaces[Namespace].Functions[MangledName] = I; - } - - if (D->isThisDeclarationADefinition()) - populateFunctionInfo(Docs.Namespaces[Namespace].Functions[MangledName], D, MangledName, File, clang::AccessSpecifier::AS_none); - - addLocation(Docs.Namespaces[Namespace].Functions[MangledName], LineNumber, File); - addComment(Docs.Namespaces[Namespace].Functions[MangledName], C); +template <> +void ClangDocReporter::populateInfo(TypeInfo &I, const RecordDecl *D, + StringRef Name, StringRef File) { + if (!D->isThisDeclarationADefinition()) + return; + populateBasicInfo(I, Name, D->getNameAsString(), getParentNamespace(D)); + I.TagType = D->getTagKind(); + if (!Docs.OmitFilenames) + I.DefinitionFile = File; + if (const auto *CXXR = dyn_cast(D)) + parseBases(I, CXXR); + parseFields(I, D); } -void ClangDocReporter::createMethodInfo(const CXXMethodDecl *D, - const FullComment *C, int LineNumber, StringRef File, - StringRef MangledName) { - std::string ParentName = D->getParent()->getQualifiedNameAsString(); - llvm::StringMapIterator T = Docs.Types.find(ParentName); - if (T == Docs.Types.end()) { - TypeInfo TI; - TI.FullyQualifiedName = getParentNamespace(D); - Docs.Types[ParentName] = TI; - } - - llvm::StringMapIterator Pair = Docs.Types[ParentName].Functions.find(MangledName); - if (Pair != Docs.Types[ParentName].Functions.end()) { - FunctionInfo I; - Docs.Types[ParentName].Functions[MangledName] = I; - } - - if (D->isThisDeclarationADefinition()) - populateFunctionInfo(Docs.Types[ParentName].Functions[MangledName], D, MangledName, File, D->getAccess()); - - addLocation(Docs.Types[ParentName].Functions[MangledName], LineNumber, File); - addComment(Docs.Types[ParentName].Functions[MangledName], C); +template <> +void ClangDocReporter::populateInfo(FunctionInfo &I, const FunctionDecl *D, + StringRef Name, StringRef File) { + if (!D->isThisDeclarationADefinition()) + return; + populateBasicInfo(I, D->getQualifiedNameAsString(), D->getNameAsString(), + getParentNamespace(D)); + I.MangledName = Name; + if (!Docs.OmitFilenames) + I.DefinitionFile = File; + I.ReturnType = D->getReturnType().getAsString(); + I.Access = clang::AccessSpecifier::AS_none; + parseParameters(I, D); } -void ClangDocReporter::parseFullComment(const FullComment *C, CommentInfo &CI) { - parseComment(&CI, C); +template <> +void ClangDocReporter::populateInfo(FunctionInfo &I, const CXXMethodDecl *D, + StringRef Name, StringRef File) { + if (!D->isThisDeclarationADefinition()) + return; + populateBasicInfo(I, D->getQualifiedNameAsString(), D->getNameAsString(), + getParentNamespace(D)); + I.MangledName = Name; + if (!Docs.OmitFilenames) + I.DefinitionFile = File; + I.ReturnType = D->getReturnType().getAsString(); + I.Access = D->getAccess(); + parseParameters(I, D); } -void ClangDocReporter::visitTextComment(const TextComment *C) { - if (!isWhitespaceOnly(C->getText())) - CurrentCI->Text = C->getText(); +template <> +void ClangDocReporter::populateInfo(EnumInfo &I, const EnumDecl *D, + StringRef Name, StringRef File) { + if (!D->isThisDeclarationADefinition()) + return; + populateBasicInfo(I, Name, D->getNameAsString(), getParentNamespace(D)); + if (!Docs.OmitFilenames) + I.DefinitionFile = File; + I.Scoped = D->isScoped(); + parseEnumerators(I, D); } -void ClangDocReporter::visitInlineCommandComment( - const InlineCommandComment *C) { - CurrentCI->Name = getCommandName(C->getCommandID()); - for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) - CurrentCI->Args.push_back(C->getArgText(i)); +template <> +void ClangDocReporter::createInfoForDecl(const EnumDecl *D, + const FullComment *FC, StringRef Key, + int LineNumber, StringRef File) { + createInfo(Docs.Enums, D, FC, Key, LineNumber, File); } -void ClangDocReporter::visitHTMLStartTagComment(const HTMLStartTagComment *C) { - CurrentCI->Name = C->getTagName(); - CurrentCI->SelfClosing = C->isSelfClosing(); - for (unsigned i = 0, e = C->getNumAttrs(); i < e; ++i) { - const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); - NamedType T{Attr.Name, Attr.Value, clang::AccessSpecifier::AS_none}; - CurrentCI->Attrs.push_back(T); - } +template <> +void ClangDocReporter::createInfoForDecl(const RecordDecl *D, + const FullComment *FC, StringRef Key, + int LineNumber, StringRef File) { + createInfo(Docs.Types, D, FC, Key, LineNumber, File); } -void ClangDocReporter::visitHTMLEndTagComment(const HTMLEndTagComment *C) { - CurrentCI->Name = C->getTagName(); - CurrentCI->SelfClosing = true; -} - -void ClangDocReporter::visitBlockCommandComment(const BlockCommandComment *C) { - CurrentCI->Name = getCommandName(C->getCommandID()); - for (unsigned i = 0, e = C->getNumArgs(); i < e; ++i) - CurrentCI->Args.push_back(C->getArgText(i)); +template <> +void ClangDocReporter::createInfoForDecl(const NamespaceDecl *D, + const FullComment *FC, StringRef Key, + int LineNumber, StringRef File) { + createInfo(Docs.Namespaces, D, FC, Key, + LineNumber, File); } -void ClangDocReporter::visitParamCommandComment(const ParamCommandComment *C) { - CurrentCI->Direction = - ParamCommandComment::getDirectionAsString(C->getDirection()); - CurrentCI->Explicit = C->isDirectionExplicit(); - if (C->hasParamName() && C->isParamIndexValid()) - CurrentCI->ParamName = C->getParamNameAsWritten(); +template <> +void ClangDocReporter::createInfoForDecl(const FunctionDecl *D, + const FullComment *FC, StringRef Key, + int LineNumber, StringRef File) { + std::string Namespace = getParentNamespace(D); + llvm::StringMapIterator> NS = + Docs.Namespaces.find(Namespace); + if (NS == Docs.Namespaces.end()) { + Docs.Namespaces[Namespace] = llvm::make_unique(); + Docs.Namespaces[Namespace]->FullyQualifiedName = Namespace; + } + createInfo(Docs.Namespaces[Namespace]->Functions, + D, FC, Key, LineNumber, File); } -void ClangDocReporter::visitTParamCommandComment( - const TParamCommandComment *C) { - if (C->hasParamName() && C->isPositionValid()) - CurrentCI->ParamName = C->getParamNameAsWritten(); - - if (C->isPositionValid()) { - for (unsigned i = 0, e = C->getDepth(); i < e; ++i) - CurrentCI->Position.push_back(C->getIndex(i)); +template <> +void ClangDocReporter::createInfoForDecl(const CXXMethodDecl *D, + const FullComment *FC, StringRef Key, + int LineNumber, StringRef File) { + std::string ParentName = D->getParent()->getQualifiedNameAsString(); + llvm::StringMapIterator> T = + Docs.Types.find(ParentName); + if (T == Docs.Types.end()) { + Docs.Types[ParentName] = llvm::make_unique(); + Docs.Types[ParentName]->FullyQualifiedName = getParentNamespace(D); } + createInfo(Docs.Types[ParentName]->Functions, D, + FC, Key, LineNumber, File); } -void ClangDocReporter::visitVerbatimBlockComment( - const VerbatimBlockComment *C) { - CurrentCI->Name = getCommandName(C->getCommandID()); - CurrentCI->CloseName = C->getCloseName(); -} +template +void ClangDocReporter::createInfo(StringMap> &InfoMap, + const C *D, const FullComment *FC, + StringRef Key, int LineNumber, + StringRef File) { + llvm::StringMapIterator> Pair = InfoMap.find(Key); + if (Pair == InfoMap.end()) + InfoMap[Key] = llvm::make_unique(); -void ClangDocReporter::visitVerbatimBlockLineComment( - const VerbatimBlockLineComment *C) { - if (!isWhitespaceOnly(C->getText())) - CurrentCI->Text = C->getText(); + populateInfo(*InfoMap[Key], D, Key, File); + addLocation(*InfoMap[Key], LineNumber, File); + addComment(*InfoMap[Key], FC); } -void ClangDocReporter::visitVerbatimLineComment(const VerbatimLineComment *C) { - if (!isWhitespaceOnly(C->getText())) - CurrentCI->Text = C->getText(); +std::unique_ptr +ClangDocReporter::parseFullComment(const FullComment *C) { + ClangDocCommentVisitor Visitor; + return Visitor.parseComment(C); } bool ClangDocReporter::hasFile(StringRef Filename) const { return Docs.Files.find(Filename) != Docs.Files.end(); } -void ClangDocReporter::serialize(OutFormat Format, raw_ostream &OS) const { - Format == clang::doc::OutFormat::LLVM ? serializeLLVM(OS) : serializeYAML(OS); +void ClangDocReporter::serialize(OutFormat Format, StringRef RootDir) { + Format == clang::doc::OutFormat::BIN ? serializeBIN(RootDir) + : serializeYAML(RootDir); } void ClangDocReporter::addComment(Info &I, const FullComment *C) { if (!C) return; - CommentInfo CI; - parseFullComment(C, CI); - I.Descriptions.push_back(CI); + I.Descriptions.push_back(std::move(parseFullComment(C))); } -void ClangDocReporter::addLocation(Info &I, int LineNumber, StringRef File) const { +void ClangDocReporter::addLocation(Info &I, int LineNumber, + StringRef File) const { Location L; L.LineNumber = LineNumber; if (!Docs.OmitFilenames) @@ -235,63 +199,22 @@ I.Locations.push_back(L); } -void ClangDocReporter::populateBasicInfo(Info &I, StringRef Name, StringRef SimpleName, StringRef Namespace) { +void ClangDocReporter::populateBasicInfo(Info &I, StringRef Name, + StringRef SimpleName, + StringRef Namespace) { I.FullyQualifiedName = Name; I.SimpleName = SimpleName; I.Namespace = Namespace; I.isDefined = true; } -void ClangDocReporter::populateTypeInfo(TypeInfo &I, const RecordDecl * D, StringRef Name, StringRef File) { - populateBasicInfo(I, Name, D->getNameAsString(), getParentNamespace(D)); - I.TagType = D->getTagKind(); - if (!Docs.OmitFilenames) - I.DefinitionFile = File; - if (const auto *CXXR = dyn_cast(D)) - parseBases(I, CXXR); - parseFields(I, D); -} - -void ClangDocReporter::populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, StringRef Name, StringRef File, AccessSpecifier AS) { - populateBasicInfo(I, D->getQualifiedNameAsString(), D->getNameAsString(), getParentNamespace(D)); - I.MangledName = Name; - if (!Docs.OmitFilenames) - I.DefinitionFile = File; - I.ReturnType = D->getReturnType().getAsString(); - I.Access = AS; - parseParameters(I, D); -} - -void ClangDocReporter::populateEnumInfo(EnumInfo &I, const EnumDecl *D, StringRef Name, StringRef File) { - populateBasicInfo(I, Name, D->getNameAsString(), getParentNamespace(D)); - if (!Docs.OmitFilenames) - I.DefinitionFile = File; - I.Scoped = D->isScoped(); - parseEnumerators(Docs.Enums[Name], D); -} - -void ClangDocReporter::parseComment(CommentInfo *CI, - const comments::Comment *C) { - CurrentCI = CI; - CI->Kind = C->getCommentKindName(); - ConstCommentVisitor::visit(C); - for (comments::Comment *Child : - make_range(C->child_begin(), C->child_end())) { - CommentInfo ChildCI; - parseComment(&ChildCI, Child); - CI->Children.push_back(ChildCI); - } -} - void ClangDocReporter::parseFields(TypeInfo &I, const RecordDecl *D) const { for (const FieldDecl *F : D->fields()) { NamedType N; N.Type = F->getTypeSourceInfo()->getType().getAsString(); N.Name = F->getQualifiedNameAsString(); - if (const auto *M = dyn_cast(D)) - N.Access = M->getAccess(); - else - N.Access = clang::AccessSpecifier::AS_none; + // FIXME: Set Access to the appropriate value. + N.Access = clang::AccessSpecifier::AS_none; I.Members.push_back(N); } } @@ -318,33 +241,84 @@ void ClangDocReporter::parseBases(TypeInfo &I, const CXXRecordDecl *D) const { for (const CXXBaseSpecifier &B : D->bases()) { - if (!B.isVirtual()) I.Parents.push_back(B.getType().getAsString()); + if (!B.isVirtual()) + I.Parents.push_back(B.getType().getAsString()); } for (const CXXBaseSpecifier &B : D->vbases()) I.VirtualParents.push_back(B.getType().getAsString()); } -void ClangDocReporter::serializeYAML(raw_ostream &OS) const { - yaml::Output Output(OS); - Documentation NonConstValue = Docs; - Output << NonConstValue; +template +void ClangDocReporter::printMap(raw_ostream &OS, StringMap &Map) { + yaml::Output YOut(OS); + for (const auto &I : Map) + YOut << *(I.second); } -void ClangDocReporter::serializeLLVM(raw_ostream &OS) const { - // TODO: Implement. - OS << "Not yet implemented.\n"; +template +void ClangDocReporter::printMapPlusFunctions(raw_ostream &OS, + StringMap &Map) { + yaml::Output YOut(OS); + for (const auto &I : Map) { + YOut << *(I.second); + for (const auto &F : I.second->Functions) + YOut << *(F.second); + } } -const char *ClangDocReporter::getCommandName(unsigned CommandID) const { - const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID); - if (Info) - return Info->Name; - // TODO: Add parsing for \file command. - return ""; +void ClangDocReporter::serializeYAML(StringRef RootDir) { + if (RootDir.empty()) { + printMap(outs(), Docs.Files); + printMapPlusFunctions(outs(), Docs.Namespaces); + printMapPlusFunctions(outs(), Docs.Types); + printMap(outs(), Docs.Enums); + return; + } + std::error_code OK; + std::error_code OutErrorInfo; + SmallString<128> FilePath; + + sys::path::native(RootDir, FilePath); + sys::path::append(FilePath, "files.yaml"); + raw_fd_ostream FileOS(FilePath, OutErrorInfo, sys::fs::F_None); + if (OutErrorInfo != OK) { + errs() << OutErrorInfo.message(); + errs() << " Error opening documentation file.\n"; + return; + } + printMap(FileOS, Docs.Files); + + sys::path::native(RootDir, FilePath); + sys::path::append(FilePath, "namespaces.yaml"); + raw_fd_ostream NamespaceOS(FilePath, OutErrorInfo, sys::fs::F_None); + if (OutErrorInfo != OK) { + errs() << "Error opening documentation file.\n"; + return; + } + printMapPlusFunctions(NamespaceOS, Docs.Namespaces); + + sys::path::native(RootDir, FilePath); + sys::path::append(FilePath, "types.yaml"); + raw_fd_ostream TypeOS(FilePath, OutErrorInfo, sys::fs::F_None); + if (OutErrorInfo != OK) { + errs() << "Error opening documentation file.\n"; + return; + } + printMapPlusFunctions(TypeOS, Docs.Types); + + sys::path::native(RootDir, FilePath); + sys::path::append(FilePath, "enums.yaml"); + raw_fd_ostream EnumOS(FilePath, OutErrorInfo, sys::fs::F_None); + if (OutErrorInfo != OK) { + errs() << "Error opening documentation file.\n"; + return; + } + printMap(EnumOS, Docs.Enums); } -bool ClangDocReporter::isWhitespaceOnly(StringRef S) const { - return S.find_first_not_of(" \t\n\v\f\r") == std::string::npos || S.empty(); +void ClangDocReporter::serializeBIN(StringRef RootDir) { + // TODO: Implement. + outs() << "Not yet implemented.\n"; } std::string ClangDocReporter::getParentNamespace(const DeclContext *D) const { @@ -353,5 +327,105 @@ return ""; } +// TODO: Remove raw pointer to CurrentCI. +std::unique_ptr +ClangDocCommentVisitor::parseComment(const comments::Comment *C) { + std::unique_ptr RootCI = llvm::make_unique(); + RootCI->Kind = C->getCommentKindName(); + CurrentCI = RootCI.get(); + ConstCommentVisitor::visit(C); + for (comments::Comment *Child : + make_range(C->child_begin(), C->child_end())) { + RootCI->Children.push_back(std::move(parseComment(Child))); + } + return RootCI; +} + +void ClangDocCommentVisitor::visitTextComment(const TextComment *C) { + if (!isWhitespaceOnly(C->getText())) + CurrentCI->Text = C->getText(); +} + +void ClangDocCommentVisitor::visitInlineCommandComment( + const InlineCommandComment *C) { + CurrentCI->Name = getCommandName(C->getCommandID()); + for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i) + CurrentCI->Args.push_back(C->getArgText(i)); +} + +void ClangDocCommentVisitor::visitHTMLStartTagComment( + const HTMLStartTagComment *C) { + CurrentCI->Name = C->getTagName(); + CurrentCI->SelfClosing = C->isSelfClosing(); + for (unsigned i = 0, e = C->getNumAttrs(); i < e; ++i) { + const HTMLStartTagComment::Attribute &Attr = C->getAttr(i); + CurrentCI->Attrs.emplace_back( + NamedType{Attr.Name, Attr.Value, clang::AccessSpecifier::AS_none}); + } +} + +void ClangDocCommentVisitor::visitHTMLEndTagComment( + const HTMLEndTagComment *C) { + CurrentCI->Name = C->getTagName(); + CurrentCI->SelfClosing = true; +} + +void ClangDocCommentVisitor::visitBlockCommandComment( + const BlockCommandComment *C) { + CurrentCI->Name = getCommandName(C->getCommandID()); + for (unsigned i = 0, e = C->getNumArgs(); i < e; ++i) + CurrentCI->Args.push_back(C->getArgText(i)); +} + +void ClangDocCommentVisitor::visitParamCommandComment( + const ParamCommandComment *C) { + CurrentCI->Direction = + ParamCommandComment::getDirectionAsString(C->getDirection()); + CurrentCI->Explicit = C->isDirectionExplicit(); + if (C->hasParamName() && C->isParamIndexValid()) + CurrentCI->ParamName = C->getParamNameAsWritten(); +} + +void ClangDocCommentVisitor::visitTParamCommandComment( + const TParamCommandComment *C) { + if (C->hasParamName() && C->isPositionValid()) + CurrentCI->ParamName = C->getParamNameAsWritten(); + + if (C->isPositionValid()) { + for (unsigned i = 0, e = C->getDepth(); i < e; ++i) + CurrentCI->Position.push_back(C->getIndex(i)); + } +} + +void ClangDocCommentVisitor::visitVerbatimBlockComment( + const VerbatimBlockComment *C) { + CurrentCI->Name = getCommandName(C->getCommandID()); + CurrentCI->CloseName = C->getCloseName(); +} + +void ClangDocCommentVisitor::visitVerbatimBlockLineComment( + const VerbatimBlockLineComment *C) { + if (!isWhitespaceOnly(C->getText())) + CurrentCI->Text = C->getText(); +} + +void ClangDocCommentVisitor::visitVerbatimLineComment( + const VerbatimLineComment *C) { + if (!isWhitespaceOnly(C->getText())) + CurrentCI->Text = C->getText(); +} + +StringRef ClangDocCommentVisitor::getCommandName(unsigned CommandID) const { + const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID); + if (Info) + return Info->Name; + // TODO: Add parsing for \file command. + return ""; +} + +bool ClangDocCommentVisitor::isWhitespaceOnly(StringRef S) const { + return S.find_first_not_of(" \t\n\v\f\r") == std::string::npos; +} + } // namespace doc } // namespace clang Index: tools/clang-doc/ClangDocYAML.h =================================================================== --- tools/clang-doc/ClangDocYAML.h +++ tools/clang-doc/ClangDocYAML.h @@ -12,37 +12,15 @@ #include "llvm/Support/YAMLTraits.h" -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::CommentInfo) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::NamedType) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Location) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair) -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::doc::Pair) +using namespace clang::doc; + +LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) +LLVM_YAML_IS_SEQUENCE_VECTOR(NamedType) +LLVM_YAML_IS_SEQUENCE_VECTOR(Location) namespace llvm { namespace yaml { -template struct NormalizedMap { - NormalizedMap(IO &) {} - NormalizedMap(IO &, const StringMap &Map) { - for (const auto &Entry : Map) { - clang::doc::Pair Pair{Entry.getKeyData(), Entry.getValue()}; - VectorMap.push_back(Pair); - } - } - - StringMap denormalize(IO &) { - StringMap Map; - for (const auto &Pair : VectorMap) - Map[Pair.Key] = Pair.Value; - return Map; - } - - std::vector> VectorMap; -}; - template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, clang::AccessSpecifier &value) { io.enumCase(value, "Public", clang::AccessSpecifier::AS_public); @@ -62,152 +40,117 @@ } }; -template <> struct MappingTraits> { - static void mapping(IO &IO, clang::doc::Pair &Pair) { - IO.mapOptional("Value", Pair.Value); +template <> struct MappingTraits { + static void mapping(IO &IO, Location &Location) { + IO.mapRequired("LineNumber", Location.LineNumber); + IO.mapRequired("Filename", Location.Filename); } }; -template <> struct MappingTraits { - static void mapping(IO &IO, clang::doc::Location &Location) { - IO.mapOptional("LineNumber", Location.LineNumber); - IO.mapOptional("Filename", Location.Filename); - } -}; - -// TODO: uncomment description attrs -template <> struct MappingTraits> { - static void mapping(IO &IO, clang::doc::Pair &Pair) { - if (!Pair.Value.isDefined) +template <> struct MappingTraits { + static void mapping(IO &IO, NamespaceInfo &N) { + if (!N.isDefined) return; - MappingNormalization, - StringMap> - functions(IO, Pair.Value.Functions); - - IO.mapOptional("Qualified Name", Pair.Value.FullyQualifiedName); - IO.mapOptional("Name", Pair.Value.SimpleName); - IO.mapOptional("Namespace", Pair.Value.Namespace); - IO.mapOptional("Descriptions", Pair.Value.Descriptions); - IO.mapOptional("Locations", Pair.Value.Locations); - IO.mapOptional("Functions", functions->VectorMap); + IO.mapRequired("Qualified Name", N.FullyQualifiedName); + IO.mapRequired("Name", N.SimpleName); + IO.mapRequired("Namespace", N.Namespace); + IO.mapRequired("Descriptions", N.Descriptions); + IO.mapRequired("Locations", N.Locations); } }; -template <> struct MappingTraits> { - static void mapping(IO &IO, clang::doc::Pair &Pair) { - if (!Pair.Value.isDefined) +template <> struct MappingTraits { + static void mapping(IO &IO, TypeInfo &T) { + if (!T.isDefined) return; - - MappingNormalization, - StringMap> - functions(IO, Pair.Value.Functions); - - IO.mapOptional("Qualified Name", Pair.Value.FullyQualifiedName); - IO.mapOptional("Name", Pair.Value.SimpleName); - IO.mapOptional("Namespace", Pair.Value.Namespace); - IO.mapOptional("Descriptions", Pair.Value.Descriptions); - IO.mapOptional("TagType", Pair.Value.TagType); - IO.mapOptional("Locations", Pair.Value.Locations); - IO.mapOptional("Functions", functions->VectorMap); - IO.mapOptional("DefinitionFile", Pair.Value.DefinitionFile); - IO.mapOptional("Members", Pair.Value.Members); - IO.mapOptional("Parents", Pair.Value.Parents); - IO.mapOptional("VirtualParents", Pair.Value.VirtualParents); + IO.mapRequired("Qualified Name", T.FullyQualifiedName); + IO.mapRequired("Name", T.SimpleName); + IO.mapRequired("Namespace", T.Namespace); + IO.mapRequired("Descriptions", T.Descriptions); + IO.mapRequired("TagType", T.TagType); + IO.mapRequired("Locations", T.Locations); + IO.mapRequired("DefinitionFile", T.DefinitionFile); + IO.mapRequired("Members", T.Members); + IO.mapRequired("Parents", T.Parents); + IO.mapRequired("VirtualParents", T.VirtualParents); } }; -template <> struct MappingTraits> { - static void mapping(IO &IO, clang::doc::Pair &Pair) { - if (!Pair.Value.isDefined) +template <> struct MappingTraits { + static void mapping(IO &IO, EnumInfo &E) { + if (!E.isDefined) return; - IO.mapOptional("Qualified Name", Pair.Value.FullyQualifiedName); - IO.mapOptional("Name", Pair.Value.SimpleName); - IO.mapOptional("Namespace", Pair.Value.Namespace); - IO.mapOptional("Descriptions", Pair.Value.Descriptions); - IO.mapOptional("Locations", Pair.Value.Locations); - IO.mapOptional("DefinitionFile", Pair.Value.DefinitionFile); - IO.mapOptional("Scoped", Pair.Value.Scoped); - IO.mapOptional("Members", Pair.Value.Members); + IO.mapRequired("Qualified Name", E.FullyQualifiedName); + IO.mapRequired("Name", E.SimpleName); + IO.mapRequired("Namespace", E.Namespace); + IO.mapRequired("Descriptions", E.Descriptions); + IO.mapRequired("Locations", E.Locations); + IO.mapRequired("DefinitionFile", E.DefinitionFile); + IO.mapRequired("Scoped", E.Scoped); + IO.mapRequired("Members", E.Members); } }; -template <> struct MappingTraits { - static void mapping(IO &IO, clang::doc::NamedType &NamedType) { - IO.mapOptional("Type", NamedType.Type); - IO.mapOptional("Name", NamedType.Name); - IO.mapOptional("Access", NamedType.Access); +template <> struct MappingTraits { + static void mapping(IO &IO, NamedType &NamedType) { + IO.mapRequired("Type", NamedType.Type); + IO.mapRequired("Name", NamedType.Name); + IO.mapRequired("Access", NamedType.Access); } }; -template <> struct MappingTraits> { - static void mapping(IO &IO, - clang::doc::Pair &Pair) { - if (!Pair.Value.isDefined) +template <> struct MappingTraits { + static void mapping(IO &IO, FunctionInfo &F) { + if (!F.isDefined) return; - IO.mapOptional("Mangled Name", Pair.Value.MangledName); - IO.mapOptional("Qualified Name", Pair.Value.FullyQualifiedName); - IO.mapOptional("Name", Pair.Value.SimpleName); - IO.mapOptional("Namespace", Pair.Value.Namespace); - IO.mapOptional("Descriptions", Pair.Value.Descriptions); - IO.mapOptional("Locations", Pair.Value.Locations); - IO.mapOptional("DefinitionFile", Pair.Value.DefinitionFile); - IO.mapOptional("Params", Pair.Value.Params); - IO.mapOptional("ReturnType", Pair.Value.ReturnType); - IO.mapOptional("Access", Pair.Value.Access); + IO.mapRequired("Mangled Name", F.MangledName); + IO.mapRequired("Qualified Name", F.FullyQualifiedName); + IO.mapRequired("Name", F.SimpleName); + IO.mapRequired("Namespace", F.Namespace); + IO.mapRequired("Descriptions", F.Descriptions); + IO.mapRequired("Locations", F.Locations); + IO.mapRequired("DefinitionFile", F.DefinitionFile); + IO.mapRequired("Params", F.Params); + IO.mapRequired("ReturnType", F.ReturnType); + IO.mapRequired("Access", F.Access); } }; -template <> struct MappingTraits> { - static void mapping(IO &IO, clang::doc::Pair &Pair) { - IO.mapOptional("Filename", Pair.Value.Filename); - IO.mapOptional("Description", Pair.Value.Description); +template <> struct MappingTraits { + static void mapping(IO &IO, File &F) { + IO.mapRequired("Filename", F.Filename); + IO.mapRequired("Description", F.Description); } }; -template <> struct MappingTraits { - static void mapping(IO &IO, clang::doc::Documentation &Docs) { - MappingNormalization, - StringMap> - files(IO, Docs.Files); - MappingNormalization, - StringMap> - namespaces(IO, Docs.Namespaces); - MappingNormalization, - StringMap> - types(IO, Docs.Types); - MappingNormalization, - StringMap> - enums(IO, Docs.Enums); - - IO.mapOptional("Files", files->VectorMap); - IO.mapOptional("Namespaces", namespaces->VectorMap); - IO.mapOptional("Types", types->VectorMap); - IO.mapOptional("Enums", enums->VectorMap); - } -}; +template <> struct MappingTraits> { -template <> struct MappingTraits { - - static void mapping(IO &IO, clang::doc::CommentInfo &Info) { - IO.mapOptional("Kind", Info.Kind); - if (!Info.Text.empty()) - IO.mapOptional("Text", Info.Text); - if (!Info.Name.empty()) - IO.mapOptional("Name", Info.Name); - if (!Info.Direction.empty()) - IO.mapOptional("Direction", Info.Direction); - if (!Info.ParamName.empty()) - IO.mapOptional("ParamName", Info.ParamName); - if (!Info.CloseName.empty()) - IO.mapOptional("CloseName", Info.CloseName); - if (Info.SelfClosing) - IO.mapOptional("SelfClosing", Info.SelfClosing); - if (Info.Explicit) - IO.mapOptional("Explicit", Info.Explicit); - IO.mapOptional("Args", Info.Args); - IO.mapOptional("Attrs", Info.Attrs); - IO.mapOptional("Position", Info.Position); - IO.mapOptional("Children", Info.Children); + static void mapping(IO &IO, std::unique_ptr &Info) { + if (!Info) + return; + IO.mapRequired("Kind", Info->Kind); + if (!Info->Text.empty()) + IO.mapRequired("Text", Info->Text); + if (!Info->Name.empty()) + IO.mapRequired("Name", Info->Name); + if (!Info->Direction.empty()) + IO.mapRequired("Direction", Info->Direction); + if (!Info->ParamName.empty()) + IO.mapRequired("ParamName", Info->ParamName); + if (!Info->CloseName.empty()) + IO.mapRequired("CloseName", Info->CloseName); + if (Info->SelfClosing) + IO.mapRequired("SelfClosing", Info->SelfClosing); + if (Info->Explicit) + IO.mapRequired("Explicit", Info->Explicit); + if (!Info->Args.empty()) + IO.mapRequired("Args", Info->Args); + if (!Info->Attrs.empty()) + IO.mapRequired("Attrs", Info->Attrs); + if (!Info->Position.empty()) + IO.mapRequired("Position", Info->Position); + if (!Info->Children.empty()) + IO.mapRequired("Children", Info->Children); } }; Index: tools/clang-doc/tool/ClangDocMain.cpp =================================================================== --- tools/clang-doc/tool/ClangDocMain.cpp +++ tools/clang-doc/tool/ClangDocMain.cpp @@ -8,35 +8,44 @@ //===----------------------------------------------------------------------===// #include "ClangDoc.h" +#include "clang/AST/AST.h" +#include "clang/AST/Decl.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" #include "clang/Driver/Options.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" #include +using namespace clang::ast_matchers; using namespace clang; using namespace llvm; namespace { -cl::OptionCategory ClangDocCategory("clang-doc options"); +static cl::OptionCategory ClangDocCategory("clang-doc options"); + +static cl::opt + OutDirectory("root", cl::desc("Directory for generated files."), + cl::init("docs"), cl::cat(ClangDocCategory)); static cl::opt - EmitLLVM("emit-llvm", - cl::desc("Output in LLVM bitstream format (default is YAML)."), + EmitYAML("emit-yaml", + cl::desc("Output in YAML format (default is clang-doc binary)."), cl::init(false), cl::cat(ClangDocCategory)); -static cl::opt - DumpResult("dump", - cl::desc("Dump results to stdout."), - cl::init(false), cl::cat(ClangDocCategory)); +static cl::opt DumpResult("dump", cl::desc("Dump results to stdout."), + cl::init(false), cl::cat(ClangDocCategory)); -static cl::opt - OmitFilenames("omit-filenames", - cl::desc("Omit filenames in output."), - cl::init(false), cl::cat(ClangDocCategory)); +static cl::opt OmitFilenames("omit-filenames", + cl::desc("Omit filenames in output."), + cl::init(false), cl::cat(ClangDocCategory)); static cl::opt DoxygenOnly("doxygen", @@ -48,12 +57,51 @@ int main(int argc, const char **argv) { sys::PrintStackTraceOnErrorSignal(argv[0]); tooling::CommonOptionsParser OptionsParser(argc, argv, ClangDocCategory); - - clang::doc::OutFormat EmitFormat = - EmitLLVM ? doc::OutFormat::LLVM : doc::OutFormat::YAML; - - doc::ClangDocReporter Reporter(OptionsParser.getSourcePathList(), OmitFilenames); - doc::ClangDocContext Context{EmitFormat}; + std::error_code OK; + + doc::OutFormat EmitFormat; + SmallString<128> IRFilePath; + sys::path::native(OutDirectory, IRFilePath); + std::string IRFilename; + if (EmitYAML) { + EmitFormat = doc::OutFormat::YAML; + sys::path::append(IRFilePath, "yaml"); + } else { + EmitFormat = doc::OutFormat::BIN; + sys::path::append(IRFilePath, "bin"); + } + + std::error_code DirectoryStatus = sys::fs::create_directories(IRFilePath); + if (DirectoryStatus != OK) { + errs() << "Unable to create documentation directories.\n"; + return 1; + } + + std::unique_ptr Reporter = + llvm::make_unique( + OptionsParser.getSourcePathList(), OmitFilenames); + std::unique_ptr Finder = llvm::make_unique(); + + doc::ClangDocCallback NCallback(Reporter, "namespace"); + DeclarationMatcher ND = namespaceDecl().bind("namespace"); + Finder->addMatcher(ND, &NCallback); + + doc::ClangDocCallback RCallback(Reporter, "record"); + DeclarationMatcher RD = recordDecl().bind("record"); + Finder->addMatcher(RD, &RCallback); + + doc::ClangDocCallback ECallback(Reporter, "enum"); + DeclarationMatcher ED = enumDecl().bind("enum"); + Finder->addMatcher(ED, &ECallback); + + doc::ClangDocCallback FCallback(Reporter, "function"); + DeclarationMatcher FD = + functionDecl(unless(cxxMethodDecl())).bind("function"); + Finder->addMatcher(FD, &FCallback); + + doc::ClangDocCallback MCallback(Reporter, "method"); + DeclarationMatcher MD = cxxMethodDecl().bind("method"); + Finder->addMatcher(MD, &MCallback); tooling::ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); @@ -62,17 +110,20 @@ Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster( "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN)); - doc::ClangDocActionFactory Factory(Context, Reporter); + doc::ClangDocAction ClangDocAction(std::move(Finder), Reporter); outs() << "Parsing codebase...\n"; - int Status = Tool.run(&Factory); + int Status = + Tool.run(tooling::newFrontendActionFactory(&ClangDocAction).get()); if (Status) return Status; + // TODO: Serialize each record as we see it, rather than at the end, followed + // by a reduce on the records to combine multiple declarations. + outs() << "Writing intermediate docs...\n"; if (DumpResult) - Reporter.serialize(EmitFormat, outs()); - - outs() << "Writing docs...\n"; - Reporter.serialize(EmitFormat, outs()); + Reporter->serialize(EmitFormat, ""); + else + Reporter->serialize(EmitFormat, IRFilePath); return 0; }