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,60 @@ +//===-- 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 +#include +#include "ClangDocBinary.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/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/Tooling.h" + +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, + ClangDocBinaryWriter &Writer) + : ECtx(ECtx), Mapper(Writer), 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) const; + 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,86 @@ +//===-- 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" +#include "llvm/Bitcode/BitstreamWriter.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) const { + 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 MangledName.str(); + } + return D->getQualifiedNameAsString(); +} + +} // namespace doc +} // namespace clang Index: clang-doc/ClangDocBinary.h =================================================================== --- /dev/null +++ clang-doc/ClangDocBinary.h @@ -0,0 +1,90 @@ +//===-- ClangDocBinary.h - ClangDoc Binary ---------------------*- 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_BINARY_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BINARY_H + +#include +#include "ClangDocRepresentation.h" +#include "clang/AST/AST.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitcode/BitstreamWriter.h" + +using namespace llvm; + +namespace clang { +namespace doc { + +class AbbreviationMap { + llvm::DenseMap Abbrevs; + + public: + AbbreviationMap() {} + void set(unsigned recordID, unsigned abbrevID); + unsigned get(unsigned recordID); + void clear(); +}; + +class ClangDocBinaryWriter { + public: + ClangDocBinaryWriter(bool OmitFilenames = false) + : OmitFilenames(OmitFilenames){}; + + using RecordData = SmallVector; + + template + void writeBitstream(const T &I, BitstreamWriter &Stream); + + private: + void emitBlockInfoBlock(BitstreamWriter &Stream); + void emitHeader(BitstreamWriter &Stream); + void emitStringRecord(StringRef Str, unsigned RecordId, + BitstreamWriter &Stream); + void emitLocationRecord(int LineNumber, StringRef File, unsigned RecordId, + BitstreamWriter &Stream); + void emitIntRecord(int Value, unsigned RecordId, BitstreamWriter &Stream); + void emitNamedTypeBlock(const NamedType &N, StringRef ID, + BitstreamWriter &Stream); + void emitCommentBlock(const CommentInfo *I, BitstreamWriter &Stream); + + void emitRecordID(unsigned ID, const char *Name, BitstreamWriter &Stream); + void emitBlockID(unsigned ID, const char *Name, BitstreamWriter &Stream); + void emitStringAbbrev(unsigned D, unsigned Block, BitstreamWriter &Stream); + void emitLocationAbbrev(unsigned D, unsigned Block, BitstreamWriter &Stream); + void emitIntAbbrev(unsigned D, unsigned Block, BitstreamWriter &Stream); + + RecordData Record; + bool OmitFilenames; + AbbreviationMap Abbrevs; +}; + +class ClangDocBinaryReader { + public: + ClangDocBinaryReader(raw_ostream &OS) : OS(OS) {} + + using RecordData = SmallVector; + + bool readBitstream(SmallString<2048> Bits); + + private: + enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; + bool readBlock(llvm::BitstreamCursor &Stream, unsigned ID); + Cursor skipUntilRecordOrBlock(llvm::BitstreamCursor &Stream, + unsigned &BlockOrRecordID); + + Optional BlockInfo; + std::map RecordNames; + raw_ostream &OS; +}; + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BINARY_H Index: clang-doc/ClangDocBinary.cpp =================================================================== --- /dev/null +++ clang-doc/ClangDocBinary.cpp @@ -0,0 +1,559 @@ +//===-- ClangDocBinary.cpp - ClangDoc Binary -------------------*- 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 BlockIds { + NAMESPACE_BLOCK_ID = bitc::FIRST_APPLICATION_BLOCKID, + NONDEF_BLOCK_ID, + ENUM_BLOCK_ID, + NAMED_TYPE_BLOCK_ID, + RECORD_BLOCK_ID, + FUNCTION_BLOCK_ID, + COMMENT_BLOCK_ID, + BI_FIRST = NAMESPACE_BLOCK_ID, + BI_LAST = COMMENT_BLOCK_ID +}; + +#define INFODATATYPES(X) X##_FULLY_QUALIFIED_NAME, X##_NAME, X##_NAMESPACE, + +enum DataTypes { + COMMENT_KIND = 1, + COMMENT_TEXT, + COMMENT_NAME, + COMMENT_POSITION, + COMMENT_DIRECTION, + COMMENT_PARAMNAME, + COMMENT_CLOSENAME, + COMMENT_SELFCLOSING, + COMMENT_EXPLICIT, + COMMENT_ATTRKEY, + COMMENT_ATTRVAL, + COMMENT_ARG, + NAMED_TYPE_ID, + NAMED_TYPE_TYPE, + NAMED_TYPE_NAME, + NAMED_TYPE_ACCESS, + INFODATATYPES(NAMESPACE) + INFODATATYPES(NONDEF) + NONDEF_LOCATION, + INFODATATYPES(ENUM) + ENUM_LOCATION, + ENUM_SCOPED, + ENUM_NAMED_TYPE, + ENUM_MEMBER, + INFODATATYPES(RECORD) + RECORD_LOCATION, + RECORD_TAG_TYPE, + RECORD_MEMBER, + RECORD_PARENT, + RECORD_VPARENT, + INFODATATYPES(FUNCTION) + FUNCTION_LOCATION, + FUNCTION_MANGLED_NAME, + FUNCTION_PARENT, + FUNCTION_ACCESS, + DT_FIRST = COMMENT_KIND, + DT_LAST = FUNCTION_ACCESS +}; + +#undef INFODATATYPES + +void AbbreviationMap::set(unsigned recordID, unsigned abbrevID) { + assert(Abbrevs.find(recordID) == Abbrevs.end() && + "Abbreviation already set."); + Abbrevs[recordID] = abbrevID; +} + +unsigned AbbreviationMap::get(unsigned recordID) { + assert(Abbrevs.find(recordID) != Abbrevs.end() && "Abbreviation not set."); + return Abbrevs[recordID]; +} + +void AbbreviationMap::clear() { Abbrevs.clear(); } + +void ClangDocBinaryWriter::emitHeader(BitstreamWriter &Stream) { + // Emit the file header. + Stream.Emit((unsigned)'D', 8); + Stream.Emit((unsigned)'O', 8); + Stream.Emit((unsigned)'C', 8); + Stream.Emit((unsigned)'S', 8); +} + +/// \brief Emits a block ID in the BLOCKINFO block. +void ClangDocBinaryWriter::emitBlockID(unsigned ID, const char *Name, + BitstreamWriter &Stream) { + Record.clear(); + Record.push_back(ID); + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record); + + // Emit the block name if present. + if (!Name || Name[0] == 0) return; + Record.clear(); + while (*Name) Record.push_back(*Name++); + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record); +} + +/// \brief Emits a record ID in the BLOCKINFO block. +void ClangDocBinaryWriter::emitRecordID(unsigned ID, const char *Name, + BitstreamWriter &Stream) { + Record.clear(); + Record.push_back(ID); + while (*Name) Record.push_back(*Name++); + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); +} + +// Common Abbreviations + +void ClangDocBinaryWriter::emitStringAbbrev(unsigned D, unsigned Block, + BitstreamWriter &Stream) { + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(D)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // String size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // String + Abbrevs.set(D, Stream.EmitBlockInfoAbbrev(Block, Abbrev)); +} + +void ClangDocBinaryWriter::emitLocationAbbrev(unsigned D, unsigned Block, + BitstreamWriter &Stream) { + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(D)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Line number + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Filename size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Filename + Abbrevs.set(D, Stream.EmitBlockInfoAbbrev(Block, Abbrev)); +} + +void ClangDocBinaryWriter::emitIntAbbrev(unsigned D, unsigned Block, + BitstreamWriter &Stream) { + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(D)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Integer + Abbrevs.set(D, Stream.EmitBlockInfoAbbrev(Block, Abbrev)); +} + +// Common Records + +void ClangDocBinaryWriter::emitStringRecord(StringRef Str, unsigned RecordId, + BitstreamWriter &Stream) { + if (Str.empty()) return; + Record.clear(); + Record.push_back(RecordId); + Record.push_back(Str.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(RecordId), Record, Str); +} + +void ClangDocBinaryWriter::emitLocationRecord(int LineNumber, StringRef File, + unsigned RecordId, + BitstreamWriter &Stream) { + if (OmitFilenames) return; + Record.clear(); + Record.push_back(RecordId); + Record.push_back(LineNumber); + Record.push_back(File.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(RecordId), Record, File); +} + +void ClangDocBinaryWriter::emitIntRecord(int Value, unsigned RecordId, + BitstreamWriter &Stream) { + if (!Value) return; + Record.clear(); + Record.push_back(RecordId); + Record.push_back(Value); + Stream.EmitRecordWithAbbrev(Abbrevs.get(RecordId), Record); +} + +// Common Blocks + +void ClangDocBinaryWriter::emitNamedTypeBlock(const NamedType &N, StringRef ID, + BitstreamWriter &Stream) { + Stream.EnterSubblock(NAMED_TYPE_BLOCK_ID, 5); + emitStringRecord(ID, NAMED_TYPE_ID, Stream); + emitStringRecord(N.Type, NAMED_TYPE_TYPE, Stream); + emitStringRecord(N.Name, NAMED_TYPE_NAME, Stream); + emitIntRecord(N.Access, NAMED_TYPE_ACCESS, Stream); + Stream.ExitBlock(); +} + +void ClangDocBinaryWriter::emitCommentBlock(const CommentInfo *I, + BitstreamWriter &Stream) { + Stream.EnterSubblock(COMMENT_BLOCK_ID, 5); + emitStringRecord(I->Kind, COMMENT_KIND, Stream); + emitStringRecord(I->Text, COMMENT_TEXT, Stream); + emitStringRecord(I->Name, COMMENT_NAME, Stream); + emitStringRecord(I->Direction, COMMENT_DIRECTION, Stream); + emitStringRecord(I->ParamName, COMMENT_PARAMNAME, Stream); + emitStringRecord(I->CloseName, COMMENT_CLOSENAME, Stream); + emitIntRecord(I->SelfClosing, COMMENT_SELFCLOSING, Stream); + emitIntRecord(I->Explicit, COMMENT_EXPLICIT, Stream); + for (const auto &A : I->AttrKeys) + emitStringRecord(A, COMMENT_ATTRKEY, Stream); + for (const auto &A : I->AttrValues) + emitStringRecord(A, COMMENT_ATTRVAL, Stream); + for (const auto &A : I->Args) emitStringRecord(A, COMMENT_ARG, Stream); + for (const auto &P : I->Position) + emitStringRecord(P, COMMENT_POSITION, Stream); + for (const auto &C : I->Children) emitCommentBlock(C.get(), Stream); + Stream.ExitBlock(); +} + +void ClangDocBinaryWriter::emitBlockInfoBlock(BitstreamWriter &Stream) { + Abbrevs.clear(); + emitHeader(Stream); + Stream.EnterBlockInfoBlock(); + + // Comment Block + emitBlockID(COMMENT_BLOCK_ID, "CommentBlock", Stream); + emitRecordID(COMMENT_KIND, "Kind", Stream); + emitRecordID(COMMENT_TEXT, "Text", Stream); + emitRecordID(COMMENT_NAME, "Name", Stream); + emitRecordID(COMMENT_DIRECTION, "Direction", Stream); + emitRecordID(COMMENT_PARAMNAME, "ParamName", Stream); + emitRecordID(COMMENT_CLOSENAME, "CloseName", Stream); + emitRecordID(COMMENT_SELFCLOSING, "SelfClosing", Stream); + emitRecordID(COMMENT_EXPLICIT, "Explicit", Stream); + emitRecordID(COMMENT_ATTRKEY, "AtrrKey", Stream); + emitRecordID(COMMENT_ATTRVAL, "AttrVal", Stream); + emitRecordID(COMMENT_ARG, "Arg", Stream); + emitRecordID(COMMENT_POSITION, "Position", Stream); + emitStringAbbrev(COMMENT_KIND, COMMENT_BLOCK_ID, Stream); + emitStringAbbrev(COMMENT_TEXT, COMMENT_BLOCK_ID, Stream); + emitStringAbbrev(COMMENT_NAME, COMMENT_BLOCK_ID, Stream); + emitStringAbbrev(COMMENT_POSITION, COMMENT_BLOCK_ID, Stream); + emitStringAbbrev(COMMENT_DIRECTION, COMMENT_BLOCK_ID, Stream); + emitStringAbbrev(COMMENT_PARAMNAME, COMMENT_BLOCK_ID, Stream); + emitStringAbbrev(COMMENT_CLOSENAME, COMMENT_BLOCK_ID, Stream); + emitIntAbbrev(COMMENT_SELFCLOSING, COMMENT_BLOCK_ID, Stream); + emitIntAbbrev(COMMENT_EXPLICIT, COMMENT_BLOCK_ID, Stream); + emitStringAbbrev(COMMENT_ATTRKEY, COMMENT_BLOCK_ID, Stream); + emitStringAbbrev(COMMENT_ATTRVAL, COMMENT_BLOCK_ID, Stream); + emitStringAbbrev(COMMENT_ARG, COMMENT_BLOCK_ID, Stream); + + // NamedType Block + emitBlockID(NAMED_TYPE_BLOCK_ID, "NamedTypeBlock", Stream); + emitRecordID(NAMED_TYPE_ID, "ID", Stream); + emitRecordID(NAMED_TYPE_TYPE, "Type", Stream); + emitRecordID(NAMED_TYPE_NAME, "Name", Stream); + emitRecordID(NAMED_TYPE_ACCESS, "Access", Stream); + emitStringAbbrev(NAMED_TYPE_ID, NAMED_TYPE_BLOCK_ID, Stream); + emitStringAbbrev(NAMED_TYPE_TYPE, NAMED_TYPE_BLOCK_ID, Stream); + emitStringAbbrev(NAMED_TYPE_NAME, NAMED_TYPE_BLOCK_ID, Stream); + emitIntAbbrev(NAMED_TYPE_ACCESS, NAMED_TYPE_BLOCK_ID, Stream); + +#define INFORECORD(X) \ + emitRecordID(X##_FULLY_QUALIFIED_NAME, "FullyQualifiedName", Stream); \ + emitRecordID(X##_NAME, "Name", Stream); \ + emitRecordID(X##_NAMESPACE, "Namespace", Stream); + +#define INFOABBREV(X) \ + emitStringAbbrev(X##_FULLY_QUALIFIED_NAME, X##_BLOCK_ID, Stream); \ + emitStringAbbrev(X##_NAME, X##_BLOCK_ID, Stream); \ + emitStringAbbrev(X##_NAMESPACE, X##_BLOCK_ID, Stream); + + // Namespace Block + emitBlockID(NAMESPACE_BLOCK_ID, "NamespaceBlock", Stream); + INFORECORD(NAMESPACE) + INFOABBREV(NAMESPACE) + + // NonDef Block + emitBlockID(NONDEF_BLOCK_ID, "NonDefBlock", Stream); + INFORECORD(NONDEF) + emitRecordID(NONDEF_LOCATION, "Location", Stream); + INFOABBREV(NONDEF) + emitLocationAbbrev(NONDEF_LOCATION, NONDEF_BLOCK_ID, Stream); + + // Enum Block + emitBlockID(ENUM_BLOCK_ID, "EnumBlock", Stream); + INFORECORD(ENUM) + emitRecordID(ENUM_LOCATION, "Location", Stream); + emitRecordID(ENUM_SCOPED, "Scoped", Stream); + INFOABBREV(ENUM) + emitLocationAbbrev(ENUM_LOCATION, ENUM_BLOCK_ID, Stream); + emitIntAbbrev(ENUM_SCOPED, ENUM_BLOCK_ID, Stream); + + // Record Block + emitBlockID(RECORD_BLOCK_ID, "RecordBlock", Stream); + INFORECORD(RECORD) + emitRecordID(RECORD_LOCATION, "Location", Stream); + emitRecordID(RECORD_TAG_TYPE, "TagType", Stream); + emitRecordID(RECORD_PARENT, "Parent", Stream); + emitRecordID(RECORD_VPARENT, "VParent", Stream); + INFOABBREV(RECORD) + emitLocationAbbrev(RECORD_LOCATION, RECORD_BLOCK_ID, Stream); + emitIntAbbrev(RECORD_TAG_TYPE, RECORD_BLOCK_ID, Stream); + emitStringAbbrev(RECORD_PARENT, RECORD_BLOCK_ID, Stream); + emitStringAbbrev(RECORD_VPARENT, RECORD_BLOCK_ID, Stream); + + // Function Block + emitBlockID(FUNCTION_BLOCK_ID, "FunctionBlock", Stream); + INFORECORD(FUNCTION) + emitRecordID(FUNCTION_LOCATION, "Location", Stream); + emitRecordID(FUNCTION_MANGLED_NAME, "MangledName", Stream); + emitRecordID(FUNCTION_PARENT, "Parent", Stream); + emitRecordID(FUNCTION_ACCESS, "Access", Stream); + INFOABBREV(FUNCTION) + emitLocationAbbrev(FUNCTION_LOCATION, FUNCTION_BLOCK_ID, Stream); + emitStringAbbrev(FUNCTION_MANGLED_NAME, FUNCTION_BLOCK_ID, Stream); + emitStringAbbrev(FUNCTION_PARENT, FUNCTION_BLOCK_ID, Stream); + emitIntAbbrev(FUNCTION_ACCESS, FUNCTION_BLOCK_ID, Stream); + +#undef INFORECORDS +#undef INFOABBREV + + Stream.ExitBlock(); +} + +// Info emission + +#define EMITINFO(X) \ + emitStringRecord(I.FullyQualifiedName, X##_FULLY_QUALIFIED_NAME, Stream); \ + emitStringRecord(I.SimpleName, X##_NAME, Stream); \ + emitStringRecord(I.Namespace, X##_NAMESPACE, Stream); \ + emitCommentBlock(&I.Description, Stream); + +template <> +void ClangDocBinaryWriter::writeBitstream(const NamespaceInfo &I, + BitstreamWriter &Stream) { + emitBlockInfoBlock(Stream); + Stream.EnterSubblock(NAMESPACE_BLOCK_ID, 5); + EMITINFO(NAMESPACE) + Stream.ExitBlock(); +} + +template <> +void ClangDocBinaryWriter::writeBitstream(const NonDefInfo &I, + BitstreamWriter &Stream) { + emitBlockInfoBlock(Stream); + Stream.EnterSubblock(NONDEF_BLOCK_ID, 5); + EMITINFO(NONDEF) + emitLocationRecord(I.LineNumber, I.Filename, NONDEF_LOCATION, Stream); + Stream.ExitBlock(); +} + +template <> +void ClangDocBinaryWriter::writeBitstream(const EnumInfo &I, + BitstreamWriter &Stream) { + emitBlockInfoBlock(Stream); + Stream.EnterSubblock(ENUM_BLOCK_ID, 5); + EMITINFO(ENUM) + emitLocationRecord(I.LineNumber, I.Filename, ENUM_LOCATION, Stream); + emitIntRecord(I.Scoped, ENUM_SCOPED, Stream); + for (const auto &N : I.Members) emitNamedTypeBlock(N, "Member", Stream); + Stream.ExitBlock(); +} + +template <> +void ClangDocBinaryWriter::writeBitstream(const RecordInfo &I, + BitstreamWriter &Stream) { + emitBlockInfoBlock(Stream); + Stream.EnterSubblock(RECORD_BLOCK_ID, 5); + EMITINFO(RECORD) + emitLocationRecord(I.LineNumber, I.Filename, RECORD_LOCATION, Stream); + emitIntRecord(I.TagType, RECORD_TAG_TYPE, Stream); + for (const auto &N : I.Members) emitNamedTypeBlock(N, "Member", Stream); + for (const auto &P : I.Parents) emitStringRecord(P, RECORD_PARENT, Stream); + for (const auto &P : I.VirtualParents) + emitStringRecord(P, RECORD_VPARENT, Stream); + Stream.ExitBlock(); +} + +template <> +void ClangDocBinaryWriter::writeBitstream(const FunctionInfo &I, + BitstreamWriter &Stream) { + emitBlockInfoBlock(Stream); + Stream.EnterSubblock(FUNCTION_BLOCK_ID, 5); + EMITINFO(FUNCTION) + emitLocationRecord(I.LineNumber, I.Filename, FUNCTION_LOCATION, Stream); + emitStringRecord(I.MangledName, FUNCTION_MANGLED_NAME, Stream); + emitStringRecord(I.Parent, FUNCTION_PARENT, Stream); + emitNamedTypeBlock(I.ReturnType, "Return", Stream); + for (const auto &N : I.Params) emitNamedTypeBlock(N, "Param", Stream); + emitIntRecord(I.Access, FUNCTION_ACCESS, Stream); + Stream.ExitBlock(); +} + +#undef EMITINFO + +// Reader + +bool ClangDocBinaryReader::readBitstream(SmallString<2048> Bits) { + BitstreamCursor Stream(Bits); + + if (Stream.AtEndOfStream()) return false; + + // Sniff for the signature. + if (Stream.Read(8) != 'D' || Stream.Read(8) != 'O' || Stream.Read(8) != 'C' || + Stream.Read(8) != 'S') + return false; + + // Read the top level blocks. + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + if (Code != bitc::ENTER_SUBBLOCK) return false; + + switch (Stream.ReadSubBlockID()) { + case llvm::bitc::BLOCKINFO_BLOCK_ID: { + BlockInfo = Stream.ReadBlockInfoBlock(/*ReadBlockInfoNames=*/true); + if (!BlockInfo) return false; + Stream.setBlockInfo(&*BlockInfo); + // Extract the record names associated with each field + for (unsigned i = BI_FIRST; i <= BI_LAST; ++i) { + for (const auto &N : (*BlockInfo).getBlockInfo(i)->RecordNames) + RecordNames[N.first] = N.second; + } + continue; + } + case NAMESPACE_BLOCK_ID: + if (readBlock(Stream, NAMESPACE_BLOCK_ID)) return true; + continue; + case NONDEF_BLOCK_ID: + if (readBlock(Stream, NONDEF_BLOCK_ID)) return true; + continue; + case NAMED_TYPE_BLOCK_ID: + if (readBlock(Stream, NAMED_TYPE_BLOCK_ID)) return true; + continue; + case COMMENT_BLOCK_ID: + if (readBlock(Stream, COMMENT_BLOCK_ID)) return true; + continue; + case RECORD_BLOCK_ID: + if (readBlock(Stream, RECORD_BLOCK_ID)) return true; + continue; + case ENUM_BLOCK_ID: + if (readBlock(Stream, ENUM_BLOCK_ID)) return true; + continue; + case FUNCTION_BLOCK_ID: + if (readBlock(Stream, FUNCTION_BLOCK_ID)) return true; + continue; + default: + if (!Stream.SkipBlock()) return false; + continue; + } + } + return true; +} + +bool ClangDocBinaryReader::readBlock(llvm::BitstreamCursor &Stream, + unsigned ID) { + if (Stream.EnterSubBlock(ID)) return false; + + SmallVector Record; + while (true) { + unsigned BlockOrCode = 0; + Cursor Res = skipUntilRecordOrBlock(Stream, BlockOrCode); + + switch (Res) { + case Cursor::BadBlock: + return false; + case Cursor::BlockEnd: + return true; + case Cursor::BlockBegin: + if (readBlock(Stream, BlockOrCode)) continue; + if (!Stream.SkipBlock()) return false; + continue; + case Cursor::Record: + break; + } + + // Read the record. + Record.clear(); + StringRef Blob; + unsigned RecID = Stream.readRecord(BlockOrCode, Record, &Blob); + if (RecID < DT_FIRST || RecID > DT_LAST) continue; + +#define INFOCASES(X) \ + case X##_FULLY_QUALIFIED_NAME: \ + case X##_NAME: \ + case X##_NAMESPACE: + + switch ((DataTypes)RecID) { + // Locations + case ENUM_LOCATION: + case RECORD_LOCATION: + case FUNCTION_LOCATION: + case NONDEF_LOCATION: + OS << RecordNames[RecID] << ": " << Blob << ":" << Record[0] << "\n"; + continue; + + // Strings + INFOCASES(NAMESPACE) + INFOCASES(NONDEF) + INFOCASES(ENUM) + INFOCASES(RECORD) + INFOCASES(FUNCTION) + case NAMED_TYPE_ID: + case NAMED_TYPE_TYPE: + case NAMED_TYPE_NAME: + case RECORD_PARENT: + case RECORD_VPARENT: + case FUNCTION_PARENT: + case FUNCTION_MANGLED_NAME: + case COMMENT_KIND: + case COMMENT_TEXT: + case COMMENT_NAME: + case COMMENT_DIRECTION: + case COMMENT_PARAMNAME: + case COMMENT_CLOSENAME: + case COMMENT_ATTRKEY: + case COMMENT_ATTRVAL: + case COMMENT_ARG: + case COMMENT_POSITION: + OS << RecordNames[RecID] << ": " << Blob << "\n"; + continue; + + // Ints + case ENUM_SCOPED: + case RECORD_TAG_TYPE: + case NAMED_TYPE_ACCESS: + case FUNCTION_ACCESS: + case COMMENT_SELFCLOSING: + case COMMENT_EXPLICIT: + OS << RecordNames[RecID] << ": " << Record[0] << "\n"; + default: + continue; + } + } +} + +#undef INFOCASES + +ClangDocBinaryReader::Cursor ClangDocBinaryReader::skipUntilRecordOrBlock( + llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) { + BlockOrRecordID = 0; + + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + + switch ((llvm::bitc::FixedAbbrevIDs)Code) { + case llvm::bitc::ENTER_SUBBLOCK: + BlockOrRecordID = Stream.ReadSubBlockID(); + return Cursor::BlockBegin; + case llvm::bitc::END_BLOCK: + if (Stream.ReadBlockEnd()) return Cursor::BadBlock; + return Cursor::BlockEnd; + case llvm::bitc::DEFINE_ABBREV: + Stream.ReadAbbrevRecord(); + continue; + case llvm::bitc::UNABBREV_RECORD: + return Cursor::BadBlock; + default: + // We found a record. + BlockOrRecordID = Code; + return Cursor::Record; + } + } + llvm_unreachable("Premature stream end."); +} + +} // namespace doc +} // namespace clang Index: clang-doc/ClangDocMapper.h =================================================================== --- /dev/null +++ clang-doc/ClangDocMapper.h @@ -0,0 +1,95 @@ +//===-- 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 +#include +#include +#include "ClangDocBinary.h" +#include "ClangDocRepresentation.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/Execution.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang::comments; +using namespace clang::tooling; + +namespace clang { +namespace doc { + +class ClangDocCommentVisitor + : public ConstCommentVisitor { + public: + ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {} + + void 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(ClangDocBinaryWriter &Writer) : Writer(Writer) {} + + template + StringRef emitInfo(const C *D, const FullComment *FC, StringRef Key, + int LineNumber, StringRef File); + + private: + template + SmallString<2048> 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; + void parseFullComment(const FullComment *C, CommentInfo &CI); + std::string getParentNamespace(const DeclContext *D) const; + + ClangDocBinaryWriter &Writer; +}; + +} // 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,283 @@ +//===-- 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 "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) parseFullComment(FC, I.Description); + 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 +SmallString<2048> ClangDocMapper::serialize(T &I) const { + SmallString<2048> Buffer; + llvm::BitstreamWriter Stream(Buffer); + Writer.writeBitstream(I, Stream); + return Buffer; +} + +void ClangDocMapper::parseFullComment(const FullComment *C, CommentInfo &CI) { + ClangDocCommentVisitor Visitor(CI); + 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) parseFullComment(C, I.Description); +} + +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) parseFullComment(FC, I.Description); + 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()); +} + +std::string ClangDocMapper::getParentNamespace(const DeclContext *D) const { + if (const auto *N = dyn_cast(D->getParent())) { + return N->getQualifiedNameAsString(); + } + return ""; +} + +// ClangDocCommentVisitor + +void 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())) { + CurrentCI.Children.emplace_back(std::make_shared()); + ClangDocCommentVisitor Visitor(*CurrentCI.Children.back()); + Visitor.parseComment(Child); + } +} + +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 std::all_of(S.begin(), S.end(), isspace); +} + +} // namespace doc +} // namespace clang Index: clang-doc/ClangDocRepresentation.h =================================================================== --- /dev/null +++ clang-doc/ClangDocRepresentation.h @@ -0,0 +1,99 @@ +///===-- ClangDocRepresentation.cpp - ClangDocRepresenation -----*- 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_REPRESENTATION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPRESENTATION_H + +#include +#include "clang/AST/Type.h" +#include "clang/Basic/Specifiers.h" +#include "llvm/ADT/SmallVector.h" + +using namespace llvm; + +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 { + std::string 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. + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REPRESENTATION_H \ No newline at end of file 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,100 @@ +//===-- 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 +#include "ClangDoc.h" +#include "ClangDocBinary.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" + +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::ClangDocBinaryWriter Writer(OmitFilenames); + + doc::ClangDocCallback NCallback("namespace", *ECtx, Writer); + Finder.addMatcher(namespaceDecl().bind("namespace"), &NCallback); + doc::ClangDocCallback RCallback("record", *ECtx, Writer); + Finder.addMatcher(recordDecl().bind("record"), &RCallback); + doc::ClangDocCallback ECallback("enum", *ECtx, Writer); + Finder.addMatcher(enumDecl().bind("enum"), &ECallback); + doc::ClangDocCallback MCallback("method", *ECtx, Writer); + Finder.addMatcher(cxxMethodDecl(isUserProvided()).bind("method"), &MCallback); + doc::ClangDocCallback FCallback("function", *ECtx, Writer); + 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) { + doc::ClangDocBinaryReader Reader(outs()); + Exec->get()->getToolResults()->forEachResult( + [&Reader](StringRef Key, SmallString<2048> Value) { + outs() << "---\n" + << "KEY: " << Key.str() << "\n"; + Reader.readBitstream(Value); + }); + } + + return 0; +} Index: docs/clang-doc.rst =================================================================== --- /dev/null +++ docs/clang-doc.rst @@ -0,0 +1,13 @@ +=================== +Clang-Doc +=================== + +.. contents:: + +Intro + +Setup +===== + +Use +============ Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -41,6 +41,7 @@ clang-apply-replacements clang-change-namespace clangd + clang-doc clang-include-fixer clang-move clang-query Index: test/clang-doc/mapper-namespace.cpp =================================================================== --- /dev/null +++ test/clang-doc/mapper-namespace.cpp @@ -0,0 +1,70 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp | FileCheck %s + +namespace A { +// CHECK: --- +// CHECK: KEY: A +// CHECK: FullyQualifiedName: A +// CHECK: Name: A + +void f() {}; +// CHECK: --- +// CHECK: KEY: _ZN1A1fEv +// CHECK: FullyQualifiedName: A::f +// CHECK: Name: f +// CHECK: Namespace: A +// CHECK: MangledName: _ZN1A1fEv +// CHECK: ID: Return +// CHECK: Type: void +// CHECK: Access: 3 +// CHECK: Access: 3 + +} // A + +namespace A { +// CHECK: --- +// CHECK: KEY: A +// CHECK: FullyQualifiedName: A +// CHECK: Name: A + +namespace B { +// CHECK: --- +// CHECK: KEY: A::B +// CHECK: FullyQualifiedName: A::B +// CHECK: Name: B +// CHECK: Namespace: A + +enum E { X }; +// CHECK: --- +// CHECK: KEY: A::B::E +// CHECK: FullyQualifiedName: A::B::E +// CHECK: Name: E +// CHECK: Namespace: A::B +// CHECK: ID: Member +// CHECK: Type: A::B::X +// CHECK: Access: 3 + +E func(int i) { + return X; +} + +// CHECK: --- +// CHECK: KEY: _ZN1A1B4funcEi +// CHECK: FullyQualifiedName: A::B::func +// CHECK: Name: func +// CHECK: Namespace: A::B +// CHECK: MangledName: _ZN1A1B4funcEi +// CHECK: ID: Return +// CHECK: Type: enum A::B::E +// CHECK: Access: 3 +// CHECK: ID: Param +// CHECK: Type: int +// CHECK: Name: i +// CHECK: Access: 3 +// CHECK: Access: 3 + +} // B +} // C Index: test/clang-doc/mapper-type.cpp =================================================================== --- /dev/null +++ test/clang-doc/mapper-type.cpp @@ -0,0 +1,137 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp | FileCheck %s + +union A { int X; int Y; }; +// CHECK: --- +// CHECK: KEY: A +// CHECK: FullyQualifiedName: A +// CHECK: Name: A +// CHECK: TagType: 2 +// CHECK: ID: Member +// CHECK: Type: int +// CHECK: Name: A::X +// CHECK: Access: 3 +// CHECK: ID: Member +// CHECK: Type: int +// CHECK: Name: A::Y +// CHECK: Access: 3 +// CHECK: --- +// CHECK: KEY: A::A +// CHECK: FullyQualifiedName: A::A +// CHECK: Name: A +// CHECK: Namespace: A + +enum B { X, Y }; +// CHECK: --- +// CHECK: KEY: B +// CHECK: FullyQualifiedName: B +// CHECK: Name: B +// CHECK: ID: Member +// CHECK: Type: X +// CHECK: Access: 3 +// CHECK: ID: Member +// CHECK: Type: Y +// CHECK: Access: 3 + +struct C { int i; }; +// CHECK: --- +// CHECK: KEY: C +// CHECK: FullyQualifiedName: C +// CHECK: Name: C +// CHECK: ID: Member +// CHECK: Type: int +// CHECK: Name: C::i +// CHECK: Access: 3 +// CHECK: --- +// CHECK: KEY: C::C +// CHECK: FullyQualifiedName: C::C +// CHECK: Name: C +// CHECK: Namespace: C + +class D {}; +// CHECK: --- +// CHECK: KEY: D +// CHECK: FullyQualifiedName: D +// CHECK: Name: D +// CHECK: TagType: 3 +// CHECK: --- +// CHECK: KEY: D::D +// CHECK: FullyQualifiedName: D::D +// CHECK: Name: D +// CHECK: Namespace: D + +class E { +// CHECK: --- +// CHECK: KEY: E +// CHECK: FullyQualifiedName: E +// CHECK: Name: E +// CHECK: TagType: 3 +// CHECK: --- +// CHECK: KEY: E::E +// CHECK: FullyQualifiedName: E::E +// CHECK: Name: E +// CHECK: Namespace: E + +public: + E() {} +// CHECK: --- +// CHECK: KEY: _ZN1EC1Ev +// CHECK: FullyQualifiedName: E::E +// CHECK: Name: E +// CHECK: Namespace: E +// CHECK: MangledName: _ZN1EC1Ev +// CHECK: Parent: E +// CHECK: ID: Return +// CHECK: Type: void +// CHECK: Access: 3 + + ~E() {} +// CHECK: --- +// CHECK: KEY: _ZN1ED1Ev +// CHECK: FullyQualifiedName: E::~E +// CHECK: Name: ~E +// CHECK: Namespace: E +// CHECK: MangledName: _ZN1ED1Ev +// CHECK: Parent: E +// CHECK: ID: Return +// CHECK: Type: void +// CHECK: Access: 3 + +protected: + void ProtectedMethod(); +// CHECK: --- +// CHECK: KEY: _ZN1E15ProtectedMethodEv +// CHECK: FullyQualifiedName: _ZN1E15ProtectedMethodEv +// CHECK: Name: ProtectedMethod +// CHECK: Namespace: E +}; + +void E::ProtectedMethod() {} +// CHECK: --- +// CHECK: KEY: _ZN1E15ProtectedMethodEv +// CHECK: FullyQualifiedName: E::ProtectedMethod +// CHECK: Name: ProtectedMethod +// CHECK: Namespace: E +// CHECK: MangledName: _ZN1E15ProtectedMethodEv +// CHECK: Parent: E +// CHECK: ID: Return +// CHECK: Type: void +// CHECK: Access: 3 +// CHECK: Access: 1 + +class F : virtual private D, public E {}; +// CHECK: --- +// CHECK: KEY: F +// CHECK: FullyQualifiedName: F +// CHECK: Name: F +// CHECK: TagType: 3 +// CHECK: Parent: class E +// CHECK: VParent: class D +// CHECK: --- +// CHECK: KEY: F::F +// CHECK: FullyQualifiedName: F::F +// CHECK: Name: F +// CHECK: Namespace: F