Index: clang-doc/BitcodeReader.h =================================================================== --- /dev/null +++ clang-doc/BitcodeReader.h @@ -0,0 +1,104 @@ +//===-- 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" +#include "llvm/Bitcode/BitstreamWriter.h" +#include +#include + +namespace clang { +namespace doc { + +// Class to read bitstream into an InfoSet collection +class ClangDocBitcodeReader { + public: + ClangDocBitcodeReader() = default; + + bool readBitstream(llvm::SmallString<2048> Bits, + std::unique_ptr &IS); + + private: + enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; + + // Top level parsing + bool validateStream(llvm::BitstreamCursor &Stream); + bool readVersion(llvm::BitstreamCursor &Stream); + bool readBlockInfoBlock(llvm::BitstreamCursor &Stream); + template + bool readBlockToInfo(llvm::BitstreamCursor &Stream, unsigned ID, + std::unique_ptr &IS, T &&I); + + // Reading records + template + bool readRecord(llvm::BitstreamCursor &Stream, unsigned ID, T &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::SmallString<4> &Field, llvm::StringRef Blob); + void storeData(llvm::SmallString<16> &Field, llvm::StringRef Blob); + void storeData(llvm::SmallString<64> &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(llvm::BitstreamCursor &Stream, unsigned ID, T &I); + template + bool readSubBlock(llvm::BitstreamCursor &Stream, unsigned ID, T &I); + Cursor skipUntilRecordOrBlock(llvm::BitstreamCursor &Stream, + 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(Iy &I, Ty &&T); + + llvm::SmallVector Record; + Optional BlockInfo; + std::map RecordNames; +}; + +} // 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,528 @@ +//===-- 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 + +void ClangDocBitcodeReader::storeData(llvm::SmallString<4> &Field, + llvm::StringRef Blob) { + Field = Blob; +} + +void ClangDocBitcodeReader::storeData(llvm::SmallString<16> &Field, + llvm::StringRef Blob) { + Field = Blob; +} + +void ClangDocBitcodeReader::storeData(llvm::SmallString<64> &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(llvm::BitstreamCursor &Stream, + unsigned ID, T &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_ISDEFINITION: + storeData(I.IsDefinition, 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_ISDEFINITION: + storeData(I.IsDefinition, 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_ISDEFINITION: + storeData(I.IsDefinition, 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(Iy &I, Ty &&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(llvm::BitstreamCursor &Stream, + unsigned ID, T &I) { + if (Stream.EnterSubBlock(ID)) return false; + + while (true) { + unsigned BlockOrCode = 0; + Cursor Res = skipUntilRecordOrBlock(Stream, BlockOrCode); + + switch (Res) { + case Cursor::BadBlock: + return false; + case Cursor::BlockEnd: + return true; + case Cursor::BlockBegin: + if (readSubBlock(Stream, BlockOrCode, I)) continue; + if (!Stream.SkipBlock()) return false; + continue; + case Cursor::Record: + break; + } + if (!readRecord(Stream, BlockOrCode, I)) return false; + } +} + +template +bool ClangDocBitcodeReader::readSubBlock(llvm::BitstreamCursor &Stream, + unsigned ID, Ty &I) { + switch (ID) { + // Blocks can only have Comment or TypeInfo subblocks + case BI_COMMENT_BLOCK_ID: + if (readBlock(Stream, ID, getCommentInfo(I))) return true; + return false; + case BI_TYPE_BLOCK_ID: { + TypeInfo T; + if (readBlock(Stream, ID, T)) { + addTypeInfo(I, std::move(T)); + return true; + } + } + case BI_FIELD_TYPE_BLOCK_ID: { + FieldTypeInfo T; + if (readBlock(Stream, ID, T)) { + addTypeInfo(I, std::move(T)); + return true; + } + } + case BI_MEMBER_TYPE_BLOCK_ID: { + MemberTypeInfo T; + if (readBlock(Stream, ID, T)) { + addTypeInfo(I, std::move(T)); + return true; + } + return false; + } + default: + llvm::errs() << "Invalid subblock type.\n"; + return false; + } +} + +ClangDocBitcodeReader::Cursor ClangDocBitcodeReader::skipUntilRecordOrBlock( + llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) { + BlockOrRecordID = 0; + + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + + switch ((llvm::bitc::FixedAbbrevIDs)Code) { + case llvm::bitc::ENTER_SUBBLOCK: + BlockOrRecordID = Stream.ReadSubBlockID(); + return Cursor::BlockBegin; + case llvm::bitc::END_BLOCK: + if (Stream.ReadBlockEnd()) return Cursor::BadBlock; + return Cursor::BlockEnd; + case llvm::bitc::DEFINE_ABBREV: + Stream.ReadAbbrevRecord(); + continue; + case llvm::bitc::UNABBREV_RECORD: + return Cursor::BadBlock; + default: + BlockOrRecordID = Code; + return Cursor::Record; + } + } + llvm_unreachable("Premature stream end."); +} + +// Validation and overview + +bool ClangDocBitcodeReader::validateStream(llvm::BitstreamCursor &Stream) { + if (Stream.AtEndOfStream()) return false; + + // Sniff for the signature. + if (Stream.Read(8) != 'D' || Stream.Read(8) != 'O' || Stream.Read(8) != 'C' || + Stream.Read(8) != 'S') + return false; + return true; +} + +bool ClangDocBitcodeReader::readBlockInfoBlock(llvm::BitstreamCursor &Stream) { + BlockInfo = Stream.ReadBlockInfoBlock(/*ReadBlockInfoNames=*/true); + if (!BlockInfo) return false; + Stream.setBlockInfo(&*BlockInfo); + // Extract the record names associated with each field + for (unsigned I = BI_FIRST; I <= BI_LAST; ++I) { + for (const auto &N : (*BlockInfo).getBlockInfo(I)->RecordNames) + RecordNames[N.first] = N.second; + } + return true; +} + +// Entry point + +bool ClangDocBitcodeReader::readBitstream(llvm::SmallString<2048> Bits, + std::unique_ptr &IS) { + llvm::BitstreamCursor Stream(Bits); + if (!validateStream(Stream)) 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(Stream, ID, VersionNumber)) continue; + return false; + case llvm::bitc::BLOCKINFO_BLOCK_ID: + if (readBlockInfoBlock(Stream)) continue; + return false; + case BI_NAMESPACE_BLOCK_ID: { + NamespaceInfo I; + if (!readBlockToInfo(Stream, ID, IS, std::move(I))) return false; + continue; + } + case BI_RECORD_BLOCK_ID: { + RecordInfo I; + if (!readBlockToInfo(Stream, ID, IS, std::move(I))) return false; + continue; + } + case BI_ENUM_BLOCK_ID: { + EnumInfo I; + if (!readBlockToInfo(Stream, ID, IS, std::move(I))) return false; + continue; + } + case BI_FUNCTION_BLOCK_ID: { + FunctionInfo I; + if (!readBlockToInfo(Stream, ID, IS, std::move(I))) return false; + continue; + } + default: + if (!Stream.SkipBlock()) return false; + continue; + } + } + return true; +} + +template +bool ClangDocBitcodeReader::readBlockToInfo(llvm::BitstreamCursor &Stream, + unsigned ID, + std::unique_ptr &IS, + T &&I) { + if (!readBlock(Stream, 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 @@ -178,6 +178,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 @@ -450,5 +450,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,33 @@ +///===-- ClangDocReducer.h - ClangDocReducer -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "BitcodeReader.h" +#include "Reducer.h" +#include "Representation.h" + +namespace clang { +namespace doc { + +std::unique_ptr mergeInfos(tooling::ToolResults *Results) { + std::unique_ptr UniqueInfos = llvm::make_unique(); + doc::ClangDocBitcodeReader Reader; + bool Err = false; + Results->forEachResult([&](StringRef Key, StringRef Value) { + if (!Reader.readBitstream(Value, 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 {}; @@ -147,6 +168,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::SmallVector &References, + Reference &Caller); + void resolveReferences(llvm::SmallVector &References, + Reference &Caller); + void resolveReferences(llvm::SmallVector &References, + Reference &Caller); + void resolveReferences(llvm::SmallVector &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,207 @@ +///===-- 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.IsDefinition) { + L.IsDefinition = true; + 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::SmallVector &References, + Reference &Caller) { + for (auto &Ref : References) resolveReferences(Ref, Caller); +} + +void InfoSet::resolveReferences(llvm::SmallVector &References, + Reference &Caller) { + for (auto &Ref : References) resolveReferences(Ref.Type, Caller); +} + +void InfoSet::resolveReferences(llvm::SmallVector &References, + Reference &Caller) { + for (auto &Ref : References) resolveReferences(Ref.Type, Caller); +} + +void InfoSet::resolveReferences( + llvm::SmallVector &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" @@ -88,27 +90,37 @@ ArgAdjuster); if (Err) llvm::errs() << toString(std::move(Err)) << "\n"; + // 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) { - Exec->get()->getToolResults()->forEachResult([&](StringRef Key, - StringRef Value) { - SmallString<128> IRRootPath; - llvm::sys::path::native(OutDirectory, IRRootPath); - 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(); - }); + 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); + 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/comment-bc.cpp =================================================================== --- test/clang-doc/comment-bc.cpp +++ test/clang-doc/comment-bc.cpp @@ -3,7 +3,7 @@ // RUN: echo "" > %t/compile_flags.txt // RUN: cp "%s" "%t/test.cpp" // RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@F@F#I#I#.bc --dump | FileCheck %s +// RUN: llvm-bcanalyzer %t/docs/docs.bc --dump | FileCheck %s /// \brief Brief description. /// @@ -20,18 +20,20 @@ /// /// \param [out] I is a parameter. /// \param J is a parameter. -/// \return int -int F(int I, int J); +/// \return void +void F(int I, int J); + +/// Bonus comment on definition +void F(int I, int J) {} // CHECK: // CHECK: // CHECK: // CHECK: -// CHECK: +// CHECK: // CHECK: blob data = 'c:@F@F#I#I#' // CHECK: blob data = 'F' - // CHECK: - // CHECK: blob data = 'FullComment' + // CHECK: // CHECK: // CHECK: blob data = 'ParagraphComment' // CHECK: @@ -143,20 +145,30 @@ // CHECK: // CHECK: // CHECK: - // CHECK: + // CHECK: // CHECK: blob data = 'BlockCommandComment' // CHECK: blob data = 'return' - // CHECK: + // CHECK: // CHECK: blob data = 'ParagraphComment' - // CHECK: + // CHECK: // CHECK: blob data = 'TextComment' - // CHECK: blob data = ' int' + // CHECK: blob data = ' void' // CHECK: // CHECK: // CHECK: // CHECK: + // CHECK: + // CHECK: + // CHECK: blob data = 'ParagraphComment' + // CHECK: + // CHECK: blob data = 'TextComment' + // CHECK: blob data = ' Bonus comment on definition' + // CHECK: + // CHECK: + // CHECK: + // CHECK: // CHECK: - // CHECK: blob data = 'int' + // CHECK: blob data = 'void' // CHECK: // CHECK: // CHECK: blob data = 'int' Index: test/clang-doc/mapper-class-in-class.cpp =================================================================== --- test/clang-doc/mapper-class-in-class.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@S@X.bc --dump | FileCheck %s --check-prefix CHECK-X -// RUN: llvm-bcanalyzer %t/docs/c:@S@X@S@Y.bc --dump | FileCheck %s --check-prefix CHECK-X-Y - -class X { - class Y {}; -}; - -// CHECK-X: -// CHECK-X: - // CHECK-X: -// CHECK-X: -// CHECK-X: - // CHECK-X: blob data = 'c:@S@X' - // CHECK-X: blob data = 'X' - // CHECK-X: - // CHECK-X: -// CHECK-X: - -// CHECK-X-Y: -// CHECK-X-Y: - // CHECK-X-Y: -// CHECK-X-Y: -// CHECK-X-Y: - // CHECK-X-Y: blob data = 'c:@S@X@S@Y' - // CHECK-X-Y: blob data = 'Y' - // CHECK-X-Y: blob data = 'c:@S@X' - // CHECK-X-Y: - // CHECK-X-Y: -// CHECK-X-Y: Index: test/clang-doc/mapper-class-in-function.cpp =================================================================== --- test/clang-doc/mapper-class-in-function.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:test.cpp@396@F@H#@S@I.bc --dump | FileCheck %s --check-prefix CHECK-H-I -// RUN: llvm-bcanalyzer %t/docs/c:@F@H#.bc --dump | FileCheck %s --check-prefix CHECK-H - -void H() { - class I {}; -} - -// CHECK-H: -// CHECK-H: - // CHECK-H: -// CHECK-H: -// CHECK-H: - // CHECK-H: blob data = 'c:@F@H#' - // CHECK-H: blob data = 'H' - // CHECK-H: - // CHECK-H: - // CHECK-H: blob data = 'void' - // CHECK-H: -// CHECK-H: - -// CHECK-H-I: -// CHECK-H-I: - // CHECK-H-I: -// CHECK-H-I: -// CHECK-H-I: - // CHECK-H-I: blob data = 'c:test.cpp@396@F@H#@S@I' - // CHECK-H-I: blob data = 'I' - // CHECK-H-I: blob data = 'c:@F@H#' - // CHECK-H-I: - // CHECK-H-I: -// CHECK-H-I: Index: test/clang-doc/mapper-class.cpp =================================================================== --- test/clang-doc/mapper-class.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@S@E.bc --dump | FileCheck %s - -class E {}; -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'c:@S@E' - // CHECK: blob data = 'E' - // CHECK: - // CHECK: -// CHECK: Index: test/clang-doc/mapper-enum.cpp =================================================================== --- test/clang-doc/mapper-enum.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@E@B.bc --dump | FileCheck %s --check-prefix CHECK-B -// RUN: llvm-bcanalyzer %t/docs/c:@E@C.bc --dump | FileCheck %s --check-prefix CHECK-C - -enum B { X, Y }; -enum class C { A, B }; -// CHECK-B: -// CHECK-B: - // CHECK-B: -// CHECK-B: -// CHECK-B: - // CHECK-B: blob data = 'c:@E@B' - // CHECK-B: blob data = 'B' - // CHECK-B: - // CHECK-B: - // CHECK-B: blob data = 'X' - // CHECK-B: - // CHECK-B: - // CHECK-B: blob data = 'Y' - // CHECK-B: -// CHECK-B: - -// CHECK-C: -// CHECK-C: - // CHECK-C: -// CHECK-C: -// CHECK-C: - // CHECK-C: blob data = 'c:@E@C' - // CHECK-C: blob data = 'C' - // CHECK-C: - // CHECK-C: - // CHECK-C: - // CHECK-C: blob data = 'C::A' - // CHECK-C: - // CHECK-C: - // CHECK-C: blob data = 'C::B' - // CHECK-C: -// CHECK-C: Index: test/clang-doc/mapper-function.cpp =================================================================== --- test/clang-doc/mapper-function.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@F@F#I#.bc --dump | FileCheck %s - -int F(int param) { return param; } -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'c:@F@F#I#' - // CHECK: blob data = 'F' - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: blob data = 'param' - // CHECK: -// CHECK: Index: test/clang-doc/mapper-method.cpp =================================================================== --- test/clang-doc/mapper-method.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@S@G@F@Method#I#.bc --dump | FileCheck %s --check-prefix CHECK-G-F -// RUN: llvm-bcanalyzer %t/docs/c:@S@G.bc --dump | FileCheck %s --check-prefix CHECK-G - -class G { -public: - int Method(int param) { return param; } -}; - -// CHECK-G: -// CHECK-G: - // CHECK-G: -// CHECK-G: -// CHECK-G: - // CHECK-G: blob data = 'c:@S@G' - // CHECK-G: blob data = 'G' - // CHECK-G: - // CHECK-G: -// CHECK-G: - - -// CHECK-G-F: -// CHECK-G-F: - // CHECK-G-F: -// CHECK-G-F: -// CHECK-G-F: - // CHECK-G-F: blob data = 'c:@S@G@F@Method#I#' - // CHECK-G-F: blob data = 'Method' - // CHECK-G-F: blob data = 'c:@S@G' - // CHECK-G-F: - // CHECK-G-F: - // CHECK-G-F: blob data = 'c:@S@G' - // CHECK-G-F: - // CHECK-G-F: blob data = 'int' - // CHECK-G-F: - // CHECK-G-F: - // CHECK-G-F: blob data = 'int' - // CHECK-G-F: blob data = 'param' - // CHECK-G-F: -// CHECK-G-F: Index: test/clang-doc/mapper-namespace.cpp =================================================================== --- test/clang-doc/mapper-namespace.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@N@A.bc --dump | FileCheck %s - -namespace A {} -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'c:@N@A' - // CHECK: blob data = 'A' -// CHECK: Index: test/clang-doc/mapper-struct.cpp =================================================================== --- test/clang-doc/mapper-struct.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@S@C.bc --dump | FileCheck %s - -struct C { int i; }; -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'c:@S@C' - // CHECK: blob data = 'C' - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: blob data = 'C::i' - // CHECK: - // CHECK: -// CHECK: Index: test/clang-doc/mapper-union.cpp =================================================================== --- test/clang-doc/mapper-union.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --dump --omit-filenames -doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer %t/docs/c:@U@D.bc --dump | FileCheck %s - -union D { int X; int Y; }; -// CHECK: -// CHECK: - // CHECK: -// CHECK: -// CHECK: - // CHECK: blob data = 'c:@U@D' - // CHECK: blob data = 'D' - // CHECK: - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: blob data = 'D::X' - // CHECK: - // CHECK: - // CHECK: - // CHECK: blob data = 'int' - // CHECK: blob data = 'D::Y' - // CHECK: - // CHECK: -// CHECK: Index: test/clang-doc/namespace-bc.cpp =================================================================== --- /dev/null +++ test/clang-doc/namespace-bc.cpp @@ -0,0 +1,74 @@ +// 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/docs.bc --dump | FileCheck %s + +namespace A { + +void f(); +void f() {}; + +} // A + +namespace A { +namespace B { + +enum E { X }; + +E func(int i) { + return X; +} + +} +} + +// CHECK: +// CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@N@A' + // CHECK: blob data = 'A' +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@N@A@N@B' + // CHECK: blob data = 'B' + // CHECK: blob data = 'c:@N@A' +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@N@A@F@f#' + // CHECK: blob data = 'f' + // CHECK: blob data = 'c:@N@A' + // CHECK: + // CHECK: + // CHECK: blob data = 'void' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@N@A@N@B@F@func#I#' + // CHECK: blob data = 'func' + // CHECK: blob data = 'c:@N@A@N@B' + // CHECK: blob data = 'c:@N@A' + // CHECK: + // CHECK: + // CHECK: blob data = 'enum A::B::E' + // CHECK: + // CHECK: + // CHECK: blob data = 'int' + // CHECK: blob data = 'i' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@N@A@N@B@E@E' + // CHECK: blob data = 'E' + // CHECK: blob data = 'c:@N@A@N@B' + // CHECK: blob data = 'c:@N@A' + // CHECK: + // CHECK: + // CHECK: blob data = 'A::B::X' + // CHECK: +// CHECK: + + Index: test/clang-doc/record-bc.cpp =================================================================== --- /dev/null +++ test/clang-doc/record-bc.cpp @@ -0,0 +1,133 @@ +// 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/docs.bc --dump | FileCheck %s + +union A { int X; int Y; }; + +enum B { X, Y }; + +struct C { int i; }; + +class D {}; + +class E { +public: + E() {} + ~E() {} + +protected: + void ProtectedMethod(); +}; + +void E::ProtectedMethod() {} + +class F : virtual private D, public E {}; + +// CHECK: +// CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@S@E@F@E#' + // CHECK: blob data = 'E' + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: blob data = 'void' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@S@E@F@~E#' + // CHECK: blob data = '~E' + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: blob data = 'void' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@S@E@F@ProtectedMethod#' + // CHECK: blob data = 'ProtectedMethod' + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: blob data = 'void' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@S@E@F@ProtectedMethod#' + // CHECK: blob data = 'ProtectedMethod' + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: + // CHECK: blob data = 'void' + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@U@A' + // CHECK: blob data = 'A' + // CHECK: + // CHECK: + // CHECK: + // CHECK: blob data = 'int' + // CHECK: blob data = 'A::X' + // CHECK: + // CHECK: + // CHECK: + // CHECK: blob data = 'int' + // CHECK: blob data = 'A::Y' + // CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@S@C' + // CHECK: blob data = 'C' + // CHECK: + // CHECK: + // CHECK: + // CHECK: blob data = 'int' + // CHECK: blob data = 'C::i' + // CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@S@D' + // CHECK: blob data = 'D' + // CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: blob data = 'E' + // CHECK: + // CHECK: +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@S@F' + // CHECK: blob data = 'F' + // CHECK: + // CHECK: + // CHECK: blob data = 'c:@S@E' + // CHECK: blob data = 'c:@S@D' +// CHECK: +// CHECK: + // CHECK: blob data = 'c:@E@B' + // CHECK: blob data = 'B' + // CHECK: + // CHECK: + // CHECK: blob data = 'X' + // CHECK: + // CHECK: + // CHECK: blob data = 'Y' + // CHECK: +// CHECK: