Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -7,6 +7,7 @@ endif() add_subdirectory(change-namespace) +add_subdirectory(clang-doc) add_subdirectory(clang-query) add_subdirectory(clang-move) add_subdirectory(clangd) Index: clang-doc/CMakeLists.txt =================================================================== --- /dev/null +++ clang-doc/CMakeLists.txt @@ -0,0 +1,22 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +add_clang_library(clangDoc + ClangDoc.cpp + ClangDocMapper.cpp + ClangDocBinary.cpp + + LINK_LIBS + clangAnalysis + clangAST + clangASTMatchers + clangBasic + clangFormat + clangFrontend + clangLex + clangTooling + clangToolingCore + ) + +add_subdirectory(tool) Index: clang-doc/ClangDoc.h =================================================================== --- /dev/null +++ clang-doc/ClangDoc.h @@ -0,0 +1,57 @@ +//===-- 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 "ClangDocMapper.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Comment.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/Tooling.h" +#include +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace doc { + +/// Callback class for matchers. +/// Parses each match and sends it along to the reporter for serialization. +class ClangDocCallback : public MatchFinder::MatchCallback { +public: + ClangDocCallback(StringRef BoundName, ExecutionContext &ECtx, bool OmitFiles) : ECtx(ECtx), Mapper(OmitFiles), BoundName(BoundName) {} + virtual void run(const MatchFinder::MatchResult &Result) final; + +private: + template + void processMatchedDecl(const T *D); + int getLine(const NamedDecl *D) const; + StringRef getFile(const NamedDecl *D) const; + comments::FullComment *getComment(const NamedDecl *D); + std::string getName(const NamedDecl *D) const; + + ASTContext *Context; + ExecutionContext &ECtx; + ClangDocMapper Mapper; + StringRef BoundName; +}; + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANGDOC_H Index: clang-doc/ClangDoc.cpp =================================================================== --- /dev/null +++ clang-doc/ClangDoc.cpp @@ -0,0 +1,82 @@ +//===-- 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/AST.h" +#include "clang/AST/Comment.h" +#include "clang/AST/Mangle.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 { + +template +void ClangDocCallback::processMatchedDecl(const T *D) { + if (!Context->getSourceManager().isWrittenInMainFile(D->getLocation())) + return; + std::string Name = getName(D); + ECtx.reportResult(Name, Mapper.emitInfo(D, getComment(D), Name, getLine(D), getFile(D))); +} + +void ClangDocCallback::run(const MatchFinder::MatchResult &Result) { + Context = Result.Context; + if (const auto *M = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(M); + else if (const auto *M = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(M); + else if (const auto *M = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(M); + else if (const auto *M = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(M); + else if (const auto *M = Result.Nodes.getNodeAs(BoundName)) + processMatchedDecl(M); +} + +comments::FullComment *ClangDocCallback::getComment(const NamedDecl *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 ClangDocCallback::getLine(const NamedDecl *D) const { + return Context->getSourceManager().getPresumedLoc(D->getLocStart()).getLine(); +} + +StringRef ClangDocCallback::getFile(const NamedDecl *D) const { + return Context->getSourceManager().getPresumedLoc(D->getLocStart()).getFilename(); +} + +std::string ClangDocCallback::getName(const NamedDecl *D) const { + if (const auto *F = dyn_cast(D)) { + MangleContext *MC = Context->createMangleContext(); + std::string S; + llvm::raw_string_ostream MangledName(S); + if (const auto *Ctor = dyn_cast(F)) + MC->mangleCXXCtor(Ctor, CXXCtorType::Ctor_Complete, MangledName); + else if (const auto *Dtor = dyn_cast(F)) + MC->mangleCXXDtor(Dtor, CXXDtorType::Dtor_Complete, MangledName); + else + MC->mangleName(F, MangledName); + return S; + } + return D->getQualifiedNameAsString(); +} + +} // namespace doc +} // namespace clang Index: clang-doc/ClangDocBinary.h =================================================================== --- /dev/null +++ clang-doc/ClangDocBinary.h @@ -0,0 +1,73 @@ +//===-- ClangDocBinGem.h - ClangDoc Binary Generator -----------*- 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_BIN_GEN_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BIN_GEN_H + +#include "ClangDocMapper.h" +#include "clang/AST/AST.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include + +using namespace llvm; + +namespace clang { +namespace doc { + +class ClangDocBinaryWriter { +public: + ClangDocBinaryWriter(BitstreamWriter Stream, bool OmitFilenames) : Stream(Stream), OmitFilenames(OmitFilenames) {}; + + using RecordData = SmallVector; + using RecordDataImpl = SmallVectorImpl; + + template + void writeBitstream(const T &I); + +private: + void addString(StringRef Str, RecordDataImpl &Record); + void addComment(const CommentInfo &I, RecordDataImpl &Record); + void addNamedType(const NamedType &I, RecordDataImpl &Record); + void addNamedTypeVector(const SmallVector &Vec, RecordDataImpl &Record); + void addStringVector(const SmallVector &Vec, RecordDataImpl &Record); + void addInfo(const Info &I, RecordDataImpl &Record); + void addSymbolInfo(const SymbolInfo &I, RecordDataImpl &Record); + + BitstreamWriter &Stream; + bool OmitFilenames; +}; + +// TODO: Implement, and use for testing. +class ClangDocBinaryReader { +public: + ClangDocBinaryReader(BitstreamWriter Stream, bool OmitFilenames) : Stream(Stream), OmitFilenames(OmitFilenames) {}; + + using RecordData = SmallVector; + using RecordDataImpl = SmallVectorImpl; + + std::string readBitstream(); + +private: + std::string readString(const RecordData &Record, unsigned &Idx); + std::string readComment(const RecordDataImpl &Record, unsigned &Idx); + std::string readNamedType(const RecordDataImpl &Record, unsigned &Idx); + std::string readNamedTypeVector(const RecordDataImpl &Record, unsigned &Idx); + std::string readStringVector(const RecordDataImpl &Record, unsigned &Idx); + std::string readInfo(const RecordDataImpl &Record, unsigned &Idx, Info &I); + std::string readSymbolInfo(const RecordDataImpl &Record, unsigned &Idx); + + BitstreamWriter &Stream; + bool OmitFilenames; +}; + +} +} + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BIN_GEN_H Index: clang-doc/ClangDocBinary.cpp =================================================================== --- /dev/null +++ clang-doc/ClangDocBinary.cpp @@ -0,0 +1,113 @@ +//===-- ClangDocBinGem.cpp - ClangDoc Binary Generator ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangDocBinary.h" + +using namespace llvm; + +namespace clang { +namespace doc { + +enum InfoType {IT_nondef = 0, IT_namespace = 1, IT_function = 2, IT_record = 3, IT_enum = 4}; + +void ClangDocBinaryWriter::addString(StringRef Str, RecordDataImpl &Record) { + Record.push_back(Str.size()); + Record.insert(Record.end(), Str.begin(), Str.end()); + } + + // TODO: implement. + void ClangDocBinaryWriter::addComment(const CommentInfo &I, RecordDataImpl &Record) {} + +void ClangDocBinaryWriter::addNamedType(const NamedType &I, RecordDataImpl &Record) { + addString(I.Type, Record); + addString(I.Name, Record); + Record.push_back(I.Access); + addComment(I.Description, Record); +} + + void ClangDocBinaryWriter::addNamedTypeVector(const SmallVector &Vec, RecordDataImpl &Record) { + Record.push_back(static_cast(Vec.size())); + for (const auto N : Vec) + addNamedType(N, Record); + } + + void ClangDocBinaryWriter::addStringVector(const SmallVector &Vec, RecordDataImpl &Record) { + Record.push_back(static_cast(Vec.size())); + for (const auto S : Vec) + addString(S, Record); + } + + void ClangDocBinaryWriter::addInfo(const Info &I, RecordDataImpl &Record) { + addString(I.FullyQualifiedName, Record); + addString(I.SimpleName, Record); + addString(I.Namespace, Record); + addComment(I.Description, Record); +} + +void ClangDocBinaryWriter::addSymbolInfo(const SymbolInfo &I, RecordDataImpl &Record) { + addInfo(I, Record); + Record.push_back(I.LineNumber); + addString(I.Filename, Record); +} + +template <> void ClangDocBinaryWriter::writeBitstream(const NamespaceInfo &I) { + RecordData Record; + Record.clear(); + addInfo(I, Record); + Stream.EmitRecord(IT_namespace, Record); +} + +template <> void ClangDocBinaryWriter::writeBitstream(const FunctionInfo &I) { + RecordData Record; + Record.clear(); + addSymbolInfo(I, Record); + addString(I.MangledName, Record); + addString(I.Parent, Record); + addNamedType(I.ReturnType, Record); + addNamedTypeVector(I.Params, Record); + Record.push_back(I.Access); + Stream.EmitRecord(IT_function, Record); +} + +template <> void ClangDocBinaryWriter::writeBitstream(const RecordInfo &I) { + RecordData Record; + Record.clear(); + addSymbolInfo(I, Record); + Record.push_back(I.LineNumber); + addNamedTypeVector(I.Members, Record); + addStringVector(I.Parents, Record); + addStringVector(I.VirtualParents, Record); + Stream.EmitRecord(IT_record, Record); +} + +template <> void ClangDocBinaryWriter::writeBitstream(const EnumInfo &I) { + RecordData Record; + Record.clear(); + addSymbolInfo(I, Record); + Record.push_back(I.Scoped); + addNamedTypeVector(I.Members, Record); + Stream.EmitRecord(IT_enum, Record); +} + +template <> void ClangDocBinaryWriter::writeBitstream(const NonDefInfo &I) { + RecordData Record; + Record.clear(); + addSymbolInfo(I, Record); + Stream.EmitRecord(IT_nondef, Record); +} + +std::string ClangDocBinaryReader::readString(const RecordData &Record, unsigned &Idx) { + unsigned Len = Record[Idx++]; + std::string Result(Record.data() + Idx, Record.data() + Idx + Len); + Idx += Len; + return Result; +} + +} // namespace clang +} // namespace doc \ No newline at end of file Index: clang-doc/ClangDocMapper.h =================================================================== --- /dev/null +++ clang-doc/ClangDocMapper.h @@ -0,0 +1,161 @@ +//===-- ClangDocMapper.cpp - ClangDocMapper -----------------*- 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_MAPPER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_MAPPER_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/Basic/Specifiers.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/Execution.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +using namespace clang::comments; +using namespace clang::tooling; + +namespace clang { +namespace doc { + +/// A representation of a parsed comment. +struct CommentInfo { + StringRef Kind; + StringRef Text; + StringRef Name; + StringRef Direction; + StringRef ParamName; + StringRef CloseName; + bool SelfClosing = false; + bool Explicit = false; + llvm::SmallVector AttrKeys; + llvm::SmallVector AttrValues; + llvm::SmallVector Args; + llvm::SmallVector Position; + std::vector Children; +}; + +// TODO: Pull the CommentInfo for a parameter or member out of the record or +// function's CommentInfo. +// Info for named types (parameters, members). +struct NamedType { + std::string Type; + std::string Name; + AccessSpecifier Access = clang::AccessSpecifier::AS_none; + CommentInfo Description; +}; + +/// A base struct for Infos. +struct Info { + std::string FullyQualifiedName; + std::string SimpleName; + std::string Namespace; + CommentInfo Description; +}; + +struct NamespaceInfo : public Info {}; + +struct SymbolInfo : public Info { + int LineNumber; + StringRef Filename; +}; + +struct NonDefInfo : public SymbolInfo {}; + +// TODO: Expand to allow for documenting templating. +// Info for functions. +struct FunctionInfo : public SymbolInfo { + StringRef MangledName; + std::string Parent; + NamedType ReturnType; + llvm::SmallVector Params; + AccessSpecifier Access; +}; + +// TODO: Expand to allow for documenting templating, inheritance access, +// friend classes +// Info for types. +struct RecordInfo : public SymbolInfo { + TagTypeKind TagType; + llvm::SmallVector Members; + llvm::SmallVector Parents; + llvm::SmallVector VirtualParents; +}; + +// TODO: Expand to allow for documenting templating. +// Info for types. +struct EnumInfo : public SymbolInfo { + bool Scoped; + llvm::SmallVector Members; +}; + +// TODO: Add functionality to include separate markdown pages. + +class ClangDocCommentVisitor : public ConstCommentVisitor { +public: + ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {} + + CommentInfo parseComment(const comments::Comment *C); + + 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); + +private: + StringRef getCommandName(unsigned CommandID) const; + bool isWhitespaceOnly(StringRef S) const; + + CommentInfo CurrentCI; +}; + +class ClangDocMapper { +public: + ClangDocMapper(bool OmitFilenames) : OmitFilenames(OmitFilenames) {} + + template + StringRef emitInfo(const C *D, const FullComment *FC, StringRef Key, int LineNumber, StringRef File); + +private: + template + StringRef serialize(T &I) const; + void populateSymbolInfo(SymbolInfo &I, StringRef Name, StringRef SimpleName, + StringRef Namespace, const FullComment *C, int LineNumber, StringRef File); + void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, StringRef Name, const FullComment *C, + int LineNumber, StringRef File); + template + StringRef serializeNonDefInfo(const C *D,StringRef Name, const FullComment *FC, int LineNumber, StringRef File); + void parseFields(RecordInfo &I, const RecordDecl *D) const; + void parseEnumerators(EnumInfo &I, const EnumDecl *D) const; + void parseBases(RecordInfo &I, const CXXRecordDecl *D) const; + void parseParameters(FunctionInfo &I, const FunctionDecl *D) const; + CommentInfo parseFullComment(const FullComment *C); + StringRef getParentNamespace(const DeclContext *D) const; + + bool OmitFilenames; +}; + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_MAPPER_H Index: clang-doc/ClangDocMapper.cpp =================================================================== --- /dev/null +++ clang-doc/ClangDocMapper.cpp @@ -0,0 +1,270 @@ +//===-- ClangDocMapper.cpp - ClangDoc Mapper ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClangDocMapper.h" +#include "ClangDocBinary.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 { + +// ClangDocMapper + +template <> StringRef ClangDocMapper::emitInfo(const NamespaceDecl *D, + const FullComment *FC, StringRef Name, int LineNumber, StringRef File) { + NamespaceInfo I; + I.FullyQualifiedName = Name; + I.SimpleName = D->getNameAsString(); + I.Namespace = getParentNamespace(D); + if (FC) I.Description = parseFullComment(FC); + return serialize(I); +} + +template <> StringRef ClangDocMapper::emitInfo(const RecordDecl *D, + const FullComment *FC, StringRef Name, int LineNumber, StringRef File) { + if (!D->isThisDeclarationADefinition()) + return serializeNonDefInfo(D, Name, FC, LineNumber, File); + RecordInfo I; + populateSymbolInfo(I, Name, D->getNameAsString(), getParentNamespace(D), FC, LineNumber, File); + I.TagType = D->getTagKind(); + if (const auto *CXXR = dyn_cast(D)) + parseBases(I, CXXR); + parseFields(I, D); + return serialize(I); +} + +template <> StringRef ClangDocMapper::emitInfo(const FunctionDecl *D, + const FullComment *FC, StringRef Name, int LineNumber, StringRef File) { + if (!D->isThisDeclarationADefinition()) + return serializeNonDefInfo(D, Name, FC, LineNumber, File); + FunctionInfo I; + populateFunctionInfo(I, D, Name, FC, LineNumber, File); + I.Access = clang::AccessSpecifier::AS_none; + return serialize(I); +} + +template <> StringRef ClangDocMapper::emitInfo(const CXXMethodDecl *D, + const FullComment *FC, StringRef Name, int LineNumber, StringRef File) { + if (!D->isThisDeclarationADefinition()) + return serializeNonDefInfo(D, Name, FC, LineNumber, File); + FunctionInfo I; + populateFunctionInfo(I, D, Name, FC, LineNumber, File); + I.Parent = D->getParent()->getQualifiedNameAsString(); + I.Access = D->getAccess(); + return serialize(I); +} + +template <> StringRef ClangDocMapper::emitInfo(const EnumDecl *D, + const FullComment *FC, StringRef Name, int LineNumber, StringRef File) { + if (!D->isThisDeclarationADefinition()) + return serializeNonDefInfo(D, Name, FC, LineNumber, File); + EnumInfo I; + populateSymbolInfo(I, Name, D->getNameAsString(), getParentNamespace(D), FC, LineNumber, File); + I.Scoped = D->isScoped(); + parseEnumerators(I, D); + return serialize(I); +} + +template +StringRef ClangDocMapper::serialize(T &I) const { + SmallString<128> Buffer; + llvm::BitstreamWriter Stream(Buffer); + ClangDocBinaryWriter Writer(Stream, OmitFilenames); + Writer.writeBitstream(I); + return Buffer; +} + +CommentInfo ClangDocMapper::parseFullComment(const FullComment *C) { + CommentInfo CI; + ClangDocCommentVisitor Visitor(CI); + return Visitor.parseComment(C); +} + +void ClangDocMapper::populateSymbolInfo(SymbolInfo &I, StringRef Name, + StringRef SimpleName, + StringRef Namespace, const FullComment *C, + int LineNumber, StringRef File) { + I.FullyQualifiedName = Name; + I.SimpleName = SimpleName; + I.Namespace = Namespace; + I.LineNumber = LineNumber; + I.Filename = File; + if (C) I.Description = parseFullComment(C); +} + +void ClangDocMapper::populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, StringRef Name, const FullComment *C, + int LineNumber, StringRef File) { + populateSymbolInfo(I, D->getQualifiedNameAsString(), D->getNameAsString(), getParentNamespace(D), C, LineNumber, File); + I.MangledName = Name; + NamedType N; + N.Type = D->getReturnType().getAsString(); + I.ReturnType = N; + parseParameters(I, D); +} + +template +StringRef ClangDocMapper::serializeNonDefInfo(const C *D, StringRef Name, const FullComment *FC, + int LineNumber, StringRef File) { + NonDefInfo I; + I.FullyQualifiedName = Name; + I.SimpleName = D->getNameAsString(); + I.Namespace = getParentNamespace(D); + I.LineNumber = LineNumber; + I.Filename = File; + if (FC) I.Description = parseFullComment(FC); + return serialize(I); +} + + +void ClangDocMapper::parseFields(RecordInfo &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. + I.Members.emplace_back(N); + } +} + +void ClangDocMapper::parseEnumerators(EnumInfo &I, const EnumDecl *D) const { + for (const EnumConstantDecl *E : D->enumerators()) { + NamedType N; + N.Type = E->getQualifiedNameAsString(); + // FIXME: Set Access to the appropriate value. + I.Members.emplace_back(N); + } +} + +void ClangDocMapper::parseParameters(FunctionInfo &I, + const FunctionDecl *D) const { + for (const ParmVarDecl *P : D->parameters()){ + NamedType N; + N.Type = P->getOriginalType().getAsString(); + N.Name = P->getQualifiedNameAsString(); + // FIXME: Set Access to the appropriate value. + I.Params.emplace_back(N); + } +} + +void ClangDocMapper::parseBases(RecordInfo &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()); +} + +StringRef ClangDocMapper::getParentNamespace(const DeclContext *D) const { + if (const auto *N = dyn_cast(D->getParent())) + return N->getQualifiedNameAsString(); + return ""; +} + +// ClangDocCommentVisitor + +CommentInfo ClangDocCommentVisitor::parseComment(const comments::Comment *C) { + CurrentCI.Kind = C->getCommentKindName(); + ConstCommentVisitor::visit(C); + for (comments::Comment *Child : make_range(C->child_begin(), C->child_end())) { + CommentInfo ChildCI; + ClangDocCommentVisitor Visitor(ChildCI); + CurrentCI.Children.emplace_back(Visitor.parseComment(Child)); + } + return CurrentCI; +} + +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.AttrKeys.push_back(Attr.Name); + CurrentCI.AttrValues.push_back(Attr.Value); + } +} + +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(std::to_string(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: clang-doc/tool/CMakeLists.txt =================================================================== --- /dev/null +++ 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: clang-doc/tool/ClangDocMain.cpp =================================================================== --- /dev/null +++ clang-doc/tool/ClangDocMain.cpp @@ -0,0 +1,88 @@ +//===-- 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/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/Execution.h" +#include "clang/Tooling/StandaloneExecution.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::tooling; +using namespace clang; +using namespace llvm; + +namespace { + +static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); +static cl::OptionCategory ClangDocCategory("clang-doc options"); + +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]); + + auto Exec = clang::tooling::createExecutorFromCommandLineArgs( + argc, argv, ClangDocCategory); + + if (!Exec) { + errs() << toString(Exec.takeError()) << "\n"; + return 1; + } + + MatchFinder Finder; + ExecutionContext *ECtx = Exec->get()->getExecutionContext(); + + doc::ClangDocCallback NCallback("namespace", *ECtx, OmitFilenames); + Finder.addMatcher(namespaceDecl().bind("namespace"), &NCallback); + doc::ClangDocCallback RCallback("record", *ECtx, OmitFilenames); + Finder.addMatcher(recordDecl().bind("record"), &RCallback); + doc::ClangDocCallback ECallback("enum", *ECtx, OmitFilenames); + Finder.addMatcher(enumDecl().bind("enum"), &ECallback); + doc::ClangDocCallback MCallback("method", *ECtx, OmitFilenames); + Finder.addMatcher(cxxMethodDecl(isUserProvided()).bind("method"), &MCallback); + doc::ClangDocCallback FCallback("function", *ECtx, OmitFilenames); + Finder.addMatcher(functionDecl(unless(cxxMethodDecl())).bind("function"), &FCallback); + + ArgumentsAdjuster ArgAdjuster; + if (!DoxygenOnly) + ArgAdjuster = combineAdjusters(getInsertArgumentAdjuster("-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN), ArgAdjuster); + auto Err = Exec->get()->execute(newFrontendActionFactory(&Finder), ArgAdjuster); + if (Err) + errs() << toString(std::move(Err)) << "\n"; + + if (DumpResult) + Exec->get()->getToolResults()->forEachResult( + [](StringRef Key, StringRef Value) { + errs() << Key.str() << " -----\n" << Value.str() << "\n"; + }); + + return 0; +} Index: docs/clang-doc.rst =================================================================== --- /dev/null +++ docs/clang-doc.rst @@ -0,0 +1,13 @@ +=================== +Clang-Doc +=================== + +.. contents:: + +Intro + +Setup +===== + +Use +============