Index: clang-doc/BitcodeReader.h =================================================================== --- /dev/null +++ clang-doc/BitcodeReader.h @@ -0,0 +1,99 @@ +//===-- 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 reader for parsing the clang-doc internal +// representation to LLVM bitcode. The reader takes in a stream of bits and +// generates the set of infos that it represents. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H + +#include "BitcodeWriter.h" +#include "Representation.h" +#include "clang/AST/AST.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Bitcode/BitstreamReader.h" + +namespace clang { +namespace doc { + +// Class to read bitstream into an InfoSet collection +class ClangDocBitcodeReader { + public: + ClangDocBitcodeReader(llvm::BitstreamCursor &Stream) : Stream(Stream) {} + + bool readBitstream(std::unique_ptr &IS); + + private: + enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; + + // Top level parsing + bool validateStream(); + bool readVersion(); + bool readBlockInfoBlock(); + + template + bool readBlockToInfo(unsigned ID, std::unique_ptr &IS, TInfo &&I); + + // Reading records + template + bool readRecord(unsigned ID, TInfo &I); + + bool parseRecord(unsigned ID, llvm::StringRef Blob, unsigned VersionNo); + bool parseRecord(unsigned ID, llvm::StringRef Blob, NamespaceInfo &I); + bool parseRecord(unsigned ID, llvm::StringRef Blob, RecordInfo &I); + bool parseRecord(unsigned ID, llvm::StringRef Blob, EnumInfo &I); + bool parseRecord(unsigned ID, llvm::StringRef Blob, FunctionInfo &I); + bool parseRecord(unsigned ID, llvm::StringRef Blob, TypeInfo &I); + bool parseRecord(unsigned ID, llvm::StringRef Blob, FieldTypeInfo &I); + bool parseRecord(unsigned ID, llvm::StringRef Blob, MemberTypeInfo &I); + bool parseRecord(unsigned ID, llvm::StringRef Blob, CommentInfo &I); + + template + void storeData(llvm::SmallString &Field, llvm::StringRef Blob); + + void storeData(bool &Field, llvm::StringRef Blob); + void storeData(int &Field, llvm::StringRef Blob); + void storeData(AccessSpecifier &Field, llvm::StringRef Blob); + void storeData(TagTypeKind &Field, llvm::StringRef Blob); + void storeData(llvm::Optional &Field, llvm::StringRef Blob); + void storeData(Reference &Field, llvm::StringRef Blob); + void storeData(llvm::SmallVectorImpl &Field, llvm::StringRef Blob); + void storeData(llvm::SmallVectorImpl &Field, llvm::StringRef Blob); + void storeData(llvm::SmallVectorImpl> &Field, + llvm::StringRef Blob); + + // Reading blocks + template + bool readBlock(unsigned ID, TInfo &I); + template + bool readSubBlock(unsigned ID, TInfo &I); + Cursor skipUntilRecordOrBlock(unsigned &BlockOrRecordID); + + // Helpers + CommentInfo &getCommentInfo(const unsigned int I); + CommentInfo &getCommentInfo(TypeInfo &I); + CommentInfo &getCommentInfo(Info &I); + CommentInfo &getCommentInfo(CommentInfo &I); + CommentInfo &getCommentInfo(std::unique_ptr &I); + + template + void addTypeInfo(TInfo &I, TTypeInfo &&T); + + llvm::BitstreamCursor &Stream; + llvm::SmallVector Record; + Optional BlockInfo; +}; + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_BITCODEREADER_H Index: clang-doc/BitcodeReader.cpp =================================================================== --- /dev/null +++ clang-doc/BitcodeReader.cpp @@ -0,0 +1,498 @@ +//===-- 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" +#include "llvm/ADT/Optional.h" + +namespace clang { +namespace doc { + +// Store data +template +void ClangDocBitcodeReader::storeData(llvm::SmallString &Field, + llvm::StringRef Blob) { + Field = Blob; +} + +void ClangDocBitcodeReader::storeData(bool &Field, llvm::StringRef Blob) { + Field = (bool)Record[0]; +} + +void ClangDocBitcodeReader::storeData(int &Field, llvm::StringRef Blob) { + Field = (int)Record[0]; +} + +void ClangDocBitcodeReader::storeData(AccessSpecifier &Field, + llvm::StringRef Blob) { + Field = (AccessSpecifier)Record[0]; +} + +void ClangDocBitcodeReader::storeData(TagTypeKind &Field, + llvm::StringRef Blob) { + Field = (TagTypeKind)Record[0]; +} + +void ClangDocBitcodeReader::storeData(llvm::Optional &Field, + llvm::StringRef Blob) { + Field.emplace((int)Record[0], Blob); +} + +void ClangDocBitcodeReader::storeData(Reference &Field, llvm::StringRef Blob) { + Field = Reference{Blob, (InfoType)Record[0]}; +} + +void ClangDocBitcodeReader::storeData( + llvm::SmallVectorImpl> &Field, llvm::StringRef Blob) { + Field.push_back(Blob); +} + +void ClangDocBitcodeReader::storeData(llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { + Field.emplace_back((int)Record[0], Blob); +} + +void ClangDocBitcodeReader::storeData(llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { + Field.emplace_back(Blob, (InfoType)Record[0]); +} + +// Read records + +template +bool ClangDocBitcodeReader::readRecord(unsigned ID, TInfo &I) { + Record.clear(); + llvm::StringRef Blob; + unsigned RecID = Stream.readRecord(ID, Record, &Blob); + return parseRecord(RecID, Blob, I); + return false; +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob, + unsigned VersionNo) { + if (ID == VERSION && Record[0] == VersionNo) return true; + return false; +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob, + NamespaceInfo &I) { + switch (ID) { + case NAMESPACE_USR: + storeData(I.USR, Blob); + return true; + case NAMESPACE_NAME: + storeData(I.Name, Blob); + return true; + case NAMESPACE_NAMESPACE: + storeData(I.Namespace, Blob); + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob, + RecordInfo &I) { + switch (ID) { + case RECORD_USR: + storeData(I.USR, Blob); + return true; + case RECORD_NAME: + storeData(I.Name, Blob); + return true; + case RECORD_NAMESPACE: + storeData(I.Namespace, Blob); + return true; + case RECORD_PARENT: + storeData(I.Parents, Blob); + return true; + case RECORD_VPARENT: + storeData(I.VirtualParents, Blob); + return true; + case RECORD_DEFLOCATION: + storeData(I.DefLoc, Blob); + return true; + case RECORD_LOCATION: + storeData(I.Loc, Blob); + return true; + case RECORD_TAG_TYPE: + storeData(I.TagType, Blob); + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob, + EnumInfo &I) { + switch (ID) { + case ENUM_USR: + storeData(I.USR, Blob); + return true; + case ENUM_NAME: + storeData(I.Name, Blob); + return true; + case ENUM_NAMESPACE: + storeData(I.Namespace, Blob); + return true; + case ENUM_DEFLOCATION: + storeData(I.DefLoc, Blob); + return true; + case ENUM_LOCATION: + storeData(I.Loc, Blob); + return true; + case ENUM_SCOPED: + storeData(I.Scoped, Blob); + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob, + FunctionInfo &I) { + switch (ID) { + case FUNCTION_USR: + storeData(I.USR, Blob); + return true; + case FUNCTION_NAME: + storeData(I.Name, Blob); + return true; + case FUNCTION_NAMESPACE: + storeData(I.Namespace, Blob); + return true; + case FUNCTION_PARENT: + storeData(I.Parent, Blob); + return true; + case FUNCTION_DEFLOCATION: + storeData(I.DefLoc, Blob); + return true; + case FUNCTION_LOCATION: + storeData(I.Loc, Blob); + return true; + case FUNCTION_ACCESS: + storeData(I.Access, Blob); + return true; + case FUNCTION_IS_METHOD: + storeData(I.IsMethod, Blob); + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob, + TypeInfo &I) { + switch (ID) { + case TYPE_REF: + storeData(I.Type, Blob); + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob, + FieldTypeInfo &I) { + switch (ID) { + case FIELD_TYPE_REF: + storeData(I.Type, Blob); + return true; + case FIELD_TYPE_NAME: + storeData(I.Name, Blob); + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob, + MemberTypeInfo &I) { + switch (ID) { + case MEMBER_TYPE_REF: + storeData(I.Type, Blob); + return true; + case MEMBER_TYPE_NAME: + storeData(I.Name, Blob); + return true; + case MEMBER_TYPE_ACCESS: + storeData(I.Access, Blob); + return true; + default: + return false; + } +} + +bool ClangDocBitcodeReader::parseRecord(unsigned ID, llvm::StringRef Blob, + CommentInfo &I) { + switch (ID) { + case COMMENT_KIND: + storeData(I.Kind, Blob); + return true; + case COMMENT_TEXT: + storeData(I.Text, Blob); + return true; + case COMMENT_NAME: + storeData(I.Name, Blob); + return true; + case COMMENT_DIRECTION: + storeData(I.Direction, Blob); + return true; + case COMMENT_PARAMNAME: + storeData(I.ParamName, Blob); + return true; + case COMMENT_CLOSENAME: + storeData(I.CloseName, Blob); + return true; + case COMMENT_ATTRKEY: + storeData(I.AttrKeys, Blob); + return true; + case COMMENT_ATTRVAL: + storeData(I.AttrValues, Blob); + return true; + case COMMENT_ARG: + storeData(I.Args, Blob); + return true; + case COMMENT_SELFCLOSING: + storeData(I.SelfClosing, Blob); + return true; + case COMMENT_EXPLICIT: + storeData(I.Explicit, Blob); + return true; + default: + return false; + } +} + +// Helpers +CommentInfo &ClangDocBitcodeReader::getCommentInfo(const unsigned int I) { + llvm::errs() << "Cannot have comment subblock.\n"; + exit(1); +} + +CommentInfo &ClangDocBitcodeReader::getCommentInfo(TypeInfo &I) { + llvm::errs() << "Cannot have comment subblock.\n"; + exit(1); +} + +CommentInfo &ClangDocBitcodeReader::getCommentInfo(Info &I) { + I.Description.emplace_back(); + return I.Description.back(); +} + +CommentInfo &ClangDocBitcodeReader::getCommentInfo(CommentInfo &I) { + I.Children.emplace_back(llvm::make_unique()); + return *I.Children.back(); +} + +CommentInfo &ClangDocBitcodeReader::getCommentInfo( + std::unique_ptr &I) { + I->Children.emplace_back(llvm::make_unique()); + return *I->Children.back(); +} + +template +void ClangDocBitcodeReader::addTypeInfo(TInfo &I, TTypeInfo &&T) { + llvm::errs() << "Invalid type for info.\n"; + exit(1); +} + +template <> +void ClangDocBitcodeReader::addTypeInfo(EnumInfo &I, TypeInfo &&T) { + I.Members.emplace_back(std::move(T)); +} + +template <> +void ClangDocBitcodeReader::addTypeInfo(RecordInfo &I, MemberTypeInfo &&T) { + I.Members.emplace_back(std::move(T)); +} + +template <> +void ClangDocBitcodeReader::addTypeInfo(FunctionInfo &I, TypeInfo &&T) { + I.ReturnType = std::move(T); +} + +template <> +void ClangDocBitcodeReader::addTypeInfo(FunctionInfo &I, FieldTypeInfo &&T) { + I.Params.emplace_back(std::move(T)); +} + +// Read blocks +template +bool ClangDocBitcodeReader::readBlock(unsigned ID, T &I) { + if (Stream.EnterSubBlock(ID)) return false; + + while (true) { + unsigned BlockOrCode = 0; + Cursor Res = skipUntilRecordOrBlock(BlockOrCode); + + switch (Res) { + case Cursor::BadBlock: + return false; + case Cursor::BlockEnd: + return true; + case Cursor::BlockBegin: + if (readSubBlock(BlockOrCode, I)) continue; + if (!Stream.SkipBlock()) return false; + continue; + case Cursor::Record: + break; + } + if (!readRecord(BlockOrCode, I)) return false; + } +} + +template +bool ClangDocBitcodeReader::readSubBlock(unsigned ID, TInfo &I) { + switch (ID) { + // Blocks can only have Comment or TypeInfo subblocks + case BI_COMMENT_BLOCK_ID: + if (readBlock(ID, getCommentInfo(I))) return true; + return false; + case BI_TYPE_BLOCK_ID: { + TypeInfo T; + if (readBlock(ID, T)) { + addTypeInfo(I, std::move(T)); + return true; + } + } + case BI_FIELD_TYPE_BLOCK_ID: { + FieldTypeInfo T; + if (readBlock(ID, T)) { + addTypeInfo(I, std::move(T)); + return true; + } + } + case BI_MEMBER_TYPE_BLOCK_ID: { + MemberTypeInfo T; + if (readBlock(ID, T)) { + addTypeInfo(I, std::move(T)); + return true; + } + return false; + } + default: + llvm::errs() << "Invalid subblock type.\n"; + return false; + } +} + +ClangDocBitcodeReader::Cursor ClangDocBitcodeReader::skipUntilRecordOrBlock( + 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."); +} + +// Validation and overview + +bool ClangDocBitcodeReader::validateStream() { + 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() { + BlockInfo = Stream.ReadBlockInfoBlock(); + if (!BlockInfo) return false; + Stream.setBlockInfo(&*BlockInfo); + return true; +} + +// Entry point + +bool ClangDocBitcodeReader::readBitstream(std::unique_ptr &IS) { + if (!validateStream()) return false; + + // Read the top level blocks. + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + if (Code != llvm::bitc::ENTER_SUBBLOCK) return false; + + unsigned ID = Stream.ReadSubBlockID(); + switch (ID) { + // 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: + llvm::errs() << "Invalid top level block.\n"; + return false; + case BI_VERSION_BLOCK_ID: + if (readBlock(ID, VersionNumber)) continue; + return false; + case llvm::bitc::BLOCKINFO_BLOCK_ID: + if (readBlockInfoBlock()) continue; + return false; + case BI_NAMESPACE_BLOCK_ID: { + NamespaceInfo I; + if (!readBlockToInfo(ID, IS, std::move(I))) return false; + continue; + } + case BI_RECORD_BLOCK_ID: { + RecordInfo I; + if (!readBlockToInfo(ID, IS, std::move(I))) return false; + continue; + } + case BI_ENUM_BLOCK_ID: { + EnumInfo I; + if (!readBlockToInfo(ID, IS, std::move(I))) return false; + continue; + } + case BI_FUNCTION_BLOCK_ID: { + FunctionInfo I; + if (!readBlockToInfo(ID, IS, std::move(I))) return false; + continue; + } + default: + if (!Stream.SkipBlock()) return false; + continue; + } + } + return true; +} + +template +bool ClangDocBitcodeReader::readBlockToInfo(unsigned ID, + std::unique_ptr &IS, + TInfo &&I) { + if (!readBlock(ID, I)) { + llvm::errs() << "Error reading from block.\n"; + return false; + } + IS->insert(I.USR, std::move(I)); + return true; +} + +} // namespace doc +} // namespace clang Index: clang-doc/BitcodeWriter.h =================================================================== --- clang-doc/BitcodeWriter.h +++ clang-doc/BitcodeWriter.h @@ -64,7 +64,9 @@ // New Ids need to be added to the enum here, and to the relevant IdNameMap and // initialization list in the implementation file. -#define INFORECORDS(X) X##_USR, X##_NAME, X##_NAMESPACE +#define INFORECORDS(X) \ + X##_USR, X##_NAME, X##_NAMESPACE, X##_NAMESPACES, X##_RECORDS, \ + X##_FUNCTIONS, X##_ENUMS enum RecordId { VERSION = 1, @@ -175,6 +177,7 @@ template void emitBlock(const T &I); + void emitInfoSet(std::unique_ptr &ISet); private: class AbbreviationMap { Index: clang-doc/BitcodeWriter.cpp =================================================================== --- clang-doc/BitcodeWriter.cpp +++ clang-doc/BitcodeWriter.cpp @@ -149,12 +149,20 @@ {NAMESPACE_USR, {"USR", &StringAbbrev}}, {NAMESPACE_NAME, {"Name", &StringAbbrev}}, {NAMESPACE_NAMESPACE, {"Namespace", &ReferenceAbbrev}}, + {NAMESPACE_NAMESPACES, {"Namespaces", &ReferenceAbbrev}}, + {NAMESPACE_RECORDS, {"Records", &ReferenceAbbrev}}, + {NAMESPACE_FUNCTIONS, {"Functions", &ReferenceAbbrev}}, + {NAMESPACE_ENUMS, {"Enums", &ReferenceAbbrev}}, {ENUM_USR, {"USR", &StringAbbrev}}, {ENUM_NAME, {"Name", &StringAbbrev}}, {ENUM_NAMESPACE, {"Namespace", &ReferenceAbbrev}}, {ENUM_DEFLOCATION, {"DefLocation", &LocationAbbrev}}, {ENUM_LOCATION, {"Location", &LocationAbbrev}}, {ENUM_SCOPED, {"Scoped", &BoolAbbrev}}, + {ENUM_NAMESPACES, {"Namespaces", &ReferenceAbbrev}}, + {ENUM_RECORDS, {"Records", &ReferenceAbbrev}}, + {ENUM_FUNCTIONS, {"Functions", &ReferenceAbbrev}}, + {ENUM_ENUMS, {"Enums", &ReferenceAbbrev}}, {RECORD_USR, {"USR", &StringAbbrev}}, {RECORD_NAME, {"Name", &StringAbbrev}}, {RECORD_NAMESPACE, {"Namespace", &ReferenceAbbrev}}, @@ -163,6 +171,10 @@ {RECORD_TAG_TYPE, {"TagType", &IntAbbrev}}, {RECORD_PARENT, {"Parent", &ReferenceAbbrev}}, {RECORD_VPARENT, {"VParent", &ReferenceAbbrev}}, + {RECORD_NAMESPACES, {"Namespaces", &ReferenceAbbrev}}, + {RECORD_RECORDS, {"Records", &ReferenceAbbrev}}, + {RECORD_FUNCTIONS, {"Functions", &ReferenceAbbrev}}, + {RECORD_ENUMS, {"Enums", &ReferenceAbbrev}}, {FUNCTION_USR, {"USR", &StringAbbrev}}, {FUNCTION_NAME, {"Name", &StringAbbrev}}, {FUNCTION_NAMESPACE, {"Namespace", &ReferenceAbbrev}}, @@ -170,13 +182,17 @@ {FUNCTION_LOCATION, {"Location", &LocationAbbrev}}, {FUNCTION_PARENT, {"Parent", &ReferenceAbbrev}}, {FUNCTION_ACCESS, {"Access", &IntAbbrev}}, - {FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}}}; - // assert(Inits.size() == RecordIdCount); + {FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}}, + {FUNCTION_NAMESPACES, {"Namespaces", &ReferenceAbbrev}}, + {FUNCTION_RECORDS, {"Records", &ReferenceAbbrev}}, + {FUNCTION_FUNCTIONS, {"Functions", &ReferenceAbbrev}}, + {FUNCTION_ENUMS, {"Enums", &ReferenceAbbrev}}}; + assert(Inits.size() == RecordIdCount); for (const auto &Init : Inits) { RecordIdNameMap[Init.first] = Init.second; assert((Init.second.Name.size() + 1) <= BitCodeConstants::RecordSize); } - // assert(RecordIdNameMap.size() == RecordIdCount); + assert(RecordIdNameMap.size() == RecordIdCount); return RecordIdNameMap; }(); @@ -200,19 +216,24 @@ // Enum Block {BI_ENUM_BLOCK_ID, {ENUM_USR, ENUM_NAME, ENUM_NAMESPACE, ENUM_DEFLOCATION, ENUM_LOCATION, - ENUM_SCOPED}}, + ENUM_SCOPED, ENUM_NAMESPACES, ENUM_RECORDS, ENUM_FUNCTIONS, + ENUM_ENUMS}}, // Namespace Block {BI_NAMESPACE_BLOCK_ID, - {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_NAMESPACE}}, + {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_NAMESPACE, + NAMESPACE_NAMESPACES, NAMESPACE_RECORDS, NAMESPACE_FUNCTIONS, + NAMESPACE_ENUMS}}, // Record Block {BI_RECORD_BLOCK_ID, {RECORD_USR, RECORD_NAME, RECORD_NAMESPACE, RECORD_DEFLOCATION, - RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_PARENT, RECORD_VPARENT}}, + RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_PARENT, RECORD_VPARENT, + RECORD_NAMESPACES, RECORD_RECORDS, RECORD_FUNCTIONS, RECORD_ENUMS}}, // Function Block {BI_FUNCTION_BLOCK_ID, {FUNCTION_USR, FUNCTION_NAME, FUNCTION_NAMESPACE, FUNCTION_DEFLOCATION, FUNCTION_LOCATION, FUNCTION_PARENT, FUNCTION_ACCESS, - FUNCTION_IS_METHOD}}}; + FUNCTION_IS_METHOD, FUNCTION_NAMESPACES, FUNCTION_RECORDS, + FUNCTION_FUNCTIONS, FUNCTION_ENUMS}}}; // AbbreviationMap @@ -403,11 +424,15 @@ for (const auto &C : I.Children) emitBlock(*C); } -#define EMITINFO(X) \ - emitRecord(I.USR, X##_USR); \ - emitRecord(I.Name, X##_NAME); \ - for (const auto &N : I.Namespace) emitRecord(N, X##_NAMESPACE); \ - for (const auto &CI : I.Description) emitBlock(CI); +#define EMITINFO(X) \ + emitRecord(I.USR, X##_USR); \ + emitRecord(I.Name, X##_NAME); \ + for (const auto &N : I.Namespace) emitRecord(N, X##_NAMESPACE); \ + for (const auto &CI : I.Description) emitBlock(CI); \ + for (const auto &R : I.Namespaces) emitRecord(R, X##_NAMESPACES); \ + for (const auto &R : I.Records) emitRecord(R, X##_RECORDS); \ + for (const auto &R : I.Functions) emitRecord(R, X##_FUNCTIONS); \ + for (const auto &R : I.Enums) emitRecord(R, X##_ENUMS); void ClangDocBitcodeWriter::emitBlockContent(const NamespaceInfo &I) { EMITINFO(NAMESPACE) @@ -443,5 +468,12 @@ #undef EMITINFO +void ClangDocBitcodeWriter::emitInfoSet(std::unique_ptr &ISet) { + for (const auto &I : ISet->getNamespaceInfos()) emitBlock(I); + for (const auto &I : ISet->getFunctionInfos()) emitBlock(I); + for (const auto &I : ISet->getRecordInfos()) emitBlock(I); + for (const auto &I : ISet->getEnumInfos()) emitBlock(I); +} + } // namespace doc } // namespace clang Index: clang-doc/CMakeLists.txt =================================================================== --- clang-doc/CMakeLists.txt +++ clang-doc/CMakeLists.txt @@ -3,9 +3,12 @@ ) add_clang_library(clangDoc + BitcodeReader.cpp BitcodeWriter.cpp Mapper.cpp Serialize.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_REDUCER_H +#define LLVM_CLANG_TOOLS_EXTRA_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_REDUCER_H Index: clang-doc/Reducer.cpp =================================================================== --- /dev/null +++ clang-doc/Reducer.cpp @@ -0,0 +1,34 @@ +///===-- 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 "Reducer.h" +#include "BitcodeReader.h" +#include "Representation.h" + +namespace clang { +namespace doc { + +std::unique_ptr mergeInfos(tooling::ToolResults *Results) { + std::unique_ptr UniqueInfos = llvm::make_unique(); + bool Err = false; + Results->forEachResult([&](StringRef Key, StringRef Value) { + llvm::BitstreamCursor Stream(Value); + ClangDocBitcodeReader Reader(Stream); + if (!Reader.readBitstream(UniqueInfos)) { + Err = true; + return; + } + }); + if (Err) return nullptr; + UniqueInfos->createReferences(); + return UniqueInfos; +} + +} // namespace doc +} // namespace clang Index: clang-doc/Representation.h =================================================================== --- clang-doc/Representation.h +++ clang-doc/Representation.h @@ -34,10 +34,17 @@ IT_default }; +enum class TypeInfoType { + TT_type, + TT_field_type, + TT_member_type, +}; + // A representation of a parsed comment. struct CommentInfo { CommentInfo() = default; CommentInfo(CommentInfo &&Other) : Children(std::move(Other.Children)) {} + SmallString<16> Kind; SmallString<64> Text; SmallString<16> Name; @@ -58,6 +65,7 @@ SmallString<16> USR; InfoType RefType = InfoType::IT_default; + Info *Ref; }; // TODO: Pull the CommentInfo for a type out of the info's CommentInfo. @@ -101,13 +109,26 @@ /// A base struct for Infos. struct Info { Info() = default; - Info(Info &&Other) : Description(std::move(Other.Description)) {} - virtual ~Info() = default; + Info(Info &&Other) + : USR(Other.USR), + Name(Other.Name), + Namespace(std::move(Other.Namespace)), + Description(std::move(Other.Description)), + Namespaces(std::move(Other.Namespaces)), + Records(std::move(Other.Records)), + Functions(std::move(Other.Functions)), + Enums(std::move(Other.Enums)) {} SmallString<16> USR; SmallString<16> Name; llvm::SmallVector Namespace; std::vector Description; + + // References to be populated after reducer phase. + llvm::SmallVector Namespaces; + llvm::SmallVector Records; + llvm::SmallVector Functions; + llvm::SmallVector Enums; }; struct NamespaceInfo : public Info {}; @@ -146,6 +167,47 @@ // TODO: Add functionality to include separate markdown pages. +class InfoSet { + public: + InfoSet() = default; + + void insert(StringRef Key, NamespaceInfo &&I); + void insert(StringRef Key, RecordInfo &&I); + void insert(StringRef Key, EnumInfo &&I); + void insert(StringRef Key, FunctionInfo &&I); + + /// Returns the info with a Key, if it exists. Valid until next insert(). + template + T *find(StringRef Key); + + /// Populate the type references in all infos + void createReferences(); + + std::vector &getNamespaceInfos() { return NamespaceInfos; } + std::vector &getFunctionInfos() { return FunctionInfos; } + std::vector &getRecordInfos() { return RecordInfos; } + std::vector &getEnumInfos() { return EnumInfos; } + + private: + void resolveReferences(llvm::SmallVectorImpl &References, + Reference &Caller); + void resolveReferences(llvm::SmallVectorImpl &References, + Reference &Caller); + void resolveReferences(llvm::SmallVectorImpl &References, + Reference &Caller); + void resolveReferences(llvm::SmallVectorImpl &References, + Reference &Caller); + void resolveReferences(TypeInfo &TI, Reference &Caller); + void resolveReferences(Reference &Ref, Reference &Caller); + void addCaller(Info *I, Reference &Caller); + + 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,205 @@ +///===-- 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" + +using namespace llvm; + +namespace clang { +namespace doc { + +static void mergeInfoBase(Info &L, Info &R) { + assert(L.USR == R.USR); + assert(L.Name == R.Name); + if (L.Namespace.empty()) std::move(R.Namespace); + std::move(R.Description.begin(), R.Description.end(), + std::back_inserter(L.Description)); +} + +static void mergeSymbolInfoBase(SymbolInfo &L, SymbolInfo &R) { + mergeInfoBase(L, R); + if (R.DefLoc) + L.DefLoc = std::move(R.DefLoc); + L.Loc.insert(L.Loc.end(), R.Loc.begin(), R.Loc.end()); +} + +static void mergeInfo(NamespaceInfo &L, NamespaceInfo &R) { + mergeInfoBase(L, R); +} + +static void mergeInfo(RecordInfo &L, RecordInfo &R) { + mergeSymbolInfoBase(L, R); + assert(L.TagType == R.TagType); + if (L.Members.empty()) L.Members = std::move(R.Members); + if (L.Parents.empty()) L.Parents = std::move(R.Parents); + if (L.VirtualParents.empty()) L.VirtualParents = std::move(R.VirtualParents); +} + +static void mergeInfo(EnumInfo &L, EnumInfo &R) { + mergeSymbolInfoBase(L, R); + assert(L.Scoped == R.Scoped); + if (L.Members.empty()) L.Members = std::move(R.Members); +} + +static void mergeInfo(FunctionInfo &L, FunctionInfo &R) { + mergeSymbolInfoBase(L, R); + assert(L.ReturnType.Type.USR == R.ReturnType.Type.USR); + assert(L.Access == R.Access); + if (L.Parent.USR.empty()) { + L.Parent.USR = R.Parent.USR; + L.Parent.RefType = R.Parent.RefType; + } + if (L.Params.empty()) L.Params = std::move(R.Params); +} + +#define FIND_FUNC(X) \ + template <> \ + X *InfoSet::find(StringRef Key) { \ + auto I = InfoIndex.find(Key); \ + if (I == InfoIndex.end()) return nullptr; \ + return &X##s[I->second]; \ + } + +FIND_FUNC(NamespaceInfo) +FIND_FUNC(RecordInfo) +FIND_FUNC(EnumInfo) +FIND_FUNC(FunctionInfo) + +#undef FIND_FUNC + +#define INSERT_FUNC(X) \ + void InfoSet::insert(StringRef Key, X &&I) { \ + auto R = InfoIndex.try_emplace(Key, X##s.size()); \ + if (R.second) \ + X##s.push_back(std::move(I)); \ + else \ + mergeInfo(X##s[R.first->second], I); \ + } + +INSERT_FUNC(NamespaceInfo) +INSERT_FUNC(RecordInfo) +INSERT_FUNC(EnumInfo) +INSERT_FUNC(FunctionInfo) + +#undef INSERT_FUNC + +void InfoSet::createReferences() { + Reference Caller; + for (auto &I : NamespaceInfos) { + Caller.USR = I.USR; + Caller.RefType = InfoType::IT_namespace; + Caller.Ref = &I; + resolveReferences(I.Namespace, Caller); + } + for (auto &I : FunctionInfos) { + Caller.USR = I.USR; + Caller.RefType = InfoType::IT_function; + Caller.Ref = &I; + resolveReferences(I.Namespace, Caller); + resolveReferences(I.Parent, Caller); + resolveReferences(I.ReturnType, Caller); + resolveReferences(I.Params, Caller); + } + for (auto &I : RecordInfos) { + Caller.USR = I.USR; + Caller.RefType = InfoType::IT_record; + Caller.Ref = &I; + resolveReferences(I.Namespace, Caller); + resolveReferences(I.Members, Caller); + resolveReferences(I.Parents, Caller); + resolveReferences(I.VirtualParents, Caller); + } + for (auto &I : EnumInfos) { + Caller.USR = I.USR; + Caller.RefType = InfoType::IT_enum; + Caller.Ref = &I; + resolveReferences(I.Namespace, Caller); + resolveReferences(I.Members, Caller); + } +} + +void InfoSet::resolveReferences(llvm::SmallVectorImpl &References, + Reference &Caller) { + for (auto &Ref : References) resolveReferences(Ref, Caller); +} + +void InfoSet::resolveReferences(llvm::SmallVectorImpl &References, + Reference &Caller) { + for (auto &Ref : References) resolveReferences(Ref.Type, Caller); +} + +void InfoSet::resolveReferences(llvm::SmallVectorImpl &References, + Reference &Caller) { + for (auto &Ref : References) resolveReferences(Ref.Type, Caller); +} + +void InfoSet::resolveReferences( + llvm::SmallVectorImpl &References, Reference &Caller) { + for (auto &Ref : References) resolveReferences(Ref.Type, Caller); +} + +void InfoSet::resolveReferences(TypeInfo &TI, Reference &Caller) { + resolveReferences(TI.Type, Caller); +} + +void InfoSet::resolveReferences(Reference &Ref, Reference &Caller) { + switch (Ref.RefType) { + case InfoType::IT_namespace: + if (auto *I = find(Ref.USR)) { + Ref.Ref = I; + addCaller(I, Caller); + } + return; + case InfoType::IT_record: + if (auto *I = find(Ref.USR)) { + Ref.Ref = I; + addCaller(I, Caller); + } + return; + case InfoType::IT_function: + if (auto *I = find(Ref.USR)) { + Ref.Ref = I; + addCaller(I, Caller); + } + return; + case InfoType::IT_enum: + if (auto *I = find(Ref.USR)) { + Ref.Ref = I; + addCaller(I, Caller); + } + return; + case InfoType::IT_default: + // We don't resolve non-USR references. + return; + } +} + +void InfoSet::addCaller(Info *I, Reference &Caller) { + switch (Caller.RefType) { + case InfoType::IT_namespace: + I->Namespaces.push_back(Caller); + return; + case InfoType::IT_record: + I->Records.push_back(Caller); + return; + case InfoType::IT_function: + I->Functions.push_back(Caller); + return; + case InfoType::IT_enum: + I->Enums.push_back(Caller); + return; + default: + // We don't resolve non-USR references. + return; + } +} + +} // namespace doc +} // namespace clang Index: clang-doc/tool/ClangDocMain.cpp =================================================================== --- clang-doc/tool/ClangDocMain.cpp +++ clang-doc/tool/ClangDocMain.cpp @@ -19,7 +19,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" @@ -52,6 +54,10 @@ "dump-mapper", llvm::cl::desc("Dump mapper results to bitcode file."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory)); +static llvm::cl::opt DumpResult( + "dump", llvm::cl::desc("Dump intermediate results to bitcode file."), + llvm::cl::init(false), llvm::cl::cat(ClangDocCategory)); + static llvm::cl::opt OmitFilenames( "omit-filenames", llvm::cl::desc("Omit filenames in output."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory)); @@ -112,5 +118,39 @@ }); } + // Reducing phase + llvm::outs() << "Reducing infos...\n"; + auto Infos = doc::mergeInfos(Exec->get()->getToolResults()); + if (!Infos) { + llvm::errs() << "Error reducing infos.\n"; + return 1; + } + + if (DumpResult) { + llvm::outs() << "Writing intermediate results...\n"; + SmallString<2048> Buffer; + llvm::BitstreamWriter Stream(Buffer); + doc::ClangDocBitcodeWriter Writer(Stream, OmitFilenames); + Writer.emitInfoSet(Infos); + SmallString<128> IRRootPath; + llvm::sys::path::native(OutDirectory, IRRootPath); + llvm::sys::path::append(IRRootPath, "bc"); + std::error_code DirectoryStatus = + llvm::sys::fs::create_directories(IRRootPath); + if (DirectoryStatus != OK) { + llvm::errs() << "Unable to create documentation directories.\n"; + return 1; + } + llvm::sys::path::append(IRRootPath, "docs.bc"); + std::error_code OutErrorInfo; + llvm::raw_fd_ostream OS(IRRootPath, OutErrorInfo, llvm::sys::fs::F_None); + if (OutErrorInfo != OK) { + llvm::errs() << "Error opening documentation file.\n"; + return 1; + } + OS << Buffer; + OS.close(); + } + return 0; } Index: test/clang-doc/bc-comment.cpp =================================================================== --- /dev/null +++ test/clang-doc/bc-comment.cpp @@ -0,0 +1,197 @@ +// 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 + +/// \brief Brief description. +/// +/// Extended description that +/// continues onto the next line. +/// +///
    +///
  • Testing. +///
+/// +/// \verbatim +/// The description continues. +/// \endverbatim +/// +/// \param [out] I is a parameter. +/// \param J is a parameter. +/// \return void +void F(int I, int J); + +/// Bonus comment on definition +void F(int I, int J) {} + +// CHECK: +// CHECK-NEXT: + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '7574630614A535710E5A6ABCFFF98BCA2D06A4CA' + // CHECK-NEXT: blob data = 'F' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'FullComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'BlockCommandComment' + // CHECK-NEXT: blob data = 'brief' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: blob data = ' Brief description.' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: blob data = ' Extended description that' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: blob data = ' continues onto the next line.' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'HTMLStartTagComment' + // CHECK-NEXT: blob data = 'ul' + // CHECK-NEXT: blob data = 'class' + // CHECK-NEXT: blob data = 'test' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'HTMLStartTagComment' + // CHECK-NEXT: blob data = 'li' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: blob data = ' Testing.' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'HTMLEndTagComment' + // CHECK-NEXT: blob data = 'ul' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'VerbatimBlockComment' + // CHECK-NEXT: blob data = 'verbatim' + // CHECK-NEXT: blob data = 'endverbatim' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'VerbatimBlockLineComment' + // CHECK-NEXT: blob data = ' The description continues.' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParamCommandComment' + // CHECK-NEXT: blob data = '[out]' + // CHECK-NEXT: blob data = 'I' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: blob data = ' is a parameter.' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParamCommandComment' + // CHECK-NEXT: blob data = '[in]' + // CHECK-NEXT: blob data = 'J' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: blob data = ' is a parameter.' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'BlockCommandComment' + // CHECK-NEXT: blob data = 'return' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: blob data = ' void' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'int' + // CHECK-NEXT: blob data = 'I' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'int' + // CHECK-NEXT: blob data = 'J' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '7574630614A535710E5A6ABCFFF98BCA2D06A4CA' + // CHECK-NEXT: blob data = 'F' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'FullComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ParagraphComment' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'TextComment' + // CHECK-NEXT: blob data = ' Bonus comment on definition' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'int' + // CHECK-NEXT: blob data = 'I' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'int' + // CHECK-NEXT: blob data = 'J' + // CHECK-NEXT: +// CHECK-NEXT: Index: test/clang-doc/bc-namespace.cpp =================================================================== --- /dev/null +++ test/clang-doc/bc-namespace.cpp @@ -0,0 +1,81 @@ +// 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(); + +} // namespace A + +namespace A { + +void f(){}; + +namespace B { + +enum E { X }; + +E func(int i) { return X; } + +} // namespace B +} // namespace A + +// CHECK: +// CHECK-NEXT: + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: blob data = 'A' +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: blob data = 'A' +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' + // CHECK-NEXT: blob data = 'B' + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC' + // CHECK-NEXT: blob data = 'f' + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC' + // CHECK-NEXT: blob data = 'f' + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '9A82CB33ED0FDF81EE383D31CD0957D153C5E840' + // CHECK-NEXT: blob data = 'func' + // CHECK-NEXT: blob data = 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'enum A::B::E' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'int' + // CHECK-NEXT: blob data = 'i' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = 'E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775' + // CHECK-NEXT: blob data = 'E' + // CHECK-NEXT: blob data = 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'A::B::X' + // CHECK-NEXT: +// CHECK-NEXT: Index: test/clang-doc/bc-record.cpp =================================================================== --- /dev/null +++ test/clang-doc/bc-record.cpp @@ -0,0 +1,168 @@ +// 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 }; + +enum class Bc { A, B }; + +struct C { int i; }; + +class D {}; + +class E { +public: + E() {} + ~E() {} + +protected: + void ProtectedMethod(); +}; + +void E::ProtectedMethod() {} + +class F : virtual private D, public E {}; + +class X { + class Y {}; +}; + +void H() { + class I {}; +} + +// CHECK: +// CHECK-NEXT: + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = 'DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4' + // CHECK-NEXT: blob data = 'E' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = 'BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17' + // CHECK-NEXT: blob data = '~E' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '5093D428CDC62096A67547BA52566E4FB9404EEE' + // CHECK-NEXT: blob data = 'ProtectedMethod' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '5093D428CDC62096A67547BA52566E4FB9404EEE' + // CHECK-NEXT: blob data = 'ProtectedMethod' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' + // CHECK-NEXT: blob data = 'H' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = 'ACE81AFA6627B4CEF2B456FB6E1252925674AF7E' + // CHECK-NEXT: blob data = 'A' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'int' + // CHECK-NEXT: blob data = 'A::X' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'int' + // CHECK-NEXT: blob data = 'A::Y' + // CHECK-NEXT: + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '06B5F6A19BA9F6A832E127C9968282B94619B210' + // CHECK-NEXT: blob data = 'C' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'int' + // CHECK-NEXT: blob data = 'C::i' + // CHECK-NEXT: + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '0921737541208B8FA9BB42B60F78AC1D779AA054' + // CHECK-NEXT: blob data = 'D' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: blob data = 'E' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = 'E3B54702FABFF4037025BA194FC27C47006330B5' + // CHECK-NEXT: blob data = 'F' + // CHECK-NEXT: + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: blob data = '0921737541208B8FA9BB42B60F78AC1D779AA054' +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' + // CHECK-NEXT: blob data = 'X' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '641AB4A3D36399954ACDE29C7A8833032BF40472' + // CHECK-NEXT: blob data = 'Y' + // CHECK-NEXT: blob data = 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '126F2D5DA516D4A749E50A062AAF4296F8D69CE1' + // CHECK-NEXT: blob data = 'I' + // CHECK-NEXT: blob data = 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = 'FC07BD34D5E77782C263FA944447929EA8753740' + // CHECK-NEXT: blob data = 'B' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'X' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'Y' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: blob data = '1E3438A08BA22025C0B46289FF0686F92C8924C5' + // CHECK-NEXT: blob data = 'Bc' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'Bc::A' + // CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'Bc::B' + // CHECK-NEXT: +// CHECK-NEXT: