Index: clang-doc/BitcodeReader.h =================================================================== --- /dev/null +++ clang-doc/BitcodeReader.h @@ -0,0 +1,95 @@ +//===-- 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(InfoSet &IS); + +private: + enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; + + // Top level parsing + bool validateStream(); + bool readVersion(); + bool readBlockInfoBlock(); + + template + bool readBlockToInfo(unsigned ID, InfoSet &IS); + + // 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); + + void storeData(llvm::SmallVectorImpl &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(SymbolID &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,567 @@ +//===-- 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" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace doc { + +// Store data +void ClangDocBitcodeReader::storeData(llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { + Field.assign(Blob.begin(), Blob.end()); +} + +void ClangDocBitcodeReader::storeData(SymbolID &Field, llvm::StringRef Blob) { + assert(Record[0] == 20); + // First position in the record is the length of the following array, so we + // copy the following elements to the field. + for (int I = 0, E = Record[0]; I < E; ++I) + Field[I] = Record[I + 1]; +} + +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) { + InfoType IT = (InfoType)Record[0]; + if (IT == InfoType::IT_default) + Field = Reference(Blob); + else + Field = Reference(StringToSymbol(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) { + InfoType IT = (InfoType)Record[0]; + if (IT == InfoType::IT_default) + Field.emplace_back(Blob); + else + Field.emplace_back(StringToSymbol(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; + case NAMESPACE_NAMESPACES: + storeData(I.Namespaces, Blob); + return true; + case NAMESPACE_RECORDS: + storeData(I.Records, Blob); + return true; + case NAMESPACE_FUNCTIONS: + storeData(I.Functions, Blob); + return true; + case NAMESPACE_ENUMS: + storeData(I.Enums, 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_NAMESPACES: + storeData(I.Namespaces, Blob); + return true; + case RECORD_RECORDS: + storeData(I.Records, Blob); + return true; + case RECORD_FUNCTIONS: + storeData(I.Functions, Blob); + return true; + case RECORD_ENUMS: + storeData(I.Enums, 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_NAMESPACES: + storeData(I.Namespaces, Blob); + return true; + case ENUM_RECORDS: + storeData(I.Records, Blob); + return true; + case ENUM_FUNCTIONS: + storeData(I.Functions, Blob); + return true; + case ENUM_ENUMS: + storeData(I.Enums, Blob); + return true; + case ENUM_DEFLOCATION: + storeData(I.DefLoc, Blob); + return true; + case ENUM_LOCATION: + storeData(I.Loc, Blob); + return true; + case ENUM_MEMBER: + storeData(I.Members, 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_NAMESPACES: + storeData(I.Namespaces, Blob); + return true; + case FUNCTION_RECORDS: + storeData(I.Records, Blob); + return true; + case FUNCTION_FUNCTIONS: + storeData(I.Functions, Blob); + return true; + case FUNCTION_ENUMS: + storeData(I.Enums, 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(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(InfoSet &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: + if (!readBlockToInfo(ID, IS)) + return false; + continue; + case BI_RECORD_BLOCK_ID: + if (!readBlockToInfo(ID, IS)) + return false; + continue; + case BI_ENUM_BLOCK_ID: + if (!readBlockToInfo(ID, IS)) + return false; + continue; + case BI_FUNCTION_BLOCK_ID: + if (!readBlockToInfo(ID, IS)) + return false; + continue; + default: + if (!Stream.SkipBlock()) + return false; + continue; + } + } + return true; +} + +template +bool ClangDocBitcodeReader::readBlockToInfo(unsigned ID, InfoSet &IS) { + TInfo I; + if (!readBlock(ID, I)) { + llvm::errs() << "Error reading from block.\n"; + return false; + } + IS.insert(std::move(I)); + return true; +} + +} // namespace doc +} // namespace clang Index: clang-doc/BitcodeWriter.h =================================================================== --- clang-doc/BitcodeWriter.h +++ clang-doc/BitcodeWriter.h @@ -65,7 +65,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, @@ -137,6 +139,7 @@ void emitBlock(const FieldTypeInfo &B); void emitBlock(const MemberTypeInfo &B); void emitBlock(const CommentInfo &B); + void emitInfoSet(InfoSet &ISet); private: class AbbreviationMap { Index: clang-doc/BitcodeWriter.cpp =================================================================== --- clang-doc/BitcodeWriter.cpp +++ clang-doc/BitcodeWriter.cpp @@ -161,6 +161,10 @@ {NAMESPACE_USR, {"USR", &SymbolIDAbbrev}}, {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", &SymbolIDAbbrev}}, {ENUM_NAME, {"Name", &StringAbbrev}}, {ENUM_NAMESPACE, {"Namespace", &ReferenceAbbrev}}, @@ -168,6 +172,10 @@ {ENUM_LOCATION, {"Location", &LocationAbbrev}}, {ENUM_MEMBER, {"Member", &StringAbbrev}}, {ENUM_SCOPED, {"Scoped", &BoolAbbrev}}, + {ENUM_NAMESPACES, {"Namespaces", &ReferenceAbbrev}}, + {ENUM_RECORDS, {"Records", &ReferenceAbbrev}}, + {ENUM_FUNCTIONS, {"Functions", &ReferenceAbbrev}}, + {ENUM_ENUMS, {"Enums", &ReferenceAbbrev}}, {RECORD_USR, {"USR", &SymbolIDAbbrev}}, {RECORD_NAME, {"Name", &StringAbbrev}}, {RECORD_NAMESPACE, {"Namespace", &ReferenceAbbrev}}, @@ -176,6 +184,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", &SymbolIDAbbrev}}, {FUNCTION_NAME, {"Name", &StringAbbrev}}, {FUNCTION_NAMESPACE, {"Namespace", &ReferenceAbbrev}}, @@ -183,7 +195,11 @@ {FUNCTION_LOCATION, {"Location", &LocationAbbrev}}, {FUNCTION_PARENT, {"Parent", &ReferenceAbbrev}}, {FUNCTION_ACCESS, {"Access", &IntAbbrev}}, - {FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}}}; + {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; @@ -212,19 +228,24 @@ // Enum Block {BI_ENUM_BLOCK_ID, {ENUM_USR, ENUM_NAME, ENUM_NAMESPACE, ENUM_DEFLOCATION, ENUM_LOCATION, - ENUM_MEMBER, ENUM_SCOPED}}, + ENUM_MEMBER, 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 @@ -428,8 +449,7 @@ void ClangDocBitcodeWriter::emitBlock(const CommentInfo &I) { StreamSubBlockGuard Block(Stream, BI_COMMENT_BLOCK_ID); - for (const auto &L : - std::vector>{ + for (const auto &L : std::vector>{ {I.Kind, COMMENT_KIND}, {I.Text, COMMENT_TEXT}, {I.Name, COMMENT_NAME}, @@ -455,7 +475,15 @@ for (const auto &N : I.Namespace) \ emitRecord(N, X##_NAMESPACE); \ for (const auto &CI : I.Description) \ - emitBlock(CI); + 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::emitBlock(const NamespaceInfo &I) { StreamSubBlockGuard Block(Stream, BI_NAMESPACE_BLOCK_ID); @@ -506,5 +534,12 @@ #undef EMITINFO +void ClangDocBitcodeWriter::emitInfoSet(InfoSet &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 ClangDoc.cpp Mapper.cpp + Reducer.cpp + Representation.cpp Serialize.cpp LINK_LIBS 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,35 @@ +///===-- 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 @@ -17,6 +17,7 @@ #include "clang/AST/Type.h" #include "clang/Basic/Specifiers.h" +#include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -26,7 +27,9 @@ namespace clang { namespace doc { +// SHA1'd hash of a USR. using SymbolID = std::array; +SymbolID StringToSymbol(llvm::StringRef StringUSR); struct Info; enum class InfoType { @@ -37,18 +40,30 @@ 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; // Kind of comment (TextComment, InlineCommandComment, - // HTMLStartTagComment, HTMLEndTagComment, BlockCommandComment, - // ParamCommandComment, TParamCommandComment, VerbatimBlockComment, - // VerbatimBlockLineComment, VerbatimLineComment). - SmallString<64> Text; // Text of the comment. - SmallString<16> Name; // Name of the comment (for Verbatim and HTML). + CommentInfo(CommentInfo &&Other) + : Text(Other.Text), Name(Other.Name), Direction(Other.Direction), + ParamName(Other.ParamName), CloseName(Other.CloseName), + SelfClosing(Other.SelfClosing), Explicit(Other.Explicit), + AttrKeys(std::move(Other.AttrKeys)), + AttrValues(std::move(Other.AttrValues)), Args(std::move(Other.Args)), + Children(std::move(Other.Children)) {} + + SmallString<16> Kind; // Kind of comment (TextComment, InlineCommandComment, + // HTMLStartTagComment, HTMLEndTagComment, + // BlockCommandComment, ParamCommandComment, + // TParamCommandComment, VerbatimBlockComment, + // VerbatimBlockLineComment, VerbatimLineComment). + SmallString<64> Text; // Text of the comment. + SmallString<16> Name; // Name of the comment (for Verbatim and HTML). SmallString<8> Direction; // Parameter direction (for (T)ParamCommand). SmallString<16> ParamName; // Parameter name (for (T)ParamCommand). SmallString<16> CloseName; // Closing tag name (for VerbatimBlock). @@ -72,9 +87,10 @@ SymbolID USR; // Unique identifer for referenced decl SmallString<16> UnresolvedName; // Name of unresolved type. - InfoType RefType = - InfoType::IT_default; // Indicates the type of this Reference (namespace, - // record, function, enum, default). + InfoType RefType = InfoType::IT_default; // Indicates the type of this + // Reference (namespace, record, + // function, enum, default). + Info *Ref; }; // A base struct for TypeInfos @@ -123,14 +139,24 @@ /// 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)) {} SymbolID USR; // Unique identifier for the decl described by this Info. SmallString<16> Name; // Unqualified name of the decl. llvm::SmallVector Namespace; // List of parent namespaces for this decl. std::vector Description; // Comment description of this decl. + + // References to be populated after reducer phase. + llvm::SmallVector Namespaces; + llvm::SmallVector Records; + llvm::SmallVector Functions; + llvm::SmallVector Enums; }; // Info for namespaces. @@ -148,10 +174,10 @@ bool IsMethod = false; // Indicates whether this function is a class method. Reference Parent; // Reference to the parent class decl for this method. TypeInfo ReturnType; // Info about the return type of this function. - llvm::SmallVector Params; // List of parameters. - AccessSpecifier Access = - AccessSpecifier::AS_none; // Access level for this method (public, - // private, protected, none). + llvm::SmallVector Params; // List of parameters. + AccessSpecifier Access = AccessSpecifier::AS_none; // Access level for this + // method (public, private, + // protected, none). }; // TODO: Expand to allow for documenting templating, inheritance access, @@ -178,7 +204,75 @@ // TODO: Add functionality to include separate markdown pages. +class InfoSet { +public: + InfoSet() = default; + + void insert(NamespaceInfo &&I); + void insert(RecordInfo &&I); + void insert(EnumInfo &&I); + void insert(FunctionInfo &&I); + + /// Returns the info with a Key, if it exists. Valid until next insert(). + template T *find(SymbolID 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 +namespace llvm { + +// Provide DenseMapInfo for std::arrays. +template struct DenseMapInfo> { + static inline std::array getEmptyKey() { return std::array(); } + + static inline std::array getTombstoneKey() { + return std::array(); + } + + static unsigned getHashValue(ArrayRef Val) { + assert(Val.data() != getEmptyKey().data() && "Cannot hash the empty key!"); + assert(Val.data() != getTombstoneKey().data() && + "Cannot hash the tombstone key!"); + return (unsigned)(hash_value(Val)); + } + + static bool isEqual(ArrayRef LHS, ArrayRef RHS) { + if (RHS.data() == getEmptyKey().data()) + return LHS.data() == getEmptyKey().data(); + if (RHS.data() == getTombstoneKey().data()) + return LHS.data() == getTombstoneKey().data(); + return LHS == RHS; + } +}; + +} // namespace llvm + #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_REPRESENTATION_H Index: clang-doc/Representation.cpp =================================================================== --- /dev/null +++ clang-doc/Representation.cpp @@ -0,0 +1,223 @@ +///===-- 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" + +namespace clang { +namespace doc { + +SymbolID StringToSymbol(llvm::StringRef StringUSR) { + SymbolID USR; + std::string HexString = fromHex(StringUSR); + std::copy(HexString.begin(), HexString.end(), USR.begin()); + return USR; +} + +static void mergeInfoBase(Info &L, Info &R) { + assert(L.USR == R.USR); + assert(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)); +} + +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.RefType == InfoType::IT_default && + L.ReturnType.Type.UnresolvedName == R.ReturnType.Type.UnresolvedName) || + 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(SymbolID 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(X &&I) { \ + auto R = InfoIndex.try_emplace(I.USR, X##s.size()); \ + if (R.second) \ + X##s.push_back(std::move(I)); \ + else \ + mergeInfo(X##s[R.first->second], std::move(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); + } +} + +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 @@ -18,7 +18,9 @@ // //===----------------------------------------------------------------------===// +#include "BitcodeWriter.h" #include "ClangDoc.h" +#include "Reducer.h" #include "clang/AST/AST.h" #include "clang/AST/Decl.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -54,14 +56,42 @@ 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 DoxygenOnly( "doxygen", llvm::cl::desc("Use only doxygen-style comments to generate docs."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory)); +bool DumpResultToFile(const Twine &DirName, const Twine &FileName, + StringRef Buffer) { + std::error_code OK; + SmallString<128> IRRootPath; + llvm::sys::path::native(OutDirectory, IRRootPath); + llvm::sys::path::append(IRRootPath, DirName); + std::error_code DirectoryStatus = + llvm::sys::fs::create_directories(IRRootPath); + if (DirectoryStatus != OK) { + llvm::errs() << "Unable to create documentation directories.\n"; + return true; + } + llvm::sys::path::append(IRRootPath, FileName); + 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 true; + } + OS << Buffer; + OS.close(); + return false; +} + int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); - std::error_code OK; auto Exec = clang::tooling::createExecutorFromCommandLineArgs( argc, argv, ClangDocCategory); @@ -80,34 +110,37 @@ // Mapping phase llvm::outs() << "Mapping decls...\n"; - auto Err = Exec->get()->execute(doc::newMapperActionFactory( - Exec->get()->getExecutionContext()), - ArgAdjuster); + auto Err = Exec->get()->execute( + doc::newMapperActionFactory(Exec->get()->getExecutionContext()), + ArgAdjuster); if (Err) llvm::errs() << toString(std::move(Err)) << "\n"; if (DumpMapperResult) { - Exec->get()->getToolResults()->forEachResult([&](StringRef Key, - StringRef Value) { - 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; - } - llvm::sys::path::append(IRRootPath, Key + ".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; - } - OS << Value; - OS.close(); - }); + bool Err = false; + Exec->get()->getToolResults()->forEachResult( + [&](StringRef Key, StringRef Value) { + Err = DumpResultToFile("bc", Key + ".bc", Value); + }); + if (Err) return 1; + } + + // 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<4098> Buffer; + llvm::BitstreamWriter Stream(Buffer); + doc::ClangDocBitcodeWriter Writer(Stream); + Writer.emitInfoSet(*Infos); + if (DumpResultToFile("bc", "docs.bc", Buffer)) + return 1; } return 0; Index: test/clang-doc/bc-comment.cpp =================================================================== --- /dev/null +++ test/clang-doc/bc-comment.cpp @@ -0,0 +1,182 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc --dump -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: + // CHECK-NEXT: blob data = 'F' + // 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 = '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: + // 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: blob data = '{{.*}}' + // CHECK-NEXT: blob data = '{{.*}}' + // 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,78 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc --dump -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: + // CHECK-NEXT: blob data = 'A' + // CHECK-NEXT: blob data = 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' + // CHECK-NEXT: blob data = '39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC' + // CHECK-NEXT: blob data = '9A82CB33ED0FDF81EE383D31CD0957D153C5E840' + // CHECK-NEXT: blob data = 'E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775' +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'B' + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: blob data = '9A82CB33ED0FDF81EE383D31CD0957D153C5E840' + // CHECK-NEXT: blob data = 'E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775' +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'f' + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'func' + // CHECK-NEXT: blob data = 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: blob data = '{{.*}}' + // 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: + // CHECK-NEXT: blob data = 'E' + // CHECK-NEXT: blob data = 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' + // CHECK-NEXT: blob data = '8D042EFFC98B373450BC6B5B90A330C25A150E9C' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: blob data = 'X' +// CHECK-NEXT: + Index: test/clang-doc/bc-record.cpp =================================================================== --- /dev/null +++ test/clang-doc/bc-record.cpp @@ -0,0 +1,178 @@ +// This test requires Linux due to the system-dependent USR for the +// inner class in function H. +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc --dump -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: + // CHECK-NEXT: blob data = 'E' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = '~E' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'ProtectedMethod' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'H' + // CHECK-NEXT: blob data = '3433664532ABFCC39301646A10E768C1882BF194' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'void' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'A' + // CHECK-NEXT: blob data = '{{.*}}' + // 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: + // CHECK-NEXT: blob data = 'C' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'int' + // CHECK-NEXT: blob data = 'C::i' + // CHECK-NEXT: + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'D' + // CHECK-NEXT: blob data = 'E3B54702FABFF4037025BA194FC27C47006330B5' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'E' + // CHECK-NEXT: blob data = 'E3B54702FABFF4037025BA194FC27C47006330B5' + // CHECK-NEXT: blob data = 'DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4' + // CHECK-NEXT: blob data = 'DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4' + // CHECK-NEXT: blob data = 'BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17' + // CHECK-NEXT: blob data = 'BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17' + // CHECK-NEXT: blob data = '5093D428CDC62096A67547BA52566E4FB9404EEE' + // CHECK-NEXT: blob data = '5093D428CDC62096A67547BA52566E4FB9404EEE' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'F' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: + // CHECK-NEXT: blob data = '289584A8E0FF4178A794622A547AA622503967A1' + // CHECK-NEXT: blob data = '0921737541208B8FA9BB42B60F78AC1D779AA054' +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'X' + // CHECK-NEXT: blob data = '641AB4A3D36399954ACDE29C7A8833032BF40472' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'Y' + // CHECK-NEXT: blob data = 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'I' + // CHECK-NEXT: blob data = 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'B' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: blob data = 'X' + // CHECK-NEXT: blob data = 'Y' +// CHECK-NEXT: +// CHECK-NEXT: + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'Bc' + // CHECK-NEXT: blob data = '{{.*}}' + // CHECK-NEXT: + // CHECK-NEXT: blob data = 'A' + // CHECK-NEXT: blob data = 'B' +// CHECK-NEXT: