Index: clang-doc/BitcodeReader.h =================================================================== --- /dev/null +++ clang-doc/BitcodeReader.h @@ -0,0 +1,96 @@ +//===-- BitcodeReader.h - ClangDoc Bitcode Reader --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a writer for serializing the clang-doc internal +// representation to LLVM bitcode. The writer takes in a stream and emits the +// generated bitcode to that stream. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BITCODE_READER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BITCODE_READER_H + +#include +#include +#include "BitcodeWriter.h" +#include "Representation.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 to read bitstream into an InfoSet collection +class ClangDocBitcodeReader { + public: + ClangDocBitcodeReader() {} + using RecordData = SmallVector; + + bool readBitstreamToInfoSet(StringRef Key, SmallString<2048> Bits, + std::unique_ptr &IS); + + private: + enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; + + // Sream validation and overview + bool validateStream(llvm::BitstreamCursor &Stream); + bool readVersion(llvm::BitstreamCursor &Stream); + bool readBlockInfoBlock(llvm::BitstreamCursor &Stream); + + // Reading records + template + bool readRecord(llvm::BitstreamCursor &Stream, unsigned ID, T &I); + + bool parseRecord(unsigned ID, StringRef Blob, unsigned VersionNo); + bool parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I); + bool parseRecord(unsigned ID, StringRef Blob, std::unique_ptr &I); + bool parseRecord(unsigned ID, StringRef Blob, std::unique_ptr &I); + bool parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I); + bool parseRecord(unsigned ID, StringRef Blob, std::unique_ptr &I); + bool parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I); + bool parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I); + bool parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I); + + // Reading blocks + template + bool readBlock(llvm::BitstreamCursor &Stream, unsigned ID, T &I); + template + bool readSubBlock(llvm::BitstreamCursor &Stream, unsigned ID, T &I); + template + bool readBlockToInfo(llvm::BitstreamCursor &Stream, + std::unique_ptr &IS, unsigned ID, StringRef Key); + + // Helpers + template + std::unique_ptr makeInfo(unsigned ID); + template + std::unique_ptr &getCommentInfo(T &I); + template + bool addTypeInfo(Iy &I, Ty &&T); + Cursor skipUntilRecordOrBlock(llvm::BitstreamCursor &Stream, + unsigned &BlockOrRecordID); + + SmallVector Record; + Optional BlockInfo; + std::map RecordNames; +}; + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_BITCODE_READER_H Index: clang-doc/BitcodeReader.cpp =================================================================== --- /dev/null +++ clang-doc/BitcodeReader.cpp @@ -0,0 +1,446 @@ +//===-- BitcodeReader.cpp - ClangDoc Bitcode Reader ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "BitcodeReader.h" +#include "llvm/ADT/IndexedMap.h" + +using namespace llvm; + +namespace clang { +namespace doc { + +// Read records + +template +bool ClangDocBitcodeReader::readRecord(llvm::BitstreamCursor &Stream, + unsigned ID, T &I) { + Record.clear(); + StringRef Blob; + unsigned RecID = Stream.readRecord(ID, Record, &Blob); + return parseRecord(RecID, Blob, I); +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob, + unsigned VersionNo) { + if (ID == VERSION && Record[0] == VersionNo) return true; + return false; +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I) { + switch (ID) { + case NAMESPACE_NAME: + I->Name = Blob; + return true; + case NAMESPACE_NAMESPACE: + I->Namespace.push_back(Blob); + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I) { + switch (ID) { + case RECORD_NAME: + I->Name = Blob; + return true; + case RECORD_NAMESPACE: + I->Namespace.push_back(Blob); + return true; + case RECORD_ISDEFINITION: + I->IsDefinition = (bool)Record[0]; + return true; + case RECORD_PARENT: + I->ParentUSRs.push_back(Blob); + return true; + case RECORD_VPARENT: + I->VirtualParentUSRs.push_back(Blob); + return true; + case RECORD_LOCATION: + I->Loc.emplace_back(Location{(int)Record[0], Blob}); + return true; + case RECORD_TAG_TYPE: + I->TagType = (TagTypeKind)Record[0]; + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I) { + switch (ID) { + case ENUM_NAME: + I->Name = Blob; + return true; + case ENUM_NAMESPACE: + I->Namespace.push_back(Blob); + return true; + case ENUM_ISDEFINITION: + I->IsDefinition = (bool)Record[0]; + return true; + case ENUM_LOCATION: + I->Loc.emplace_back(Location{(int)Record[0], Blob}); + return true; + case ENUM_SCOPED: + I->Scoped = Record[0]; + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I) { + switch (ID) { + case FUNCTION_NAME: + I->Name = Blob; + return true; + case FUNCTION_NAMESPACE: + I->Namespace.push_back(Blob); + return true; + case FUNCTION_PARENT: + I->ParentUSR = Blob; + return true; + case FUNCTION_ISDEFINITION: + I->IsDefinition = (bool)Record[0]; + return true; + case FUNCTION_LOCATION: + I->Loc.emplace_back(Location{(int)Record[0], Blob}); + return true; + case FUNCTION_ACCESS: + I->Access = (AccessSpecifier)Record[0]; + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I) { + switch (ID) { + case TYPE_TYPE: + I->TypeUSR = Blob; + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I) { + switch (ID) { + case FIELD_TYPE_TYPE: + I->TypeUSR = Blob; + return true; + case FIELD_TYPE_NAME: + I->Name = Blob; + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I) { + switch (ID) { + case MEMBER_TYPE_TYPE: + I->TypeUSR = Blob; + return true; + case MEMBER_TYPE_NAME: + I->Name = Blob; + return true; + case MEMBER_TYPE_ACCESS: + I->Access = (AccessSpecifier)Record[0]; + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, StringRef Blob, + std::unique_ptr &I) { + switch (ID) { + case COMMENT_KIND: + I->Kind = Blob; + return true; + case COMMENT_TEXT: + I->Text = Blob; + return true; + case COMMENT_NAME: + I->Name = Blob; + return true; + case COMMENT_DIRECTION: + I->Direction = Blob; + return true; + case COMMENT_PARAMNAME: + I->ParamName = Blob; + return true; + case COMMENT_CLOSENAME: + I->CloseName = Blob; + return true; + case COMMENT_ATTRKEY: + I->AttrKeys.push_back(Blob); + return true; + case COMMENT_ATTRVAL: + I->AttrValues.push_back(Blob); + return true; + case COMMENT_ARG: + I->Args.push_back(Blob); + return true; + case COMMENT_POSITION: + I->Position.push_back(Blob); + return true; + case COMMENT_SELFCLOSING: + I->SelfClosing = Record[0]; + return true; + case COMMENT_EXPLICIT: + I->Explicit = Record[0]; + return true; + default: + return false; + } +} + +// Helpers + +template +std::unique_ptr &ClangDocBitcodeReader::getCommentInfo(T &I) { + I->Description.emplace_back(llvm::make_unique()); + return I->Description.back(); +} + +template <> +std::unique_ptr &ClangDocBitcodeReader::getCommentInfo( + const unsigned int &I) { + errs() << "Cannot not have comment subblock.\n"; + exit(1); +} + +template <> +std::unique_ptr &ClangDocBitcodeReader::getCommentInfo( + std::unique_ptr &I) { + I->Children.emplace_back(llvm::make_unique()); + return I->Children.back(); +} + +template +bool ClangDocBitcodeReader::addTypeInfo(Iy &I, Ty &&T) { + errs() << "Cannot not have type subblock.\n"; + exit(1); +} + +template <> +bool ClangDocBitcodeReader::addTypeInfo(std::unique_ptr &I, + std::unique_ptr &&T) { + I->Members.emplace_back(std::move(T)); + return true; +} + +template <> +bool ClangDocBitcodeReader::addTypeInfo(std::unique_ptr &I, + std::unique_ptr &&T) { + I->Members.emplace_back(std::move(T)); + return true; +} + +template <> +bool ClangDocBitcodeReader::addTypeInfo(std::unique_ptr &I, + std::unique_ptr &&T) { + I->ReturnType = std::move(T); + return true; +} + +template <> +bool ClangDocBitcodeReader::addTypeInfo(std::unique_ptr &I, + std::unique_ptr &&T) { + I->Params.emplace_back(std::move(T)); + return true; +} + +ClangDocBitcodeReader::Cursor ClangDocBitcodeReader::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: + BlockOrRecordID = Code; + return Cursor::Record; + } + } + llvm_unreachable("Premature stream end."); +} + +// Read blocks + +template +bool ClangDocBitcodeReader::readBlock(llvm::BitstreamCursor &Stream, + unsigned ID, Ty &I) { + if (Stream.EnterSubBlock(ID)) return false; + + 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 (readSubBlock(Stream, BlockOrCode, I)) continue; + if (!Stream.SkipBlock()) return false; + continue; + case Cursor::Record: + break; + } + if (!readRecord(Stream, BlockOrCode, I)) return false; + } +} + +template +bool ClangDocBitcodeReader::readSubBlock(llvm::BitstreamCursor &Stream, + unsigned ID, Ty &I) { + switch (ID) { + // Blocks can only have Comment or TypeInfo subblocks + case BI_COMMENT_BLOCK_ID: + if (readBlock(Stream, ID, getCommentInfo(I))) return true; + return false; + case BI_TYPE_BLOCK_ID: { + std::unique_ptr T = llvm::make_unique(); + if (readBlock(Stream, ID, T)) { + if (addTypeInfo(I, std::move(T))) return true; + } + return false; + } + case BI_FIELD_TYPE_BLOCK_ID: { + std::unique_ptr T = llvm::make_unique(); + if (readBlock(Stream, ID, T)) { + if (addTypeInfo(I, std::move(T))) return true; + } + return false; + } + case BI_MEMBER_TYPE_BLOCK_ID: { + std::unique_ptr T = llvm::make_unique(); + if (readBlock(Stream, ID, T)) { + if (addTypeInfo(I, std::move(T))) return true; + } + return false; + } + default: + errs() << "Invalid subblock type.\n"; + exit(1); + } +} + +// Validation and overview + +bool ClangDocBitcodeReader::validateStream(llvm::BitstreamCursor &Stream) { + 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; + return true; +} + +bool ClangDocBitcodeReader::readBlockInfoBlock(llvm::BitstreamCursor &Stream) { + 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; + } + return true; +} + +// Entry point + +bool ClangDocBitcodeReader::readBitstreamToInfoSet( + StringRef Key, SmallString<2048> Bits, std::unique_ptr &IS) { + BitstreamCursor Stream(Bits); + if (!validateStream(Stream)) return false; + + // Read the top level blocks. + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + if (Code != bitc::ENTER_SUBBLOCK) return false; + + switch (auto ID = Stream.ReadSubBlockID()) { + // NamedType and Comment blocks should not appear at the top level + case BI_TYPE_BLOCK_ID: + case BI_FIELD_TYPE_BLOCK_ID: + case BI_MEMBER_TYPE_BLOCK_ID: + case BI_COMMENT_BLOCK_ID: + errs() << "Invalid top level block.\n"; + return false; + case BI_VERSION_BLOCK_ID: + if (readBlock(Stream, ID, VERSION_NUMBER)) continue; + return false; + case llvm::bitc::BLOCKINFO_BLOCK_ID: + if (readBlockInfoBlock(Stream)) continue; + return false; + default: + if (!Stream.SkipBlock()) return false; + continue; + case BI_NAMESPACE_BLOCK_ID: + if (readBlockToInfo(Stream, IS, ID, Key)) + continue; + return false; + case BI_RECORD_BLOCK_ID: + if (readBlockToInfo(Stream, IS, ID, Key)) + continue; + return false; + case BI_ENUM_BLOCK_ID: + if (readBlockToInfo(Stream, IS, ID, Key)) + continue; + return false; + case BI_FUNCTION_BLOCK_ID: + if (readBlockToInfo(Stream, IS, ID, Key)) + continue; + return false; + } + } + return true; +} + +template +bool ClangDocBitcodeReader::readBlockToInfo(llvm::BitstreamCursor &Stream, + std::unique_ptr &IS, + unsigned ID, + StringRef Key) { + auto I = llvm::make_unique(); + if (readBlock(Stream, ID, I)) { + IS->insert(Key, std::move(I)); + return true; + } + return false; +} + +} // namespace doc +} // namespace clang Index: clang-doc/BitcodeWriter.h =================================================================== --- clang-doc/BitcodeWriter.h +++ clang-doc/BitcodeWriter.h @@ -151,6 +151,7 @@ template void writeBitstream(const T &I, bool WriteBlockInfo = false); + void writeBitstreamForInfoSet(InfoSet &ISet); private: class AbbreviationMap { Index: clang-doc/BitcodeWriter.cpp =================================================================== --- clang-doc/BitcodeWriter.cpp +++ clang-doc/BitcodeWriter.cpp @@ -396,5 +396,13 @@ #undef EMITINFO +void ClangDocBitcodeWriter::writeBitstreamForInfoSet(InfoSet &ISet) { + emitBlockInfoBlock(); + for (const auto &I : ISet.getNamespaceInfos()) writeBitstream(*I); + for (const auto &I : ISet.getFunctionInfos()) writeBitstream(*I); + for (const auto &I : ISet.getRecordInfos()) writeBitstream(*I); + for (const auto &I : ISet.getEnumInfos()) writeBitstream(*I); +} + } // namespace doc } // namespace clang Index: clang-doc/CMakeLists.txt =================================================================== --- clang-doc/CMakeLists.txt +++ clang-doc/CMakeLists.txt @@ -3,8 +3,11 @@ ) add_clang_library(clangDoc + BitcodeReader.cpp BitcodeWriter.cpp Mapper.cpp + Reducer.cpp + Representation.cpp LINK_LIBS clangAnalysis Index: clang-doc/Reducer.h =================================================================== --- /dev/null +++ clang-doc/Reducer.h @@ -0,0 +1,26 @@ +///===-- ClangDocReducer.h - ClangDocReducer -------*- 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_REDUCER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REDUCER_H + +#include "Representation.h" +#include "clang/Tooling/Execution.h" +#include "clang/Tooling/Tooling.h" + +namespace clang { +namespace doc { + +// Combine occurrences of the same info across translation units. +std::unique_ptr mergeInfos(tooling::ToolResults *Results); + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_CLANG_DOC_REDUCER_H Index: clang-doc/Reducer.cpp =================================================================== --- /dev/null +++ clang-doc/Reducer.cpp @@ -0,0 +1,32 @@ +///===-- ClangDocReducer.h - ClangDocReducer -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "BitcodeReader.h" +#include "Reducer.h" +#include "Representation.h" + +namespace clang { +namespace doc { + +std::unique_ptr mergeInfos(tooling::ToolResults *Results) { + std::unique_ptr UniqueInfos = llvm::make_unique(); + doc::ClangDocBitcodeReader Reader; + bool Err = false; + Results->forEachResult([&](StringRef Key, StringRef Value) { + if (!Reader.readBitstreamToInfoSet(Key, Value, UniqueInfos)) { + Err = true; + return; + } + }); + if (Err) return nullptr; + return std::move(UniqueInfos); +} + +} // namespace doc +} // namespace clang Index: clang-doc/Representation.h =================================================================== --- clang-doc/Representation.h +++ clang-doc/Representation.h @@ -123,6 +123,59 @@ // TODO: Add functionality to include separate markdown pages. +class InfoSet { + public: + InfoSet() {} + InfoSet &operator=(const InfoSet &) = delete; + InfoSet &&operator=(const InfoSet &&) = delete; + + void insert(StringRef Key, std::unique_ptr &&I); + void insert(StringRef Key, std::unique_ptr &&I); + void insert(StringRef Key, std::unique_ptr &&I); + void insert(StringRef Key, std::unique_ptr &&I); + + // Returns the info with a Key, if it exists. Valid until next insert(). + template + std::unique_ptr& find(StringRef Key); + + const std::vector> &getNamespaceInfos() const { + return NamespaceInfos; + } + const std::vector> &getFunctionInfos() const { + return FunctionInfos; + } + const std::vector> &getRecordInfos() const { + return RecordInfos; + } + const std::vector> &getEnumInfos() const { + return EnumInfos; + } + + private: + // Merge symbols L and R, preferring data from L in case of conflict. + void mergeInfo(std::unique_ptr &L, + std::unique_ptr &R); + void mergeInfo(std::unique_ptr &L, + std::unique_ptr &R); + void mergeInfo(std::unique_ptr &L, std::unique_ptr &R); + void mergeInfo(std::unique_ptr &L, + std::unique_ptr &R); + void mergeInfo(std::unique_ptr &L, std::unique_ptr &R); + void mergeInfo(std::unique_ptr &L, + std::unique_ptr &R); + void mergeInfo(std::unique_ptr &L, + std::unique_ptr &R); + + void mergeInfoBase(Info *L, Info *R); + void mergeSymbolInfoBase(SymbolInfo *L, SymbolInfo *R); + + std::vector> NamespaceInfos; + std::vector> FunctionInfos; + std::vector> RecordInfos; + std::vector> EnumInfos; + llvm::DenseMap InfoIndex; +}; + } // namespace doc } // namespace clang Index: clang-doc/Representation.cpp =================================================================== --- /dev/null +++ clang-doc/Representation.cpp @@ -0,0 +1,159 @@ +///===-- 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. +// +//===----------------------------------------------------------------------===// + +#include "Representation.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace clang { +namespace doc { + +void InfoSet::mergeInfoBase(Info *L, Info *R) { + if (L->Name.empty()) L->Name = R->Name; + if (L->Namespace.empty()) L->Namespace = std::move(R->Namespace); + std::move(R->Description.begin(), R->Description.end(), + std::back_inserter(L->Description)); +} + +void InfoSet::mergeSymbolInfoBase(SymbolInfo *L, SymbolInfo *R) { + mergeInfoBase(L, R); + L->Loc.insert(L->Loc.end(), R->Loc.begin(), R->Loc.end()); +} + +void InfoSet::mergeInfo(std::unique_ptr &L, + std::unique_ptr &R) { + if (L->TypeUSR.empty()) L->TypeUSR = R->TypeUSR; + std::move(R->Description.begin(), R->Description.end(), + std::back_inserter(L->Description)); +} + +void InfoSet::mergeInfo(std::unique_ptr &L, + std::unique_ptr &R) { + if (L->TypeUSR.empty()) L->TypeUSR = R->TypeUSR; + if (L->Name.empty()) L->Name = R->Name; + std::move(R->Description.begin(), R->Description.end(), + std::back_inserter(L->Description)); +} + +void InfoSet::mergeInfo(std::unique_ptr &L, + std::unique_ptr &R) { + if (L->TypeUSR.empty()) L->TypeUSR = R->TypeUSR; + if (L->Name.empty()) L->Name = R->Name; + if (!L->Access) L->Access = R->Access; + std::move(R->Description.begin(), R->Description.end(), + std::back_inserter(L->Description)); +} + +void InfoSet::mergeInfo(std::unique_ptr &L, + std::unique_ptr &R) { + mergeInfoBase(L.get(), R.get()); +} + +void InfoSet::mergeInfo(std::unique_ptr &L, + std::unique_ptr &R) { + mergeSymbolInfoBase(L.get(), R.get()); + if (!L->TagType) L->TagType = R->TagType; + if (L->Members.empty()) + L->Members = std::move(R->Members); + else if (L->Members.size() == R->Members.size()) { + for (unsigned i = 0; i < L->Members.size(); ++i) + mergeInfo(L->Members[i], R->Members[i]); + } + if (L->ParentUSRs.empty()) L->ParentUSRs = std::move(R->ParentUSRs); + if (L->VirtualParentUSRs.empty()) + L->VirtualParentUSRs = std::move(R->VirtualParentUSRs); +} + +void InfoSet::mergeInfo(std::unique_ptr &L, + std::unique_ptr &R) { + mergeSymbolInfoBase(L.get(), R.get()); + if (!L->Scoped) L->Scoped = R->Scoped; + if (L->Members.empty()) + L->Members = std::move(R->Members); + else if (L->Members.size() == R->Members.size()) { + for (unsigned i = 0; i < L->Members.size(); ++i) + mergeInfo(L->Members[i], R->Members[i]); + } +} + +void InfoSet::mergeInfo(std::unique_ptr &L, + std::unique_ptr &R) { + mergeSymbolInfoBase(L.get(), R.get()); + if (L->ParentUSR.empty()) L->ParentUSR = R->ParentUSR; + if (!L->Access) L->Access = R->Access; + mergeInfo(L->ReturnType, R->ReturnType); + if (L->Params.empty()) + L->Params = std::move(R->Params); + else if (L->Params.size() == R->Params.size()) { + for (unsigned i = 0; i < L->Params.size(); ++i) + mergeInfo(L->Params[i], R->Params[i]); + } +} + +#define FIND_FUNC(X) \ + template <> \ + std::unique_ptr &InfoSet::find(StringRef Key) { \ + auto I = InfoIndex.find(Key); \ + return I == InfoIndex.end() ? nullptr : X##s[I->second]; \ + } + +// FIND_FUNC(NamespaceInfo) +// FIND_FUNC(RecordInfo) +// FIND_FUNC(EnumInfo) +// FIND_FUNC(FunctionInfo) + +#undef FIND_FUNC + +void InfoSet::insert(StringRef Key, std::unique_ptr &&I) { + auto R = InfoIndex.try_emplace(Key, RecordInfos.size()); + if (R.second) + RecordInfos.push_back(std::move(I)); + else { + if (I->IsDefinition) { + mergeInfo(I, RecordInfos[R.first->second]); + RecordInfos[R.first->second] = std::move(I); + return; + } + mergeInfo(RecordInfos[R.first->second], I); + } +} + +#define INSERT_FUNC(X) \ + void InfoSet::insert(StringRef Key, std::unique_ptr &&I) { \ + auto R = InfoIndex.try_emplace(Key, X##s.size()); \ + if (R.second) \ + X##s.push_back(std::move(I)); \ + else { \ + if (I->IsDefinition) { \ + mergeInfo(I, X##s[R.first->second]); \ + X##s[R.first->second] = std::move(I); \ + return; \ + } \ + mergeInfo(X##s[R.first->second], I); \ + } \ + } + +// INSERT_FUNC(RecordInfo) +INSERT_FUNC(EnumInfo) +INSERT_FUNC(FunctionInfo) + +#undef INSERT_FUNC + +void InfoSet::insert(StringRef Key, std::unique_ptr &&I) { + auto R = InfoIndex.try_emplace(Key, NamespaceInfos.size()); + if (R.second) + NamespaceInfos.push_back(std::move(I)); + else + mergeInfo(NamespaceInfos[R.first->second], I); +} + +} // namespace doc +} // namespace clang Index: clang-doc/tool/ClangDocMain.cpp =================================================================== --- clang-doc/tool/ClangDocMain.cpp +++ clang-doc/tool/ClangDocMain.cpp @@ -13,7 +13,9 @@ //===----------------------------------------------------------------------===// #include +#include "BitcodeWriter.h" #include "ClangDoc.h" +#include "Reducer.h" #include "clang/AST/AST.h" #include "clang/AST/Decl.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -86,26 +88,32 @@ ArgAdjuster); if (Err) errs() << toString(std::move(Err)) << "\n"; + // Reducing phase + outs() << "Reducing infos...\n"; + auto Infos = doc::mergeInfos(Exec->get()->getToolResults()); + if (DumpResult) { - Exec->get()->getToolResults()->forEachResult([&](StringRef Key, - StringRef Value) { - SmallString<128> IRRootPath; - sys::path::native(OutDirectory, IRRootPath); - std::error_code DirectoryStatus = sys::fs::create_directories(IRRootPath); - if (DirectoryStatus != OK) { - errs() << "Unable to create documentation directories.\n"; - return; - } - sys::path::append(IRRootPath, Key + ".bc"); - std::error_code OutErrorInfo; - raw_fd_ostream OS(IRRootPath, OutErrorInfo, sys::fs::F_None); - if (OutErrorInfo != OK) { - errs() << "Error opening documentation file.\n"; - return; - } - OS << Value; - OS.close(); - }); + SmallString<2048> Buffer; + llvm::BitstreamWriter Stream(Buffer); + doc::ClangDocBitcodeWriter Writer(Stream); + Writer.writeBitstreamForInfoSet(*Infos); + SmallString<128> IRRootPath; + sys::path::native(OutDirectory, IRRootPath); + sys::path::append(IRRootPath, "bc"); + std::error_code DirectoryStatus = sys::fs::create_directories(IRRootPath); + if (DirectoryStatus != OK) { + errs() << "Unable to create documentation directories.\n"; + return 1; + } + sys::path::append(IRRootPath, "docs.bc"); + std::error_code OutErrorInfo; + raw_fd_ostream OS(IRRootPath, OutErrorInfo, sys::fs::F_None); + if (OutErrorInfo != OK) { + errs() << "Error opening documentation file.\n"; + return 1; + } + OS << Buffer; + OS.close(); } return 0; Index: test/clang-doc/mapper-class.cpp =================================================================== --- test/clang-doc/mapper-class.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// 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 -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@S@E.bc --dump | FileCheck %s - -class E {}; -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'E' - // CHECK: - // CHECK: -// CHECK: - Index: test/clang-doc/mapper-enum.cpp =================================================================== --- test/clang-doc/mapper-enum.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// 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 -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@E@B.bc --dump | FileCheck %s - -enum B { X, Y }; -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'B' - // CHECK: - // CHECK: - // CHECK: blob data = 'X' - // CHECK: - // CHECK: - // CHECK: blob data = 'Y' - // CHECK: -// CHECK: - - - Index: test/clang-doc/mapper-function.cpp =================================================================== --- test/clang-doc/mapper-function.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// 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 -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@F@F#I#.bc --dump | FileCheck %s - -int F(int param) { return param; } -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'F' - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: blob data = 'param' - // CHECK: -// CHECK: - Index: test/clang-doc/mapper-method.cpp =================================================================== --- test/clang-doc/mapper-method.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// 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 -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@S@G@F@Method#I#.bc --dump | FileCheck %s - -class G { -public: - int Method(int param) { return param; } -}; -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'Method' - // CHECK: - // CHECK: blob data = 'c:@S@G' - // CHECK: - // CHECK: blob data = 'int' - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: blob data = 'param' - // CHECK: -// CHECK: - - - - Index: test/clang-doc/mapper-namespace.cpp =================================================================== --- test/clang-doc/mapper-namespace.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// 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 -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@N@A.bc --dump | FileCheck %s - -namespace A {} -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'A' -// CHECK: - - Index: test/clang-doc/mapper-struct.cpp =================================================================== --- test/clang-doc/mapper-struct.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// 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 -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@S@C.bc --dump | FileCheck %s - -struct C { int i; }; -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'C' - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: blob data = 'C::i' - // CHECK: - // CHECK: -// CHECK: - - Index: test/clang-doc/mapper-union.cpp =================================================================== --- test/clang-doc/mapper-union.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// 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 -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@U@D.bc --dump | FileCheck %s - -union D { int X; int Y; }; -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'D' - // CHECK: - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: blob data = 'D::X' - // CHECK: - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: blob data = 'D::Y' - // CHECK: - // CHECK: -// CHECK: - - Index: test/clang-doc/namespace.cpp =================================================================== --- /dev/null +++ test/clang-doc/namespace.cpp @@ -0,0 +1,67 @@ +// 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 -output=%t/docs +// RUN: llvm-bcanalyzer %t/docs/bc/docs.bc --dump | FileCheck %s + +namespace A { + +void f(); +void f() {}; + +} // A + +namespace A { +namespace B { + +enum E { X }; + +E func(int i) { + return X; +} + +} +} + +// CHECK: +// CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'A' +// CHECK: +// CHECK: + // CHECK: blob data = 'B' + // CHECK: blob data = 'c:@N@A' +// CHECK: +// CHECK: + // CHECK: blob data = 'f' + // CHECK: blob data = 'c:@N@A' + // CHECK: + // CHECK: + // CHECK: blob data = 'void' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'func' + // CHECK: blob data = 'c:@N@A@N@B' + // CHECK: blob data = 'c:@N@A' + // CHECK: + // CHECK: + // CHECK: blob data = 'enum A::B::E' + // CHECK: + // CHECK: + // CHECK: blob data = 'int' + // CHECK: blob data = 'i' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'E' + // CHECK: blob data = 'c:@N@A@N@B' + // CHECK: blob data = 'c:@N@A' + // CHECK: + // CHECK: + // CHECK: blob data = 'A::B::X' + // CHECK: +// CHECK: Index: test/clang-doc/record.cpp =================================================================== --- /dev/null +++ test/clang-doc/record.cpp @@ -0,0 +1,107 @@ +// 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 -output=%t/docs +// RUN: llvm-bcanalyzer %t/docs/bc/docs.bc --dump | FileCheck %s + +union A { int X; int Y; }; + +enum B { X, Y }; + +struct C { int i; }; + +class D {}; + +class E { +public: + E() {} + ~E() {} + +protected: + void ProtectedMethod(); +}; + +void E::ProtectedMethod() {} + +class F : virtual private D, public E {}; + +// CHECK: +// CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'E' + // CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: blob data = 'void' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = '~E' + // CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: blob data = 'void' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'ProtectedMethod' + // CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: blob data = 'void' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'A' + // CHECK: + // CHECK: + // CHECK: + // CHECK: blob data = 'int' + // CHECK: blob data = 'A::X' + // CHECK: + // CHECK: + // CHECK: + // CHECK: blob data = 'int' + // CHECK: blob data = 'A::Y' + // CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'C' + // CHECK: + // CHECK: + // CHECK: blob data = 'int' + // CHECK: blob data = 'C::i' + // CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'D' + // CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'E' + // CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'F' + // CHECK: + // CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: blob data = 'c:@S@D' +// CHECK: +// CHECK: + // CHECK: blob data = 'B' + // CHECK: + // CHECK: + // CHECK: blob data = 'X' + // CHECK: + // CHECK: + // CHECK: blob data = 'Y' + // CHECK: +// CHECK: