Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -54,6 +54,7 @@ clang-rename clang-refactor clang-diff + clang-doc ) if(CLANG_ENABLE_STATIC_ANALYZER) Index: test/Tooling/clang-doc-basic.cpp =================================================================== --- /dev/null +++ test/Tooling/clang-doc-basic.cpp @@ -0,0 +1,88 @@ +// 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: cp "%s" "%t/test.cpp" +// RUN: clang-doc --dump --omit-filenames -p %t %t/test.cpp | FileCheck %s + +/// Test namespace +namespace A { + +/// A function +void f(int x) {} + +} // end namespace A + +/// A C++ class +class C { +private: + int i; +}; + + // CHECK: --- + // CHECK: Qualified Name: '' + // CHECK: Name: '' + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: ... + // 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 =================================================================== --- /dev/null +++ test/Tooling/clang-doc-namespace.cpp @@ -0,0 +1,84 @@ +// 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: cp "%s" "%t/test.cpp" +// RUN: clang-doc --dump --omit-filenames -p %t %t/test.cpp | FileCheck %s + +/// Test namespace +namespace A {} + +namespace B { +namespace C { +namespace D {} +} +} + +namespace A { + void f() {}; +} + + // CHECK: --- + // CHECK: Qualified Name: '' + // CHECK: Name: '' + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: ... + // 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: - 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 =================================================================== --- /dev/null +++ test/Tooling/clang-doc-type.cpp @@ -0,0 +1,235 @@ +// 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: cp "%s" "%t/test.cpp" +// RUN: clang-doc --dump --omit-filenames -p %t %t/test.cpp | FileCheck %s + +/// A union. +union A { int X; int Y; }; + +/// An enum. +enum B { X, Y }; + +/// A struct. +struct C { int i; }; + +/// An empty class. +class D {}; + +/// An example class. +class E { +public: + E() {} + ~E() {} +protected: + void ProtectedMethod(); +}; + +void E::ProtectedMethod() {} + +/// An inherited class. +class F : public D, public E {}; +class G : virtual private D, public E {}; + + // CHECK: --- + // CHECK: Qualified Name: '' + // CHECK: Name: '' + // CHECK: Namespace: '' + // CHECK: Descriptions: + // CHECK: Locations: + // CHECK: ... + // 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: ' 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: 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: 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: 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: 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: 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: test/lit.cfg.py =================================================================== --- test/lit.cfg.py +++ test/lit.cfg.py @@ -57,9 +57,9 @@ tool_dirs = [config.clang_tools_dir, config.llvm_tools_dir] tools = [ - 'c-index-test', 'clang-check', 'clang-diff', 'clang-format', 'opt', - ToolSubst('%clang_func_map', command=FindTool( - 'clang-func-mapping'), unresolved='ignore'), + 'c-index-test', 'clang-check', 'clang-diff', 'clang-doc', 'clang-format', + 'opt', ToolSubst('%clang_func_map', command=FindTool( + 'clang-func-mapping'), unresolved='ignore'), ] if config.clang_examples: Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_subdirectory(diagtool) add_clang_subdirectory(driver) add_clang_subdirectory(clang-diff) +add_clang_subdirectory(clang-doc) add_clang_subdirectory(clang-format) add_clang_subdirectory(clang-format-vs) add_clang_subdirectory(clang-fuzzer) Index: tools/clang-doc/CMakeLists.txt =================================================================== --- /dev/null +++ tools/clang-doc/CMakeLists.txt @@ -0,0 +1,21 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +add_clang_library(clangDoc + ClangDoc.cpp + ClangDocReporter.cpp + + LINK_LIBS + clangAnalysis + clangAST + clangASTMatchers + clangBasic + clangFormat + clangFrontend + clangLex + clangTooling + clangToolingCore + ) + +add_subdirectory(tool) Index: tools/clang-doc/ClangDoc.h =================================================================== --- /dev/null +++ tools/clang-doc/ClangDoc.h @@ -0,0 +1,97 @@ +//===-- ClangDoc.cpp - ClangDoc ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H + +#include "ClangDocReporter.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/Tooling.h" +#include +#include + +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 { +public: + explicit ClangDocVisitor(ASTContext *Ctx, ClangDocReporter &Reporter) + : Context(Ctx), Manager(Ctx->getSourceManager()), + MC(Ctx->createMangleContext()), Reporter(Reporter) {} + + bool VisitTagDecl(const TagDecl *D); + bool VisitNamespaceDecl(const NamespaceDecl *D); + bool VisitFunctionDecl(const FunctionDecl *D); + void parseUnattachedComments(); + +private: + 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); + + ASTContext *Context; + SourceManager &Manager; + MangleContext *MC; + ClangDocReporter &Reporter; +}; + +class ClangDocConsumer : public clang::ASTConsumer { +public: + explicit ClangDocConsumer(ASTContext *Ctx, ClangDocReporter &Reporter) + : Visitor(Ctx, Reporter), Reporter(Reporter) {} + + virtual void HandleTranslationUnit(clang::ASTContext &Context); + +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); + +private: + ClangDocReporter &Reporter; +}; + +class ClangDocActionFactory : public tooling::FrontendActionFactory { +public: + ClangDocActionFactory(ClangDocContext &Ctx, ClangDocReporter &Reporter) + : Context(Ctx), Reporter(Reporter) {} + + clang::FrontendAction *create() override { + return new ClangDocAction(Reporter); + } + +private: + ClangDocContext &Context; + ClangDocReporter &Reporter; +}; + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H Index: tools/clang-doc/ClangDoc.cpp =================================================================== --- /dev/null +++ tools/clang-doc/ClangDoc.cpp @@ -0,0 +1,127 @@ +//===-- ClangDoc.cpp - ClangDoc ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangDoc.h" +#include "clang/AST/Comment.h" +#include "clang/AST/Mangle.h" +#include "clang/Frontend/CompilerInstance.h" + +using namespace clang; +using namespace clang::tooling; +using namespace llvm; + +namespace clang { +namespace doc { + +bool ClangDocVisitor::VisitTagDecl(const TagDecl *D) { + 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; +} + +bool ClangDocVisitor::VisitNamespaceDecl(const NamespaceDecl *D) { + if (!isUnparsed(D->getLocation())) + return true; + Reporter.createNamespaceInfo(D, getComment(D), getLine(D), getFile(D)); + return true; +} + +bool ClangDocVisitor::VisitFunctionDecl(const FunctionDecl *D) { + 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; +} + +comments::FullComment *ClangDocVisitor::getComment(const Decl *D) { + RawComment *Comment = Context->getRawCommentForDeclNoCache(D); + + // FIXME: Move setAttached to the initial comment parsing. + if (Comment) { + Comment->setAttached(); + return Comment->parse(*Context, nullptr, D); + } + return nullptr; +} + +int ClangDocVisitor::getLine(const Decl *D) const { + PresumedLoc PLoc = Manager.getPresumedLoc(D->getLocStart()); + return PLoc.getLine(); +} + +std::string ClangDocVisitor::getFile(const Decl *D) const { + PresumedLoc PLoc = Manager.getPresumedLoc(D->getLocStart()); + return PLoc.getFilename(); +} + +void ClangDocVisitor::parseUnattachedComments() { + for (RawComment *Comment : Context->getRawCommentList().getComments()) { + if (!isUnparsed(Comment->getLocStart()) || Comment->isAttached()) + continue; + std::shared_ptr CI = std::make_shared(); + Reporter.parseFullComment(Comment->parse(*Context, nullptr, nullptr), CI); + Reporter.addUnattachedComment(Manager.getFilename(Comment->getLocStart()), + std::move(CI)); + } +} + +bool ClangDocVisitor::isUnparsed(SourceLocation Loc) const { + if (!Loc.isValid()) + return false; + const std::string &Filename = Manager.getFilename(Loc); + + if (!Reporter.hasFile(Filename)) + return false; + if (Manager.isInSystemHeader(Loc) || Manager.isInExternCSystemHeader(Loc)) + return false; + return true; +} + +std::string ClangDocVisitor::mangleName(const FunctionDecl *D) { + std::string S; + llvm::raw_string_ostream MangledName(S); + if (const auto *Ctor = dyn_cast(D)) + MC->mangleCXXCtor(Ctor, CXXCtorType::Ctor_Complete, MangledName); + else if (const auto *Dtor = dyn_cast(D)) + 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); +} + +} // namespace doc +} // namespace clang Index: tools/clang-doc/ClangDocReporter.h =================================================================== --- /dev/null +++ tools/clang-doc/ClangDocReporter.h @@ -0,0 +1,197 @@ +//===-- Doc.cpp - ClangDoc --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPORTER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPORTER_H + +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/CommentVisitor.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include + +using namespace clang::comments; + +namespace clang { +namespace doc { + +enum class OutFormat { YAML, LLVM }; + +// Info for named types (parameters, members). +struct NamedType { + std::string Type; + std::string Name; + AccessSpecifier Access; +}; + +struct Location { + int LineNumber; + std::string Filename; +}; + +/// A representation of a parsed comment. +struct CommentInfo { + std::string Kind; + std::string Text; + std::string Name; + std::string Direction; + std::string ParamName; + std::string CloseName; + bool SelfClosing = false; + bool Explicit = false; + llvm::SmallVector Attrs; + llvm::SmallVector Args; + llvm::SmallVector Position; + std::vector> Children; +}; + +/// A source file in the project. +struct File { + std::string Filename; + std::shared_ptr Description; + llvm::SmallVector, 8> UnattachedComments; +}; + +/// A base struct for Infos. +struct Info { + bool isDefined = false; + std::string FullyQualifiedName; + std::string SimpleName; + std::string Namespace; + std::vector> Descriptions; + llvm::SmallVector Locations; +}; + +// TODO: Expand to allow for documenting templating. +// Info for functions. +struct FunctionInfo : public Info { + std::string MangledName; + std::string DefinitionFile; + std::string ReturnType; + llvm::SmallVector Params; + AccessSpecifier Access; +}; + +struct NamespaceInfo : public Info { + llvm::StringMap> Functions; +}; + +// TODO: Expand to allow for documenting templating, inheritance access, +// friend classes +// Info for types. +struct TypeInfo : public Info { + std::string DefinitionFile; + TagTypeKind TagType; + llvm::SmallVector Members; + llvm::SmallVector Parents; + llvm::SmallVector VirtualParents; + llvm::StringMap> Functions; +}; + +// TODO: Expand to allow for documenting templating. +// Info for types. +struct EnumInfo : public Info { + std::string DefinitionFile; + bool Scoped; + llvm::SmallVector Members; +}; + +/// 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; + // TODO: Add functionality to include separate markdown pages. +}; + +class ClangDocReporter : public ConstCommentVisitor { +public: + ClangDocReporter() {} + ClangDocReporter(const std::vector &Sources, bool OmitFilenames); + + void addUnattachedComment(StringRef Filename, + std::shared_ptr CI); + void addFile(StringRef Filename); + + 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, std::shared_ptr &CI); + void visitTextComment(const TextComment *C); + void visitInlineCommandComment(const InlineCommandComment *C); + void visitHTMLStartTagComment(const HTMLStartTagComment *C); + void visitHTMLEndTagComment(const HTMLEndTagComment *C); + void visitBlockCommandComment(const BlockCommandComment *C); + void visitParamCommandComment(const ParamCommandComment *C); + void visitTParamCommandComment(const TParamCommandComment *C); + void visitVerbatimBlockComment(const VerbatimBlockComment *C); + void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); + void visitVerbatimLineComment(const VerbatimLineComment *C); + + bool hasFile(StringRef Filename) const; + void serialize(clang::doc::OutFormat Format, StringRef RootDir); + +private: + 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(std::shared_ptr &CI, + const comments::Comment *C); + 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; + + template void printMap(raw_ostream &OS, llvm::StringMap &Map); + template + void printMapPlusFunctions(raw_ostream &OS, llvm::StringMap &Map); + void serializeYAML(StringRef RootDir); + void serializeLLVM(StringRef RootDir); + + const char *getCommandName(unsigned CommandID) const; + bool isWhitespaceOnly(StringRef S) const; + std::string getParentNamespace(const DeclContext *D) const; + + std::shared_ptr CurrentCI; + Documentation Docs; +}; + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPORTER_H Index: tools/clang-doc/ClangDocReporter.cpp =================================================================== --- /dev/null +++ tools/clang-doc/ClangDocReporter.cpp @@ -0,0 +1,444 @@ +//===-- ClangDocReporter.cpp - ClangDoc Reporter ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangDocReporter.h" +#include "ClangDocYAML.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using clang::comments::FullComment; + +namespace clang { +namespace doc { + +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 + std::unique_ptr I = make_unique(); + I->isDefined = true; + Docs.Namespaces[""] = std::move(I); + Docs.OmitFilenames = OF; +} + +void ClangDocReporter::addUnattachedComment(StringRef Filename, + std::shared_ptr CI) { + Docs.Files[Filename]->UnattachedComments.push_back(std::move(CI)); +} + +void ClangDocReporter::addFile(StringRef Filename) { + std::unique_ptr F = make_unique(); + F->Filename = Filename; + Docs.Files.insert(std::make_pair(Filename, std::move(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()) { + std::unique_ptr I = make_unique(); + Docs.Namespaces[Name] = std::move(I); + populateBasicInfo(*Docs.Namespaces[Name], Name, D->getNameAsString(), + getParentNamespace(D)); + } + + addLocation(*Docs.Namespaces[Name], LineNumber, File); + addComment(*Docs.Namespaces[Name], C); +} + +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()) { + std::unique_ptr I = make_unique(); + Docs.Types[Name] = std::move(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()) { + std::unique_ptr I = make_unique(); + Docs.Enums[Name] = std::move(I); + } + + if (D->isThisDeclarationADefinition()) + populateEnumInfo(*Docs.Enums[Name], D, Name, File); + + addLocation(*Docs.Enums[Name], LineNumber, File); + addComment(*Docs.Enums[Name], C); +} + +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()) { + std::unique_ptr NI = make_unique(); + NI->FullyQualifiedName = Namespace; + Docs.Namespaces[Namespace] = std::move(NI); + } + + llvm::StringMapIterator> Pair = + Docs.Namespaces[Namespace]->Functions.find(MangledName); + if (Pair == Docs.Namespaces[Namespace]->Functions.end()) { + std::unique_ptr I = make_unique(); + Docs.Namespaces[Namespace]->Functions[MangledName] = std::move(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); +} + +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()) { + std::unique_ptr TI = make_unique(); + TI->FullyQualifiedName = getParentNamespace(D); + Docs.Types[ParentName] = std::move(TI); + } + + llvm::StringMapIterator> Pair = + Docs.Types[ParentName]->Functions.find(MangledName); + if (Pair == Docs.Types[ParentName]->Functions.end()) { + std::unique_ptr I = make_unique(); + Docs.Types[ParentName]->Functions[MangledName] = std::move(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); +} + +void ClangDocReporter::parseFullComment(const FullComment *C, + std::shared_ptr &CI) { + parseComment(CI, C); +} + +void ClangDocReporter::visitTextComment(const TextComment *C) { + if (!isWhitespaceOnly(C->getText())) + CurrentCI->Text = C->getText(); +} + +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)); +} + +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); + } +} + +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)); +} + +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(); +} + +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)); + } +} + +void ClangDocReporter::visitVerbatimBlockComment( + const VerbatimBlockComment *C) { + CurrentCI->Name = getCommandName(C->getCommandID()); + CurrentCI->CloseName = C->getCloseName(); +} + +void ClangDocReporter::visitVerbatimBlockLineComment( + const VerbatimBlockLineComment *C) { + if (!isWhitespaceOnly(C->getText())) + CurrentCI->Text = C->getText(); +} + +void ClangDocReporter::visitVerbatimLineComment(const VerbatimLineComment *C) { + if (!isWhitespaceOnly(C->getText())) + CurrentCI->Text = C->getText(); +} + +bool ClangDocReporter::hasFile(StringRef Filename) const { + return Docs.Files.find(Filename) != Docs.Files.end(); +} + +void ClangDocReporter::serialize(OutFormat Format, StringRef RootDir) { + Format == clang::doc::OutFormat::LLVM ? serializeLLVM(RootDir) + : serializeYAML(RootDir); +} + +void ClangDocReporter::addComment(Info &I, const FullComment *C) { + if (!C) + return; + std::shared_ptr CI = std::make_shared(); + parseFullComment(C, CI); + I.Descriptions.push_back(std::move(CI)); +} + +void ClangDocReporter::addLocation(Info &I, int LineNumber, + StringRef File) const { + Location L; + L.LineNumber = LineNumber; + if (!Docs.OmitFilenames) + L.Filename = File; + I.Locations.push_back(L); +} + +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(I, D); +} + +// TODO: finish converting comment info to unique_ptr +void ClangDocReporter::parseComment(std::shared_ptr &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())) { + std::shared_ptr ChildCI = std::make_shared(); + parseComment(ChildCI, Child); + CI->Children.push_back(std::move(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(); + // FIXME: Set Access to the appropriate value. + N.Access = clang::AccessSpecifier::AS_none; + I.Members.push_back(N); + } +} + +void ClangDocReporter::parseEnumerators(EnumInfo &I, const EnumDecl *D) const { + for (const EnumConstantDecl *E : D->enumerators()) { + NamedType N; + N.Type = E->getQualifiedNameAsString(); + N.Access = clang::AccessSpecifier::AS_none; + I.Members.push_back(N); + } +} + +void ClangDocReporter::parseParameters(FunctionInfo &I, + const FunctionDecl *D) const { + for (const ParmVarDecl *P : D->parameters()) { + NamedType Parm; + Parm.Type = P->getOriginalType().getAsString(); + Parm.Access = clang::AccessSpecifier::AS_none; + Parm.Name = P->getQualifiedNameAsString(); + I.Params.push_back(Parm); + } +} + +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()); + } + for (const CXXBaseSpecifier &B : D->vbases()) + I.VirtualParents.push_back(B.getType().getAsString()); +} + +template +void ClangDocReporter::printMap(raw_ostream &OS, StringMap &Map) { + yaml::Output YOut(OS); + for (auto &I : Map) + YOut << *(I.second); +} + +template +void ClangDocReporter::printMapPlusFunctions(raw_ostream &OS, + StringMap &Map) { + yaml::Output YOut(OS); + for (auto &I : Map) { + YOut << *(I.second); + for (auto &F : I.second->Functions) + YOut << *(F.second); + } +} + +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() << "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); +} + +void ClangDocReporter::serializeLLVM(StringRef RootDir) { + // TODO: Implement. +} + +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 ""; +} + +bool ClangDocReporter::isWhitespaceOnly(StringRef S) const { + return S.find_first_not_of(" \t\n\v\f\r") == std::string::npos || S.empty(); +} + +std::string ClangDocReporter::getParentNamespace(const DeclContext *D) const { + if (const auto *N = dyn_cast(D->getParent())) + return N->getQualifiedNameAsString(); + return ""; +} + +} // namespace doc +} // namespace clang Index: tools/clang-doc/ClangDocYAML.h =================================================================== --- /dev/null +++ tools/clang-doc/ClangDocYAML.h @@ -0,0 +1,160 @@ +//===-- ClangDocYAML.h - ClangDoc YAML -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_YAML_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_YAML_H + +#include "llvm/Support/YAMLTraits.h" + +using namespace clang::doc; + +LLVM_YAML_IS_SEQUENCE_VECTOR(std::shared_ptr) +LLVM_YAML_IS_SEQUENCE_VECTOR(NamedType) +LLVM_YAML_IS_SEQUENCE_VECTOR(Location) + +namespace llvm { +namespace yaml { + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, clang::AccessSpecifier &value) { + io.enumCase(value, "Public", clang::AccessSpecifier::AS_public); + io.enumCase(value, "Protected", clang::AccessSpecifier::AS_protected); + io.enumCase(value, "Private", clang::AccessSpecifier::AS_private); + io.enumCase(value, "None", clang::AccessSpecifier::AS_none); + } +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, clang::TagTypeKind &value) { + io.enumCase(value, "Struct", clang::TagTypeKind::TTK_Struct); + io.enumCase(value, "Interface", clang::TagTypeKind::TTK_Interface); + io.enumCase(value, "Union", clang::TagTypeKind::TTK_Union); + io.enumCase(value, "Class", clang::TagTypeKind::TTK_Class); + io.enumCase(value, "Enum", clang::TagTypeKind::TTK_Enum); + } +}; + +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, NamespaceInfo &N) { + if (!N.isDefined) + return; + 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, TypeInfo &T) { + if (!T.isDefined) + return; + 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, EnumInfo &E) { + if (!E.isDefined) + return; + 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, 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, FunctionInfo &F) { + if (!F.isDefined) + return; + 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, File &F) { + IO.mapRequired("Filename", F.Filename); + IO.mapRequired("Description", F.Description); + } +}; + +template <> struct MappingTraits> { + + static void mapping(IO &IO, std::shared_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); + } +}; + +} // end namespace yaml +} // end namespace llvm + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_YAML_H Index: tools/clang-doc/tool/CMakeLists.txt =================================================================== --- /dev/null +++ tools/clang-doc/tool/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) + +add_clang_executable(clang-doc + ClangDocMain.cpp + ) + +target_link_libraries(clang-doc + PRIVATE + clangAST + clangASTMatchers + clangBasic + clangFormat + clangFrontend + clangDoc + clangRewrite + clangTooling + clangToolingCore + ) Index: tools/clang-doc/tool/ClangDocMain.cpp =================================================================== --- /dev/null +++ tools/clang-doc/tool/ClangDocMain.cpp @@ -0,0 +1,103 @@ +//===-- ClangDocMain.cpp - Clangdoc -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangDoc.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; +using namespace llvm; + +namespace { + +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)."), + 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 + DoxygenOnly("doxygen", + cl::desc("Use only doxygen-style comments to generate docs."), + cl::init(false), cl::cat(ClangDocCategory)); + +} // namespace + +int main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(argv[0]); + tooling::CommonOptionsParser OptionsParser(argc, argv, ClangDocCategory); + std::error_code OK; + + doc::OutFormat EmitFormat; + SmallString<128> IRFilePath; + sys::path::native(OutDirectory, IRFilePath); + std::string IRFilename; + if (EmitLLVM) { + EmitFormat = doc::OutFormat::LLVM; + sys::path::append(IRFilePath, "llvm"); + IRFilename = "docs.bc"; + } else { + EmitFormat = doc::OutFormat::YAML; + sys::path::append(IRFilePath, "yaml"); + IRFilename = "docs.yaml"; + } + + std::error_code DirectoryStatus = sys::fs::create_directories(IRFilePath); + if (DirectoryStatus != OK) { + errs() << "Unable to create documentation directories.\n"; + return 1; + } + + sys::path::append(IRFilePath, IRFilename); + + doc::ClangDocReporter Reporter(OptionsParser.getSourcePathList(), + OmitFilenames); + doc::ClangDocContext Context{EmitFormat}; + + tooling::ClangTool Tool(OptionsParser.getCompilations(), + OptionsParser.getSourcePathList()); + + if (!DoxygenOnly) + Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster( + "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN)); + + doc::ClangDocActionFactory Factory(Context, Reporter); + + outs() << "Parsing codebase...\n"; + int Status = Tool.run(&Factory); + if (Status) + return Status; + + outs() << "Writing intermediate docs...\n"; + if (DumpResult) + Reporter.serialize(EmitFormat, ""); + else + Reporter.serialize(EmitFormat, IRFilePath); + return 0; +}