diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp --- a/clang-tools-extra/clang-doc/BitcodeReader.cpp +++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp @@ -24,12 +24,6 @@ return llvm::Error::success(); } -llvm::Error decodeRecord(const Record &R, std::string &Field, - llvm::StringRef Blob) { - Field.assign(Blob.begin(), Blob.end()); - return llvm::Error::success(); -} - llvm::Error decodeRecord(const Record &R, SymbolID &Field, llvm::StringRef Blob) { if (R[0] != BitCodeConstants::USRHashSize) @@ -104,6 +98,7 @@ case InfoType::IT_function: case InfoType::IT_default: case InfoType::IT_enum: + case InfoType::IT_typedef: Field = IT; return llvm::Error::success(); } @@ -233,6 +228,23 @@ } } +llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob, + TypedefInfo *I) { + switch (ID) { + case TYPEDEF_USR: + return decodeRecord(R, I->USR, Blob); + case TYPEDEF_NAME: + return decodeRecord(R, I->Name, Blob); + case TYPEDEF_DEFLOCATION: + return decodeRecord(R, I->DefLoc, Blob); + case TYPEDEF_IS_USING: + return decodeRecord(R, I->IsUsing, Blob); + default: + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "invalid field for TypedefInfo"); + } +} + llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob, EnumValueInfo *I) { switch (ID) { @@ -424,6 +436,11 @@ return llvm::Error::success(); } +template <> llvm::Error addTypeInfo(TypedefInfo *I, TypeInfo &&T) { + I->Underlying = std::move(T); + return llvm::Error::success(); +} + template llvm::Error addReference(T I, Reference &&R, FieldId F) { return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid type cannot contain Reference"); @@ -475,6 +492,17 @@ } } +template <> llvm::Error addReference(TypedefInfo *I, Reference &&R, FieldId F) { + switch (F) { + case FieldId::F_namespace: + I->Namespace.emplace_back(std::move(R)); + return llvm::Error::success(); + default: + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "invalid type cannot contain Reference"); + } +} + template <> llvm::Error addReference(NamespaceInfo *I, Reference &&R, FieldId F) { switch (F) { @@ -482,10 +510,10 @@ I->Namespace.emplace_back(std::move(R)); return llvm::Error::success(); case FieldId::F_child_namespace: - I->ChildNamespaces.emplace_back(std::move(R)); + I->Children.Namespaces.emplace_back(std::move(R)); return llvm::Error::success(); case FieldId::F_child_record: - I->ChildRecords.emplace_back(std::move(R)); + I->Children.Records.emplace_back(std::move(R)); return llvm::Error::success(); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), @@ -520,7 +548,7 @@ I->VirtualParents.emplace_back(std::move(R)); return llvm::Error::success(); case FieldId::F_child_record: - I->ChildRecords.emplace_back(std::move(R)); + I->Children.Records.emplace_back(std::move(R)); return llvm::Error::success(); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), @@ -534,32 +562,37 @@ exit(1); } +// Namespace children: template <> void addChild(NamespaceInfo *I, FunctionInfo &&R) { - I->ChildFunctions.emplace_back(std::move(R)); + I->Children.Functions.emplace_back(std::move(R)); } - template <> void addChild(NamespaceInfo *I, EnumInfo &&R) { - I->ChildEnums.emplace_back(std::move(R)); + I->Children.Enums.emplace_back(std::move(R)); +} +template <> void addChild(NamespaceInfo *I, TypedefInfo &&R) { + I->Children.Typedefs.emplace_back(std::move(R)); } +// Record children: template <> void addChild(RecordInfo *I, FunctionInfo &&R) { - I->ChildFunctions.emplace_back(std::move(R)); + I->Children.Functions.emplace_back(std::move(R)); } - template <> void addChild(RecordInfo *I, EnumInfo &&R) { - I->ChildEnums.emplace_back(std::move(R)); + I->Children.Enums.emplace_back(std::move(R)); +} +template <> void addChild(RecordInfo *I, TypedefInfo &&R) { + I->Children.Typedefs.emplace_back(std::move(R)); } +// Other types of children: template <> void addChild(EnumInfo *I, EnumValueInfo &&R) { I->Members.emplace_back(std::move(R)); } - template <> void addChild(RecordInfo *I, BaseRecordInfo &&R) { I->Bases.emplace_back(std::move(R)); } - template <> void addChild(BaseRecordInfo *I, FunctionInfo &&R) { - I->ChildFunctions.emplace_back(std::move(R)); + I->Children.Functions.emplace_back(std::move(R)); } // Read records from bitcode into a given info. @@ -686,6 +719,13 @@ addChild(I, std::move(EV)); return llvm::Error::success(); } + case BI_TYPEDEF_BLOCK_ID: { + TypedefInfo TI; + if (auto Err = readBlock(ID, &TI)) + return Err; + addChild(I, std::move(TI)); + return llvm::Error::success(); + } default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid subblock type"); @@ -786,6 +826,8 @@ return createInfo(ID); case BI_ENUM_BLOCK_ID: return createInfo(ID); + case BI_TYPEDEF_BLOCK_ID: + return createInfo(ID); case BI_FUNCTION_BLOCK_ID: return createInfo(ID); default: @@ -825,6 +867,7 @@ case BI_NAMESPACE_BLOCK_ID: case BI_RECORD_BLOCK_ID: case BI_ENUM_BLOCK_ID: + case BI_TYPEDEF_BLOCK_ID: case BI_FUNCTION_BLOCK_ID: { auto InfoOrErr = readBlockToInfo(ID); if (!InfoOrErr) diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h --- a/clang-tools-extra/clang-doc/BitcodeWriter.h +++ b/clang-tools-extra/clang-doc/BitcodeWriter.h @@ -64,6 +64,7 @@ BI_FUNCTION_BLOCK_ID, BI_COMMENT_BLOCK_ID, BI_REFERENCE_BLOCK_ID, + BI_TYPEDEF_BLOCK_ID, BI_LAST, BI_FIRST = BI_VERSION_BLOCK_ID }; @@ -123,6 +124,10 @@ REFERENCE_TYPE, REFERENCE_PATH, REFERENCE_FIELD, + TYPEDEF_USR, + TYPEDEF_NAME, + TYPEDEF_DEFLOCATION, + TYPEDEF_IS_USING, RI_LAST, RI_FIRST = VERSION }; @@ -160,8 +165,9 @@ void emitBlock(const EnumInfo &I); void emitBlock(const EnumValueInfo &I); void emitBlock(const TypeInfo &B); + void emitBlock(const TypedefInfo &B); void emitBlock(const FieldTypeInfo &B); - void emitBlock(const MemberTypeInfo &B); + void emitBlock(const MemberTypeInfo &T); void emitBlock(const CommentInfo &B); void emitBlock(const Reference &B, FieldId F); diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp --- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp +++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp @@ -113,6 +113,7 @@ {BI_NAMESPACE_BLOCK_ID, "NamespaceBlock"}, {BI_ENUM_BLOCK_ID, "EnumBlock"}, {BI_ENUM_VALUE_BLOCK_ID, "EnumValueBlock"}, + {BI_TYPEDEF_BLOCK_ID, "TypedefBlock"}, {BI_TYPE_BLOCK_ID, "TypeBlock"}, {BI_FIELD_TYPE_BLOCK_ID, "FieldTypeBlock"}, {BI_MEMBER_TYPE_BLOCK_ID, "MemberTypeBlock"}, @@ -187,7 +188,11 @@ {REFERENCE_NAME, {"Name", &StringAbbrev}}, {REFERENCE_TYPE, {"RefType", &IntAbbrev}}, {REFERENCE_PATH, {"Path", &StringAbbrev}}, - {REFERENCE_FIELD, {"Field", &IntAbbrev}}}; + {REFERENCE_FIELD, {"Field", &IntAbbrev}}, + {TYPEDEF_USR, {"USR", &SymbolIDAbbrev}}, + {TYPEDEF_NAME, {"Name", &StringAbbrev}}, + {TYPEDEF_DEFLOCATION, {"DefLocation", &LocationAbbrev}}, + {TYPEDEF_IS_USING, {"IsUsing", &BoolAbbrev}}}; assert(Inits.size() == RecordIdCount); for (const auto &Init : Inits) { RecordIdNameMap[Init.first] = Init.second; @@ -218,6 +223,9 @@ // Enum Value Block {BI_ENUM_VALUE_BLOCK_ID, {ENUM_VALUE_NAME, ENUM_VALUE_VALUE, ENUM_VALUE_EXPR}}, + // Typedef Block + {BI_TYPEDEF_BLOCK_ID, + {TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DEFLOCATION, TYPEDEF_IS_USING}}, // Namespace Block {BI_NAMESPACE_BLOCK_ID, {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}}, @@ -418,6 +426,18 @@ emitBlock(T.Type, FieldId::F_type); } +void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) { + StreamSubBlockGuard Block(Stream, BI_TYPEDEF_BLOCK_ID); + emitRecord(T.USR, TYPEDEF_USR); + emitRecord(T.Name, TYPEDEF_NAME); + for (const auto &N : T.Namespace) + emitBlock(N, FieldId::F_namespace); + if (T.DefLoc) + emitRecord(*T.DefLoc, TYPEDEF_DEFLOCATION); + emitRecord(T.IsUsing, TYPEDEF_IS_USING); + emitBlock(T.Underlying); +} + void ClangDocBitcodeWriter::emitBlock(const FieldTypeInfo &T) { StreamSubBlockGuard Block(Stream, BI_FIELD_TYPE_BLOCK_ID); emitBlock(T.Type, FieldId::F_type); @@ -465,13 +485,15 @@ emitBlock(N, FieldId::F_namespace); for (const auto &CI : I.Description) emitBlock(CI); - for (const auto &C : I.ChildNamespaces) + for (const auto &C : I.Children.Namespaces) emitBlock(C, FieldId::F_child_namespace); - for (const auto &C : I.ChildRecords) + for (const auto &C : I.Children.Records) emitBlock(C, FieldId::F_child_record); - for (const auto &C : I.ChildFunctions) + for (const auto &C : I.Children.Functions) emitBlock(C); - for (const auto &C : I.ChildEnums) + for (const auto &C : I.Children.Enums) + emitBlock(C); + for (const auto &C : I.Children.Typedefs) emitBlock(C); } @@ -524,11 +546,13 @@ emitBlock(P, FieldId::F_vparent); for (const auto &PB : I.Bases) emitBlock(PB); - for (const auto &C : I.ChildRecords) + for (const auto &C : I.Children.Records) emitBlock(C, FieldId::F_child_record); - for (const auto &C : I.ChildFunctions) + for (const auto &C : I.Children.Functions) + emitBlock(C); + for (const auto &C : I.Children.Enums) emitBlock(C); - for (const auto &C : I.ChildEnums) + for (const auto &C : I.Children.Typedefs) emitBlock(C); } @@ -543,7 +567,7 @@ emitRecord(I.IsParent, BASE_RECORD_IS_PARENT); for (const auto &M : I.Members) emitBlock(M); - for (const auto &C : I.ChildFunctions) + for (const auto &C : I.Children.Functions) emitBlock(C); } @@ -581,6 +605,9 @@ case InfoType::IT_function: emitBlock(*static_cast(I)); break; + case InfoType::IT_typedef: + emitBlock(*static_cast(I)); + break; default: llvm::errs() << "Unexpected info, unable to write.\n"; return true; diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp --- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -734,28 +734,29 @@ llvm::SmallString<64> BasePath = I.getRelativeFilePath(""); std::vector> ChildNamespaces = - genReferencesBlock(I.ChildNamespaces, "Namespaces", BasePath); + genReferencesBlock(I.Children.Namespaces, "Namespaces", BasePath); AppendVector(std::move(ChildNamespaces), Out); std::vector> ChildRecords = - genReferencesBlock(I.ChildRecords, "Records", BasePath); + genReferencesBlock(I.Children.Records, "Records", BasePath); AppendVector(std::move(ChildRecords), Out); std::vector> ChildFunctions = - genFunctionsBlock(I.ChildFunctions, CDCtx, BasePath); + genFunctionsBlock(I.Children.Functions, CDCtx, BasePath); AppendVector(std::move(ChildFunctions), Out); std::vector> ChildEnums = - genEnumsBlock(I.ChildEnums, CDCtx); + genEnumsBlock(I.Children.Enums, CDCtx); AppendVector(std::move(ChildEnums), Out); - if (!I.ChildNamespaces.empty()) + if (!I.Children.Namespaces.empty()) InfoIndex.Children.emplace_back("Namespaces", "Namespaces"); - if (!I.ChildRecords.empty()) + if (!I.Children.Records.empty()) InfoIndex.Children.emplace_back("Records", "Records"); - if (!I.ChildFunctions.empty()) + if (!I.Children.Functions.empty()) InfoIndex.Children.emplace_back( - genInfoIndexItem(I.ChildFunctions, "Functions")); - if (!I.ChildEnums.empty()) - InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums")); + genInfoIndexItem(I.Children.Functions, "Functions")); + if (!I.Children.Enums.empty()) + InfoIndex.Children.emplace_back( + genInfoIndexItem(I.Children.Enums, "Enums")); return Out; } @@ -802,25 +803,26 @@ genRecordMembersBlock(I.Members, I.Path); AppendVector(std::move(Members), Out); std::vector> ChildRecords = - genReferencesBlock(I.ChildRecords, "Records", I.Path); + genReferencesBlock(I.Children.Records, "Records", I.Path); AppendVector(std::move(ChildRecords), Out); std::vector> ChildFunctions = - genFunctionsBlock(I.ChildFunctions, CDCtx, I.Path); + genFunctionsBlock(I.Children.Functions, CDCtx, I.Path); AppendVector(std::move(ChildFunctions), Out); std::vector> ChildEnums = - genEnumsBlock(I.ChildEnums, CDCtx); + genEnumsBlock(I.Children.Enums, CDCtx); AppendVector(std::move(ChildEnums), Out); if (!I.Members.empty()) InfoIndex.Children.emplace_back("Members", "Members"); - if (!I.ChildRecords.empty()) + if (!I.Children.Records.empty()) InfoIndex.Children.emplace_back("Records", "Records"); - if (!I.ChildFunctions.empty()) + if (!I.Children.Functions.empty()) + InfoIndex.Children.emplace_back( + genInfoIndexItem(I.Children.Functions, "Functions")); + if (!I.Children.Enums.empty()) InfoIndex.Children.emplace_back( - genInfoIndexItem(I.ChildFunctions, "Functions")); - if (!I.ChildEnums.empty()) - InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums")); + genInfoIndexItem(I.Children.Enums, "Enums")); return Out; } diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp --- a/clang-tools-extra/clang-doc/MDGenerator.cpp +++ b/clang-tools-extra/clang-doc/MDGenerator.cpp @@ -189,9 +189,9 @@ llvm::SmallString<64> BasePath = I.getRelativeFilePath(""); - if (!I.ChildNamespaces.empty()) { + if (!I.Children.Namespaces.empty()) { writeHeader("Namespaces", 2, OS); - for (const auto &R : I.ChildNamespaces) { + for (const auto &R : I.Children.Namespaces) { OS << "* "; writeNameLink(BasePath, R, OS); OS << "\n"; @@ -199,9 +199,9 @@ writeNewLine(OS); } - if (!I.ChildRecords.empty()) { + if (!I.Children.Records.empty()) { writeHeader("Records", 2, OS); - for (const auto &R : I.ChildRecords) { + for (const auto &R : I.Children.Records) { OS << "* "; writeNameLink(BasePath, R, OS); OS << "\n"; @@ -209,15 +209,15 @@ writeNewLine(OS); } - if (!I.ChildFunctions.empty()) { + if (!I.Children.Functions.empty()) { writeHeader("Functions", 2, OS); - for (const auto &F : I.ChildFunctions) + for (const auto &F : I.Children.Functions) genMarkdown(CDCtx, F, OS); writeNewLine(OS); } - if (!I.ChildEnums.empty()) { + if (!I.Children.Enums.empty()) { writeHeader("Enums", 2, OS); - for (const auto &E : I.ChildEnums) + for (const auto &E : I.Children.Enums) genMarkdown(CDCtx, E, OS); writeNewLine(OS); } @@ -259,21 +259,21 @@ writeNewLine(OS); } - if (!I.ChildRecords.empty()) { + if (!I.Children.Records.empty()) { writeHeader("Records", 2, OS); - for (const auto &R : I.ChildRecords) + for (const auto &R : I.Children.Records) writeLine(R.Name, OS); writeNewLine(OS); } - if (!I.ChildFunctions.empty()) { + if (!I.Children.Functions.empty()) { writeHeader("Functions", 2, OS); - for (const auto &F : I.ChildFunctions) + for (const auto &F : I.Children.Functions) genMarkdown(CDCtx, F, OS); writeNewLine(OS); } - if (!I.ChildEnums.empty()) { + if (!I.Children.Enums.empty()) { writeHeader("Enums", 2, OS); - for (const auto &E : I.ChildEnums) + for (const auto &E : I.Children.Enums) genMarkdown(CDCtx, E, OS); writeNewLine(OS); } diff --git a/clang-tools-extra/clang-doc/Mapper.h b/clang-tools-extra/clang-doc/Mapper.h --- a/clang-tools-extra/clang-doc/Mapper.h +++ b/clang-tools-extra/clang-doc/Mapper.h @@ -39,6 +39,8 @@ bool VisitEnumDecl(const EnumDecl *D); bool VisitCXXMethodDecl(const CXXMethodDecl *D); bool VisitFunctionDecl(const FunctionDecl *D); + bool VisitTypedefDecl(const TypedefDecl *D); + bool VisitTypeAliasDecl(const TypeAliasDecl *D); private: template bool mapDecl(const T *D); diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp --- a/clang-tools-extra/clang-doc/Mapper.cpp +++ b/clang-tools-extra/clang-doc/Mapper.cpp @@ -71,6 +71,14 @@ return mapDecl(D); } +bool MapASTVisitor::VisitTypedefDecl(const TypedefDecl *D) { + return mapDecl(D); +} + +bool MapASTVisitor::VisitTypeAliasDecl(const TypeAliasDecl *D) { + return mapDecl(D); +} + comments::FullComment * MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const { RawComment *Comment = Context.getRawCommentForDeclNoCache(D); diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -30,17 +30,19 @@ // SHA1'd hash of a USR. using SymbolID = std::array; -struct Info; -struct FunctionInfo; -struct EnumInfo; struct BaseRecordInfo; +struct EnumInfo; +struct FunctionInfo; +struct Info; +struct TypedefInfo; enum class InfoType { IT_default, IT_namespace, IT_record, IT_function, - IT_enum + IT_enum, + IT_typedef }; // A representation of a parsed comment. @@ -142,6 +144,22 @@ llvm::SmallString<128> Path; }; +// Holds the children of a record or namespace. +struct ScopeChildren { + // Namespaces and Records are references because they will be properly + // documented in their own info, while the entirety of Functions and Enums are + // included here because they should not have separate documentation from + // their scope. + // + // Namespaces are not syntactically valid as children of records, but making + // this general for all possible container types reduces code complexity. + std::vector Namespaces; + std::vector Records; + std::vector Functions; + std::vector Enums; + std::vector Typedefs; +}; + // A base struct for TypeInfos struct TypeInfo { TypeInfo() = default; @@ -199,7 +217,7 @@ struct Location { Location(int LineNumber = 0, StringRef Filename = StringRef(), bool IsFileInRootDir = false) - : LineNumber(LineNumber), Filename(Filename), + : LineNumber(LineNumber), Filename(std::move(Filename)), IsFileInRootDir(IsFileInRootDir) {} bool operator==(const Location &Other) const { @@ -266,14 +284,7 @@ void merge(NamespaceInfo &&I); - // Namespaces and Records are references because they will be properly - // documented in their own info, while the entirety of Functions and Enums are - // included here because they should not have separate documentation from - // their scope. - std::vector ChildNamespaces; - std::vector ChildRecords; - std::vector ChildFunctions; - std::vector ChildEnums; + ScopeChildren Children; }; // Info for symbols. @@ -338,12 +349,23 @@ Bases; // List of base/parent records; this includes inherited methods and // attributes - // Records are references because they will be properly documented in their - // own info, while the entirety of Functions and Enums are included here - // because they should not have separate documentation from their scope. - std::vector ChildRecords; - std::vector ChildFunctions; - std::vector ChildEnums; + ScopeChildren Children; +}; + +// Info for typedef and using statements. +struct TypedefInfo : public SymbolInfo { + TypedefInfo(SymbolID USR = SymbolID()) + : SymbolInfo(InfoType::IT_typedef, USR) {} + + void merge(TypedefInfo &&I); + + TypeInfo Underlying; + + // Inidicates if this is a new C++ "using"-style typedef: + // using MyVector = std::vector + // False means it's a C-style typedef: + // typedef std::vector MyVector; + bool IsUsing; }; struct BaseRecordInfo : public RecordInfo { diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp --- a/clang-tools-extra/clang-doc/Representation.cpp +++ b/clang-tools-extra/clang-doc/Representation.cpp @@ -90,6 +90,18 @@ } } +void reduceChildren(std::vector &Children, + std::vector &&ChildrenToMerge) { + for (auto &ChildToMerge : ChildrenToMerge) { + int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); + if (mergeIdx == -1) { + Children.push_back(std::move(ChildToMerge)); + continue; + } + Children[mergeIdx].merge(std::move(ChildToMerge)); + } +} + } // namespace // Dispatch function. @@ -108,6 +120,8 @@ return reduce(Values); case InfoType::IT_function: return reduce(Values); + case InfoType::IT_typedef: + return reduce(Values); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "unexpected info type"); @@ -209,10 +223,11 @@ void NamespaceInfo::merge(NamespaceInfo &&Other) { assert(mergeable(Other)); // Reduce children if necessary. - reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces)); - reduceChildren(ChildRecords, std::move(Other.ChildRecords)); - reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); - reduceChildren(ChildEnums, std::move(Other.ChildEnums)); + reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces)); + reduceChildren(Children.Records, std::move(Other.Children.Records)); + reduceChildren(Children.Functions, std::move(Other.Children.Functions)); + reduceChildren(Children.Enums, std::move(Other.Children.Enums)); + reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs)); mergeBase(std::move(Other)); } @@ -230,9 +245,10 @@ if (VirtualParents.empty()) VirtualParents = std::move(Other.VirtualParents); // Reduce children if necessary. - reduceChildren(ChildRecords, std::move(Other.ChildRecords)); - reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); - reduceChildren(ChildEnums, std::move(Other.ChildEnums)); + reduceChildren(Children.Records, std::move(Other.Children.Records)); + reduceChildren(Children.Functions, std::move(Other.Children.Functions)); + reduceChildren(Children.Enums, std::move(Other.Children.Enums)); + reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs)); SymbolInfo::merge(std::move(Other)); } @@ -260,6 +276,15 @@ SymbolInfo::merge(std::move(Other)); } +void TypedefInfo::merge(TypedefInfo &&Other) { + assert(mergeable(Other)); + if (!IsUsing) + IsUsing = Other.IsUsing; + if (Underlying.Type.Name == "") + Underlying = Other.Underlying; + SymbolInfo::merge(std::move(Other)); +} + llvm::SmallString<16> Info::extractName() const { if (!Name.empty()) return Name; @@ -282,6 +307,9 @@ case InfoType::IT_enum: return llvm::SmallString<16>("@nonymous_enum_" + toHex(llvm::toStringRef(USR))); + case InfoType::IT_typedef: + return llvm::SmallString<16>("@nonymous_typedef_" + + toHex(llvm::toStringRef(USR))); case InfoType::IT_function: return llvm::SmallString<16>("@nonymous_function_" + toHex(llvm::toStringRef(USR))); diff --git a/clang-tools-extra/clang-doc/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h --- a/clang-tools-extra/clang-doc/Serialize.h +++ b/clang-tools-extra/clang-doc/Serialize.h @@ -39,19 +39,31 @@ std::pair, std::unique_ptr> emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, StringRef File, bool IsFileInRootDir, bool PublicOnly); + std::pair, std::unique_ptr> emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, StringRef File, bool IsFileInRootDir, bool PublicOnly); + std::pair, std::unique_ptr> emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, StringRef File, bool IsFileInRootDir, bool PublicOnly); + std::pair, std::unique_ptr> emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, StringRef File, bool IsFileInRootDir, bool PublicOnly); + std::pair, std::unique_ptr> emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, StringRef File, bool IsFileInRootDir, bool PublicOnly); +std::pair, std::unique_ptr> +emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber, + StringRef File, bool IsFileInRootDir, bool PublicOnly); + +std::pair, std::unique_ptr> +emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber, + StringRef File, bool IsFileInRootDir, bool PublicOnly); + // Function to hash a given USR value for storage. // As USRs (Unified Symbol Resolution) could be large, especially for functions // with long type arguments, we use 160-bits SHA1(USR) values to diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -273,6 +273,75 @@ isPublic(D->getAccessUnsafe(), D->getLinkageInternal())); } +// The InsertChild functions insert the given info into the given scope using +// the method appropriate for that type. Some types are moved into the +// appropriate vector, while other types have Reference objects generated to +// refer to them. +// +// See MakeAndInsertIntoParent(). +static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) { + Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace, + getInfoRelativePath(Info.Namespace)); +} + +static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) { + Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record, + getInfoRelativePath(Info.Namespace)); +} + +static void InsertChild(ScopeChildren &Scope, EnumInfo Info) { + Scope.Enums.push_back(std::move(Info)); +} + +static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) { + Scope.Functions.push_back(std::move(Info)); +} + +static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) { + Scope.Typedefs.push_back(std::move(Info)); +} + +// Creates a parent of the correct type for the given child and inserts it into +// that parent. +// +// This is complicated by the fact that namespaces and records are inserted by +// reference (constructing a "Reference" object with that namespace/record's +// info), while everything else is inserted by moving it directly into the child +// vectors. +// +// For namespaces and records, explicitly specify a const& template parameter +// when invoking this function: +// MakeAndInsertIntoParent(...); +// Otherwise, specify an rvalue reference and move into the +// parameter. Since each variant is used once, it's not worth having a more +// elaborate system to automatically deduce this information. +template +std::unique_ptr MakeAndInsertIntoParent(ChildType Child) { + if (Child.Namespace.empty()) { + // Insert into unnamed parent namespace. + auto ParentNS = std::make_unique(); + InsertChild(ParentNS->Children, std::forward(Child)); + return ParentNS; + } + + switch (Child.Namespace[0].RefType) { + case InfoType::IT_namespace: { + auto ParentNS = std::make_unique(); + ParentNS->USR = Child.Namespace[0].USR; + InsertChild(ParentNS->Children, std::forward(Child)); + return ParentNS; + } + case InfoType::IT_record: { + auto ParentRec = std::make_unique(); + ParentRec->USR = Child.Namespace[0].USR; + InsertChild(ParentRec->Children, std::forward(Child)); + return ParentRec; + } + default: + llvm_unreachable("Invalid reference type for parent namespace"); + } +} + // There are two uses for this function. // 1) Getting the resulting mode of inheritance of a record. // Example: class A {}; class B : private A {}; class C : public B {}; @@ -376,8 +445,8 @@ static void populateParentNamespaces(llvm::SmallVector &Namespaces, const T *D, bool &IsInAnonymousNamespace) { - const auto *DC = cast(D); - while ((DC = DC->getParent())) { + const DeclContext *DC = D->getDeclContext(); + do { if (const auto *N = dyn_cast(DC)) { std::string Namespace; if (N->isAnonymousNamespace()) { @@ -396,7 +465,7 @@ else if (const auto *N = dyn_cast(DC)) Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), InfoType::IT_enum); - } + } while ((DC = DC->getParent())); // The global namespace should be added to the list of namespaces if the decl // corresponds to a Record and if it doesn't have any namespace (because this // means it's in the global namespace). Also if its outermost namespace is a @@ -501,7 +570,7 @@ IsInAnonymousNamespace); FI.Access = getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe()); - BI.ChildFunctions.emplace_back(std::move(FI)); + BI.Children.Functions.emplace_back(std::move(FI)); } I.Bases.emplace_back(std::move(BI)); // Call this function recursively to get the inherited classes of @@ -530,14 +599,9 @@ if (I->Namespace.empty() && I->USR == SymbolID()) return {std::unique_ptr{std::move(I)}, nullptr}; - auto ParentI = std::make_unique(); - ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR; - ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace, - getInfoRelativePath(I->Namespace)); - if (I->Namespace.empty()) - ParentI->Path = getInfoRelativePath(ParentI->Namespace); - return {std::unique_ptr{std::move(I)}, - std::unique_ptr{std::move(ParentI)}}; + // Namespaces are inserted into the parent by reference, so we need to return + // both the parent and the record itself. + return {std::move(I), MakeAndInsertIntoParent(*I)}; } std::pair, std::unique_ptr> @@ -563,26 +627,10 @@ } I->Path = getInfoRelativePath(I->Namespace); - switch (I->Namespace[0].RefType) { - case InfoType::IT_namespace: { - auto ParentI = std::make_unique(); - ParentI->USR = I->Namespace[0].USR; - ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, - getInfoRelativePath(I->Namespace)); - return {std::unique_ptr{std::move(I)}, - std::unique_ptr{std::move(ParentI)}}; - } - case InfoType::IT_record: { - auto ParentI = std::make_unique(); - ParentI->USR = I->Namespace[0].USR; - ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, - getInfoRelativePath(I->Namespace)); - return {std::unique_ptr{std::move(I)}, - std::unique_ptr{std::move(ParentI)}}; - } - default: - llvm_unreachable("Invalid reference type for parent namespace"); - } + // Records are inserted into the parent by reference, so we need to return + // both the parent and the record itself. + auto Parent = MakeAndInsertIntoParent(*I); + return {std::move(I), std::move(Parent)}; } std::pair, std::unique_ptr> @@ -596,17 +644,8 @@ if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) return {}; - // Wrap in enclosing scope - auto ParentI = std::make_unique(); - if (!Func.Namespace.empty()) - ParentI->USR = Func.Namespace[0].USR; - else - ParentI->USR = SymbolID(); - if (Func.Namespace.empty()) - ParentI->Path = getInfoRelativePath(ParentI->Namespace); - ParentI->ChildFunctions.emplace_back(std::move(Func)); - // Info is wrapped in its parent scope so it's returned in the second position - return {nullptr, std::unique_ptr{std::move(ParentI)}}; + // Info is wrapped in its parent scope so is returned in the second position. + return {nullptr, MakeAndInsertIntoParent(std::move(Func))}; } std::pair, std::unique_ptr> @@ -633,12 +672,52 @@ Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record}; Func.Access = D->getAccess(); - // Wrap in enclosing scope - auto ParentI = std::make_unique(); - ParentI->USR = ParentUSR; - ParentI->ChildFunctions.emplace_back(std::move(Func)); - // Info is wrapped in its parent scope so it's returned in the second position - return {nullptr, std::unique_ptr{std::move(ParentI)}}; + // Info is wrapped in its parent scope so is returned in the second position. + return {nullptr, MakeAndInsertIntoParent(std::move(Func))}; +} + +std::pair, std::unique_ptr> +emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber, + StringRef File, bool IsFileInRootDir, bool PublicOnly) { + TypedefInfo Info; + + bool IsInAnonymousNamespace = false; + populateInfo(Info, D, FC, IsInAnonymousNamespace); + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) + return {}; + + Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir); + Info.Underlying = getTypeInfoForType(D->getUnderlyingType()); + if (Info.Underlying.Type.Name.empty()) { + // Typedef for an unnamed type. This is like "typedef struct { } Foo;" + // The record serializer explicitly checks for this syntax and constructs + // a record with that name, so we don't want to emit a duplicate here. + return {}; + } + Info.IsUsing = false; + + // Info is wrapped in its parent scope so is returned in the second position. + return {nullptr, MakeAndInsertIntoParent(std::move(Info))}; +} + +// A type alias is a C++ "using" declaration for a type. It gets mapped to a +// TypedefInfo with the IsUsing flag set. +std::pair, std::unique_ptr> +emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber, + StringRef File, bool IsFileInRootDir, bool PublicOnly) { + TypedefInfo Info; + + bool IsInAnonymousNamespace = false; + populateInfo(Info, D, FC, IsInAnonymousNamespace); + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) + return {}; + + Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir); + Info.Underlying = getTypeInfoForType(D->getUnderlyingType()); + Info.IsUsing = true; + + // Info is wrapped in its parent scope so is returned in the second position. + return {nullptr, MakeAndInsertIntoParent(std::move(Info))}; } std::pair, std::unique_ptr> @@ -656,38 +735,8 @@ Enum.BaseType = TypeInfo(D->getIntegerType().getAsString()); parseEnumerators(Enum, D); - // Put in global namespace - if (Enum.Namespace.empty()) { - auto ParentI = std::make_unique(); - ParentI->USR = SymbolID(); - ParentI->ChildEnums.emplace_back(std::move(Enum)); - ParentI->Path = getInfoRelativePath(ParentI->Namespace); - // Info is wrapped in its parent scope so it's returned in the second - // position - return {nullptr, std::unique_ptr{std::move(ParentI)}}; - } - - // Wrap in enclosing scope - switch (Enum.Namespace[0].RefType) { - case InfoType::IT_namespace: { - auto ParentI = std::make_unique(); - ParentI->USR = Enum.Namespace[0].USR; - ParentI->ChildEnums.emplace_back(std::move(Enum)); - // Info is wrapped in its parent scope so it's returned in the second - // position - return {nullptr, std::unique_ptr{std::move(ParentI)}}; - } - case InfoType::IT_record: { - auto ParentI = std::make_unique(); - ParentI->USR = Enum.Namespace[0].USR; - ParentI->ChildEnums.emplace_back(std::move(Enum)); - // Info is wrapped in its parent scope so it's returned in the second - // position - return {nullptr, std::unique_ptr{std::move(ParentI)}}; - } - default: - llvm_unreachable("Invalid reference type for parent namespace"); - } + // Info is wrapped in its parent scope so is returned in the second position. + return {nullptr, MakeAndInsertIntoParent(std::move(Enum))}; } } // namespace serialize diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp --- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp @@ -23,6 +23,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(EnumValueInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(TypedefInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>) @@ -137,9 +138,10 @@ IO.mapOptional("Parents", I.Parents, llvm::SmallVector()); IO.mapOptional("VirtualParents", I.VirtualParents, llvm::SmallVector()); - IO.mapOptional("ChildRecords", I.ChildRecords, std::vector()); - IO.mapOptional("ChildFunctions", I.ChildFunctions); - IO.mapOptional("ChildEnums", I.ChildEnums); + IO.mapOptional("ChildRecords", I.Children.Records, std::vector()); + IO.mapOptional("ChildFunctions", I.Children.Functions); + IO.mapOptional("ChildEnums", I.Children.Enums); + IO.mapOptional("ChildTypedefs", I.Children.Typedefs); } static void CommentInfoMapping(IO &IO, CommentInfo &I) { @@ -203,11 +205,13 @@ template <> struct MappingTraits { static void mapping(IO &IO, NamespaceInfo &I) { InfoMapping(IO, I); - IO.mapOptional("ChildNamespaces", I.ChildNamespaces, + IO.mapOptional("ChildNamespaces", I.Children.Namespaces, std::vector()); - IO.mapOptional("ChildRecords", I.ChildRecords, std::vector()); - IO.mapOptional("ChildFunctions", I.ChildFunctions); - IO.mapOptional("ChildEnums", I.ChildEnums); + IO.mapOptional("ChildRecords", I.Children.Records, + std::vector()); + IO.mapOptional("ChildFunctions", I.Children.Functions); + IO.mapOptional("ChildEnums", I.Children.Enums); + IO.mapOptional("ChildTypedefs", I.Children.Typedefs); } }; @@ -244,6 +248,14 @@ } }; +template <> struct MappingTraits { + static void mapping(IO &IO, TypedefInfo &I) { + SymbolInfoMapping(IO, I); + IO.mapOptional("Underlying", I.Underlying.Type); + IO.mapOptional("IsUsing", I.IsUsing, false); + } +}; + template <> struct MappingTraits { static void mapping(IO &IO, FunctionInfo &I) { SymbolInfoMapping(IO, I); @@ -302,6 +314,9 @@ case InfoType::IT_function: InfoYAML << *static_cast(I); break; + case InfoType::IT_typedef: + InfoYAML << *static_cast(I); + break; case InfoType::IT_default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "unexpected InfoType"); diff --git a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp --- a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp @@ -35,6 +35,8 @@ return writeInfo(*static_cast(I)); case InfoType::IT_function: return writeInfo(*static_cast(I)); + case InfoType::IT_typedef: + return writeInfo(*static_cast(I)); default: return ""; } @@ -57,11 +59,11 @@ I.Name = "r"; I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); - I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace", - InfoType::IT_namespace); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); - I.ChildFunctions.emplace_back(); - I.ChildEnums.emplace_back(); + I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace", + InfoType::IT_namespace); + I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); + I.Children.Functions.emplace_back(); + I.Children.Enums.emplace_back(); std::string WriteResult = writeInfo(&I); EXPECT_TRUE(WriteResult.size() > 0); @@ -83,7 +85,7 @@ I.IsTypeDef = true; I.Bases.emplace_back(EmptySID, "F", "path/to/F", true, AccessSpecifier::AS_public, true); - I.Bases.back().ChildFunctions.emplace_back(); + I.Bases.back().Children.Functions.emplace_back(); I.Bases.back().Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private); I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record); @@ -101,9 +103,9 @@ Brief->Children.back()->Text = "Value of the thing."; I.Bases.back().Members.back().Description.emplace_back(std::move(TopComment)); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); - I.ChildFunctions.emplace_back(); - I.ChildEnums.emplace_back(); + I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); + I.Children.Functions.emplace_back(); + I.Children.Enums.emplace_back(); std::string WriteResult = writeInfo(&I); EXPECT_TRUE(WriteResult.size() > 0); @@ -172,6 +174,22 @@ CheckEnumInfo(&I, InfoAsEnum(ReadResults[0].get())); } +TEST(BitcodeTest, emitTypedefInfoBitcode) { + TypedefInfo I; + I.Name = "MyInt"; + I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); + + I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}); + I.Underlying = TypeInfo("unsigned"); + I.IsUsing = true; + + std::string WriteResult = writeInfo(&I); + EXPECT_TRUE(WriteResult.size() > 0); + std::vector> ReadResults = readInfo(WriteResult, 1); + + CheckTypedefInfo(&I, InfoAsTypedef(ReadResults[0].get())); +} + TEST(SerializeTest, emitInfoWithCommentBitcode) { FunctionInfo F; F.Name = "F"; diff --git a/clang-tools-extra/unittests/clang-doc/ClangDocTest.h b/clang-tools-extra/unittests/clang-doc/ClangDocTest.h --- a/clang-tools-extra/unittests/clang-doc/ClangDocTest.h +++ b/clang-tools-extra/unittests/clang-doc/ClangDocTest.h @@ -27,6 +27,7 @@ RecordInfo *InfoAsRecord(Info *I); FunctionInfo *InfoAsFunction(Info *I); EnumInfo *InfoAsEnum(Info *I); +TypedefInfo *InfoAsTypedef(Info *I); // Unlike the operator==, these functions explicitly does not check USRs, as // that may change and it would be better to not rely on its implementation. @@ -41,6 +42,7 @@ void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual); void CheckFunctionInfo(FunctionInfo *Expected, FunctionInfo *Actual); void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual); +void CheckTypedefInfo(TypedefInfo *Expected, TypedefInfo *Actual); void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual); void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual); void CheckBaseRecordInfo(BaseRecordInfo *Expected, BaseRecordInfo *Actual); diff --git a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp --- a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp @@ -34,6 +34,11 @@ return static_cast(I); } +TypedefInfo *InfoAsTypedef(Info *I) { + assert(I->IT == InfoType::IT_typedef); + return static_cast(I); +} + void CheckCommentInfo(const std::vector &Expected, const std::vector &Actual); void CheckCommentInfo(const std::vector> &Expected, @@ -144,26 +149,35 @@ EXPECT_EQ(Expected->Members[Idx], Actual->Members[Idx]); } +void CheckTypedefInfo(TypedefInfo *Expected, TypedefInfo *Actual) { + CheckSymbolInfo(Expected, Actual); + EXPECT_EQ(Expected->IsUsing, Actual->IsUsing); + CheckTypeInfo(&Expected->Underlying, &Actual->Underlying); +} + void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual) { CheckBaseInfo(Expected, Actual); - ASSERT_EQ(Expected->ChildNamespaces.size(), Actual->ChildNamespaces.size()); - for (size_t Idx = 0; Idx < Actual->ChildNamespaces.size(); ++Idx) - CheckReference(Expected->ChildNamespaces[Idx], - Actual->ChildNamespaces[Idx]); + ASSERT_EQ(Expected->Children.Namespaces.size(), + Actual->Children.Namespaces.size()); + for (size_t Idx = 0; Idx < Actual->Children.Namespaces.size(); ++Idx) + CheckReference(Expected->Children.Namespaces[Idx], + Actual->Children.Namespaces[Idx]); - ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size()); - for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx) - CheckReference(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]); + ASSERT_EQ(Expected->Children.Records.size(), Actual->Children.Records.size()); + for (size_t Idx = 0; Idx < Actual->Children.Records.size(); ++Idx) + CheckReference(Expected->Children.Records[Idx], + Actual->Children.Records[Idx]); - ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size()); - for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx) - CheckFunctionInfo(&Expected->ChildFunctions[Idx], - &Actual->ChildFunctions[Idx]); + ASSERT_EQ(Expected->Children.Functions.size(), + Actual->Children.Functions.size()); + for (size_t Idx = 0; Idx < Actual->Children.Functions.size(); ++Idx) + CheckFunctionInfo(&Expected->Children.Functions[Idx], + &Actual->Children.Functions[Idx]); - ASSERT_EQ(Expected->ChildEnums.size(), Actual->ChildEnums.size()); - for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx) - CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]); + ASSERT_EQ(Expected->Children.Enums.size(), Actual->Children.Enums.size()); + for (size_t Idx = 0; Idx < Actual->Children.Enums.size(); ++Idx) + CheckEnumInfo(&Expected->Children.Enums[Idx], &Actual->Children.Enums[Idx]); } void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) { @@ -189,18 +203,20 @@ for (size_t Idx = 0; Idx < Actual->Bases.size(); ++Idx) CheckBaseRecordInfo(&Expected->Bases[Idx], &Actual->Bases[Idx]); - ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size()); - for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx) - CheckReference(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]); + ASSERT_EQ(Expected->Children.Records.size(), Actual->Children.Records.size()); + for (size_t Idx = 0; Idx < Actual->Children.Records.size(); ++Idx) + CheckReference(Expected->Children.Records[Idx], + Actual->Children.Records[Idx]); - ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size()); - for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx) - CheckFunctionInfo(&Expected->ChildFunctions[Idx], - &Actual->ChildFunctions[Idx]); + ASSERT_EQ(Expected->Children.Functions.size(), + Actual->Children.Functions.size()); + for (size_t Idx = 0; Idx < Actual->Children.Functions.size(); ++Idx) + CheckFunctionInfo(&Expected->Children.Functions[Idx], + &Actual->Children.Functions[Idx]); - ASSERT_EQ(Expected->ChildEnums.size(), Actual->ChildEnums.size()); - for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx) - CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]); + ASSERT_EQ(Expected->Children.Enums.size(), Actual->Children.Enums.size()); + for (size_t Idx = 0; Idx < Actual->Children.Enums.size(); ++Idx) + CheckEnumInfo(&Expected->Children.Enums[Idx], &Actual->Children.Enums[Idx]); } void CheckBaseRecordInfo(BaseRecordInfo *Expected, BaseRecordInfo *Actual) { diff --git a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp --- a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp @@ -43,15 +43,15 @@ I.Name = "Namespace"; I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); - I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace", - InfoType::IT_namespace, "Namespace"); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, - "Namespace"); - I.ChildFunctions.emplace_back(); - I.ChildFunctions.back().Access = AccessSpecifier::AS_none; - I.ChildFunctions.back().Name = "OneFunction"; - I.ChildEnums.emplace_back(); - I.ChildEnums.back().Name = "OneEnum"; + I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace", + InfoType::IT_namespace, "Namespace"); + I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, + "Namespace"); + I.Children.Functions.emplace_back(); + I.Children.Functions.back().Access = AccessSpecifier::AS_none; + I.Children.Functions.back().Name = "OneFunction"; + I.Children.Enums.emplace_back(); + I.Children.Enums.back().Name = "OneEnum"; auto G = getHTMLGenerator(); assert(G); @@ -158,12 +158,12 @@ I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, PathTo); I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, - "X/Y/Z/r"); - I.ChildFunctions.emplace_back(); - I.ChildFunctions.back().Name = "OneFunction"; - I.ChildEnums.emplace_back(); - I.ChildEnums.back().Name = "OneEnum"; + I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, + "X/Y/Z/r"); + I.Children.Functions.emplace_back(); + I.Children.Functions.back().Name = "OneFunction"; + I.Children.Enums.emplace_back(); + I.Children.Enums.back().Name = "OneEnum"; auto G = getHTMLGenerator(); assert(G); diff --git a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp --- a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp @@ -26,14 +26,14 @@ I.Name = "Namespace"; I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); - I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace", - InfoType::IT_namespace); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); - I.ChildFunctions.emplace_back(); - I.ChildFunctions.back().Name = "OneFunction"; - I.ChildFunctions.back().Access = AccessSpecifier::AS_none; - I.ChildEnums.emplace_back(); - I.ChildEnums.back().Name = "OneEnum"; + I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace", + InfoType::IT_namespace); + I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); + I.Children.Functions.emplace_back(); + I.Children.Functions.back().Name = "OneFunction"; + I.Children.Functions.back().Access = AccessSpecifier::AS_none; + I.Children.Enums.emplace_back(); + I.Children.Enums.back().Name = "OneEnum"; auto G = getMDGenerator(); assert(G); @@ -90,11 +90,11 @@ I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record); I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); - I.ChildFunctions.emplace_back(); - I.ChildFunctions.back().Name = "OneFunction"; - I.ChildEnums.emplace_back(); - I.ChildEnums.back().Name = "OneEnum"; + I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); + I.Children.Functions.emplace_back(); + I.Children.Functions.back().Name = "OneFunction"; + I.Children.Enums.emplace_back(); + I.Children.Enums.back().Name = "OneEnum"; auto G = getMDGenerator(); assert(G); diff --git a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp --- a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp @@ -18,29 +18,29 @@ One.Name = "Namespace"; One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); - One.ChildNamespaces.emplace_back(NonEmptySID, "ChildNamespace", - InfoType::IT_namespace); - One.ChildRecords.emplace_back(NonEmptySID, "ChildStruct", - InfoType::IT_record); - One.ChildFunctions.emplace_back(); - One.ChildFunctions.back().Name = "OneFunction"; - One.ChildFunctions.back().USR = NonEmptySID; - One.ChildEnums.emplace_back(); - One.ChildEnums.back().Name = "OneEnum"; - One.ChildEnums.back().USR = NonEmptySID; + One.Children.Namespaces.emplace_back(NonEmptySID, "ChildNamespace", + InfoType::IT_namespace); + One.Children.Records.emplace_back(NonEmptySID, "ChildStruct", + InfoType::IT_record); + One.Children.Functions.emplace_back(); + One.Children.Functions.back().Name = "OneFunction"; + One.Children.Functions.back().USR = NonEmptySID; + One.Children.Enums.emplace_back(); + One.Children.Enums.back().Name = "OneEnum"; + One.Children.Enums.back().USR = NonEmptySID; NamespaceInfo Two; Two.Name = "Namespace"; Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); - Two.ChildNamespaces.emplace_back(EmptySID, "OtherChildNamespace", - InfoType::IT_namespace); - Two.ChildRecords.emplace_back(EmptySID, "OtherChildStruct", - InfoType::IT_record); - Two.ChildFunctions.emplace_back(); - Two.ChildFunctions.back().Name = "TwoFunction"; - Two.ChildEnums.emplace_back(); - Two.ChildEnums.back().Name = "TwoEnum"; + Two.Children.Namespaces.emplace_back(EmptySID, "OtherChildNamespace", + InfoType::IT_namespace); + Two.Children.Records.emplace_back(EmptySID, "OtherChildStruct", + InfoType::IT_record); + Two.Children.Functions.emplace_back(); + Two.Children.Functions.back().Name = "TwoFunction"; + Two.Children.Enums.emplace_back(); + Two.Children.Enums.back().Name = "TwoEnum"; std::vector> Infos; Infos.emplace_back(std::make_unique(std::move(One))); @@ -50,24 +50,24 @@ Expected->Name = "Namespace"; Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); - Expected->ChildNamespaces.emplace_back(NonEmptySID, "ChildNamespace", - InfoType::IT_namespace); - Expected->ChildRecords.emplace_back(NonEmptySID, "ChildStruct", - InfoType::IT_record); - Expected->ChildNamespaces.emplace_back(EmptySID, "OtherChildNamespace", - InfoType::IT_namespace); - Expected->ChildRecords.emplace_back(EmptySID, "OtherChildStruct", - InfoType::IT_record); - Expected->ChildFunctions.emplace_back(); - Expected->ChildFunctions.back().Name = "OneFunction"; - Expected->ChildFunctions.back().USR = NonEmptySID; - Expected->ChildFunctions.emplace_back(); - Expected->ChildFunctions.back().Name = "TwoFunction"; - Expected->ChildEnums.emplace_back(); - Expected->ChildEnums.back().Name = "OneEnum"; - Expected->ChildEnums.back().USR = NonEmptySID; - Expected->ChildEnums.emplace_back(); - Expected->ChildEnums.back().Name = "TwoEnum"; + Expected->Children.Namespaces.emplace_back(NonEmptySID, "ChildNamespace", + InfoType::IT_namespace); + Expected->Children.Records.emplace_back(NonEmptySID, "ChildStruct", + InfoType::IT_record); + Expected->Children.Namespaces.emplace_back(EmptySID, "OtherChildNamespace", + InfoType::IT_namespace); + Expected->Children.Records.emplace_back(EmptySID, "OtherChildStruct", + InfoType::IT_record); + Expected->Children.Functions.emplace_back(); + Expected->Children.Functions.back().Name = "OneFunction"; + Expected->Children.Functions.back().USR = NonEmptySID; + Expected->Children.Functions.emplace_back(); + Expected->Children.Functions.back().Name = "TwoFunction"; + Expected->Children.Enums.emplace_back(); + Expected->Children.Enums.back().Name = "OneEnum"; + Expected->Children.Enums.back().USR = NonEmptySID; + Expected->Children.Enums.emplace_back(); + Expected->Children.Enums.back().Name = "TwoEnum"; auto Actual = mergeInfos(Infos); assert(Actual); @@ -90,14 +90,14 @@ One.Bases.emplace_back(EmptySID, "F", "path/to/F", true, AccessSpecifier::AS_protected, true); - One.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct", - InfoType::IT_record); - One.ChildFunctions.emplace_back(); - One.ChildFunctions.back().Name = "OneFunction"; - One.ChildFunctions.back().USR = NonEmptySID; - One.ChildEnums.emplace_back(); - One.ChildEnums.back().Name = "OneEnum"; - One.ChildEnums.back().USR = NonEmptySID; + One.Children.Records.emplace_back(NonEmptySID, "SharedChildStruct", + InfoType::IT_record); + One.Children.Functions.emplace_back(); + One.Children.Functions.back().Name = "OneFunction"; + One.Children.Functions.back().USR = NonEmptySID; + One.Children.Enums.emplace_back(); + One.Children.Enums.back().Name = "OneEnum"; + One.Children.Enums.back().USR = NonEmptySID; RecordInfo Two; Two.Name = "r"; @@ -107,12 +107,12 @@ Two.TagType = TagTypeKind::TTK_Class; - Two.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct", - InfoType::IT_record, "path"); - Two.ChildFunctions.emplace_back(); - Two.ChildFunctions.back().Name = "TwoFunction"; - Two.ChildEnums.emplace_back(); - Two.ChildEnums.back().Name = "TwoEnum"; + Two.Children.Records.emplace_back(NonEmptySID, "SharedChildStruct", + InfoType::IT_record, "path"); + Two.Children.Functions.emplace_back(); + Two.Children.Functions.back().Name = "TwoFunction"; + Two.Children.Enums.emplace_back(); + Two.Children.Enums.back().Name = "TwoEnum"; std::vector> Infos; Infos.emplace_back(std::make_unique(std::move(One))); @@ -134,18 +134,18 @@ Expected->Bases.emplace_back(EmptySID, "F", "path/to/F", true, AccessSpecifier::AS_protected, true); - Expected->ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct", - InfoType::IT_record, "path"); - Expected->ChildFunctions.emplace_back(); - Expected->ChildFunctions.back().Name = "OneFunction"; - Expected->ChildFunctions.back().USR = NonEmptySID; - Expected->ChildFunctions.emplace_back(); - Expected->ChildFunctions.back().Name = "TwoFunction"; - Expected->ChildEnums.emplace_back(); - Expected->ChildEnums.back().Name = "OneEnum"; - Expected->ChildEnums.back().USR = NonEmptySID; - Expected->ChildEnums.emplace_back(); - Expected->ChildEnums.back().Name = "TwoEnum"; + Expected->Children.Records.emplace_back(NonEmptySID, "SharedChildStruct", + InfoType::IT_record, "path"); + Expected->Children.Functions.emplace_back(); + Expected->Children.Functions.back().Name = "OneFunction"; + Expected->Children.Functions.back().USR = NonEmptySID; + Expected->Children.Functions.emplace_back(); + Expected->Children.Functions.back().Name = "TwoFunction"; + Expected->Children.Enums.emplace_back(); + Expected->Children.Enums.back().Name = "OneEnum"; + Expected->Children.Enums.back().USR = NonEmptySID; + Expected->Children.Enums.emplace_back(); + Expected->Children.Enums.back().Name = "TwoEnum"; auto Actual = mergeInfos(Infos); assert(Actual); diff --git a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp --- a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp @@ -59,6 +59,10 @@ bool VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); } bool VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); } + + bool VisitTypedefDecl(const TypedefDecl *D) { return mapDecl(D); } + + bool VisitTypeAliasDecl(const TypeAliasDecl *D) { return mapDecl(D); } }; void ExtractInfosFromCode(StringRef Code, size_t NumExpectedInfos, bool Public, @@ -124,7 +128,7 @@ F.Namespace.emplace_back(EmptySID, "B", InfoType::IT_namespace); F.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); F.Access = AccessSpecifier::AS_none; - ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F)); CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); } @@ -183,7 +187,7 @@ InfoType::IT_namespace); EConstructor.Access = AccessSpecifier::AS_public; EConstructor.IsMethod = true; - ExpectedRecordWithEConstructor.ChildFunctions.emplace_back( + ExpectedRecordWithEConstructor.Children.Functions.emplace_back( std::move(EConstructor)); CheckRecordInfo(&ExpectedRecordWithEConstructor, RecordWithEConstructor); @@ -199,7 +203,7 @@ InfoType::IT_namespace); Method.Access = AccessSpecifier::AS_protected; Method.IsMethod = true; - ExpectedRecordWithMethod.ChildFunctions.emplace_back(std::move(Method)); + ExpectedRecordWithMethod.Children.Functions.emplace_back(std::move(Method)); CheckRecordInfo(&ExpectedRecordWithMethod, RecordWithMethod); RecordInfo *F = InfoAsRecord(Infos[4].get()); @@ -222,7 +226,7 @@ InfoType::IT_namespace); TemplateMethod.Access = AccessSpecifier::AS_public; TemplateMethod.IsMethod = true; - ExpectedRecordWithTemplateMethod.ChildFunctions.emplace_back( + ExpectedRecordWithTemplateMethod.Children.Functions.emplace_back( std::move(TemplateMethod)); CheckRecordInfo(&ExpectedRecordWithTemplateMethod, RecordWithTemplateMethod); @@ -241,7 +245,7 @@ InfoType::IT_namespace); SpecializedTemplateMethod.Access = AccessSpecifier::AS_public; SpecializedTemplateMethod.IsMethod = true; - ExpectedTemplatedRecord.ChildFunctions.emplace_back( + ExpectedTemplatedRecord.Children.Functions.emplace_back( std::move(SpecializedTemplateMethod)); CheckRecordInfo(&ExpectedTemplatedRecord, TemplatedRecord); @@ -268,7 +272,7 @@ E.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); E.Members.emplace_back("X", "0"); E.Members.emplace_back("Y", "1"); - ExpectedNamespaceWithEnum.ChildEnums.emplace_back(std::move(E)); + ExpectedNamespaceWithEnum.Children.Enums.emplace_back(std::move(E)); CheckNamespaceInfo(&ExpectedNamespaceWithEnum, NamespaceWithEnum); NamespaceInfo *NamespaceWithScopedEnum = InfoAsNamespace(Infos[1].get()); @@ -279,7 +283,7 @@ G.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); G.Members.emplace_back("A", "0"); G.Members.emplace_back("B", "1"); - ExpectedNamespaceWithScopedEnum.ChildEnums.emplace_back(std::move(G)); + ExpectedNamespaceWithScopedEnum.Children.Enums.emplace_back(std::move(G)); CheckNamespaceInfo(&ExpectedNamespaceWithScopedEnum, NamespaceWithScopedEnum); } @@ -352,7 +356,7 @@ F.ReturnType = TypeInfo("int"); F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); F.Access = AccessSpecifier::AS_none; - ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F)); CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); } @@ -368,7 +372,7 @@ F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); F.Params.emplace_back(TypeInfo("int"), "I"); F.Access = AccessSpecifier::AS_none; - ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F)); CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); } @@ -422,7 +426,8 @@ InfoType::IT_namespace); FunctionSet.Access = AccessSpecifier::AS_protected; FunctionSet.IsMethod = true; - ExpectedE.Bases.back().ChildFunctions.emplace_back(std::move(FunctionSet)); + ExpectedE.Bases.back().Children.Functions.emplace_back( + std::move(FunctionSet)); ExpectedE.Bases.emplace_back(EmptySID, /*Name=*/"G", /*Path=*/"GlobalNamespace", true, AccessSpecifier::AS_private, true); @@ -435,7 +440,8 @@ InfoType::IT_namespace); FunctionGet.Access = AccessSpecifier::AS_private; FunctionGet.IsMethod = true; - ExpectedE.Bases.back().ChildFunctions.emplace_back(std::move(FunctionGet)); + ExpectedE.Bases.back().Children.Functions.emplace_back( + std::move(FunctionGet)); ExpectedE.Bases.back().Members.emplace_back(TypeInfo("int"), "I", AccessSpecifier::AS_private); ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); @@ -468,7 +474,8 @@ InfoType::IT_namespace); FunctionSetNew.Access = AccessSpecifier::AS_private; FunctionSetNew.IsMethod = true; - ExpectedH.Bases.back().ChildFunctions.emplace_back(std::move(FunctionSetNew)); + ExpectedH.Bases.back().Children.Functions.emplace_back( + std::move(FunctionSetNew)); ExpectedH.Bases.emplace_back(EmptySID, /*Name=*/"G", /*Path=*/"GlobalNamespace", true, AccessSpecifier::AS_private, false); @@ -481,7 +488,8 @@ InfoType::IT_namespace); FunctionGetNew.Access = AccessSpecifier::AS_private; FunctionGetNew.IsMethod = true; - ExpectedH.Bases.back().ChildFunctions.emplace_back(std::move(FunctionGetNew)); + ExpectedH.Bases.back().Children.Functions.emplace_back( + std::move(FunctionGetNew)); ExpectedH.Bases.back().Members.emplace_back(TypeInfo("int"), "I", AccessSpecifier::AS_private); CheckRecordInfo(&ExpectedH, H); @@ -528,7 +536,7 @@ F.Params.emplace_back(TypeInfo("double"), "d"); F.Params.back().DefaultValue = "3.2 - 1.0"; F.Access = AccessSpecifier::AS_none; - ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F)); CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); NamespaceInfo *BWithExportedFunction = InfoAsNamespace(Infos[1].get()); @@ -540,7 +548,7 @@ ExportedF.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"}); ExportedF.Params.emplace_back(TypeInfo("double"), "y"); ExportedF.Access = AccessSpecifier::AS_none; - ExpectedBWithExportedFunction.ChildFunctions.emplace_back( + ExpectedBWithExportedFunction.Children.Functions.emplace_back( std::move(ExportedF)); CheckNamespaceInfo(&ExpectedBWithExportedFunction, BWithExportedFunction); } @@ -553,22 +561,22 @@ NamespaceInfo *ParentA = InfoAsNamespace(Infos[1].get()); NamespaceInfo ExpectedParentA(EmptySID); - ExpectedParentA.ChildRecords.emplace_back(EmptySID, "A", InfoType::IT_record, - "GlobalNamespace"); + ExpectedParentA.Children.Records.emplace_back( + EmptySID, "A", InfoType::IT_record, "GlobalNamespace"); CheckNamespaceInfo(&ExpectedParentA, ParentA); RecordInfo *ParentB = InfoAsRecord(Infos[3].get()); RecordInfo ExpectedParentB(EmptySID); llvm::SmallString<128> ExpectedParentBPath("GlobalNamespace/A"); llvm::sys::path::native(ExpectedParentBPath); - ExpectedParentB.ChildRecords.emplace_back(EmptySID, "B", InfoType::IT_record, - ExpectedParentBPath); + ExpectedParentB.Children.Records.emplace_back( + EmptySID, "B", InfoType::IT_record, ExpectedParentBPath); CheckRecordInfo(&ExpectedParentB, ParentB); NamespaceInfo *ParentC = InfoAsNamespace(Infos[7].get()); NamespaceInfo ExpectedParentC(EmptySID); - ExpectedParentC.ChildRecords.emplace_back(EmptySID, "C", InfoType::IT_record, - "@nonymous_namespace"); + ExpectedParentC.Children.Records.emplace_back( + EmptySID, "C", InfoType::IT_record, "@nonymous_namespace"); CheckNamespaceInfo(&ExpectedParentC, ParentC); } @@ -580,16 +588,43 @@ NamespaceInfo *ParentA = InfoAsNamespace(Infos[1].get()); NamespaceInfo ExpectedParentA(EmptySID); - ExpectedParentA.ChildNamespaces.emplace_back(EmptySID, "A", - InfoType::IT_namespace); + ExpectedParentA.Children.Namespaces.emplace_back(EmptySID, "A", + InfoType::IT_namespace); CheckNamespaceInfo(&ExpectedParentA, ParentA); NamespaceInfo *ParentB = InfoAsNamespace(Infos[3].get()); NamespaceInfo ExpectedParentB(EmptySID); - ExpectedParentB.ChildNamespaces.emplace_back(EmptySID, "B", - InfoType::IT_namespace, "A"); + ExpectedParentB.Children.Namespaces.emplace_back(EmptySID, "B", + InfoType::IT_namespace, "A"); CheckNamespaceInfo(&ExpectedParentB, ParentB); } +TEST(SerializeTests, emitTypedefs) { + EmittedInfoList Infos; + ExtractInfosFromCode("typedef int MyInt; using MyDouble = double;", 2, + /*Public=*/false, Infos); + + // First info will be the global namespace with the typedef in it. + NamespaceInfo *GlobalNS1 = InfoAsNamespace(Infos[0].get()); + ASSERT_EQ(1u, GlobalNS1->Children.Typedefs.size()); + + const TypedefInfo &FirstTD = GlobalNS1->Children.Typedefs[0]; + EXPECT_EQ("MyInt", FirstTD.Name); + EXPECT_FALSE(FirstTD.IsUsing); + EXPECT_EQ("int", FirstTD.Underlying.Type.Name); + + // The second will be another global namespace with the using in it (the + // global namespace is duplicated because the items haven't been merged at the + // serialization phase of processing). + NamespaceInfo *GlobalNS2 = InfoAsNamespace(Infos[1].get()); + ASSERT_EQ(1u, GlobalNS2->Children.Typedefs.size()); + + // Second is the "using" typedef. + const TypedefInfo &SecondTD = GlobalNS2->Children.Typedefs[0]; + EXPECT_EQ("MyDouble", SecondTD.Name); + EXPECT_TRUE(SecondTD.IsUsing); + EXPECT_EQ("double", SecondTD.Underlying.Type.Name); +} + } // namespace doc } // end namespace clang diff --git a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp --- a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp @@ -28,15 +28,16 @@ I.Path = "path/to/A"; I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); - I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace", - InfoType::IT_namespace, "path/to/A/Namespace"); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, - "path/to/A/Namespace"); - I.ChildFunctions.emplace_back(); - I.ChildFunctions.back().Name = "OneFunction"; - I.ChildFunctions.back().Access = AccessSpecifier::AS_none; - I.ChildEnums.emplace_back(); - I.ChildEnums.back().Name = "OneEnum"; + I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace", + InfoType::IT_namespace, + "path/to/A/Namespace"); + I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, + "path/to/A/Namespace"); + I.Children.Functions.emplace_back(); + I.Children.Functions.back().Name = "OneFunction"; + I.Children.Functions.back().Access = AccessSpecifier::AS_none; + I.Children.Enums.emplace_back(); + I.Children.Enums.back().Name = "OneEnum"; auto G = getYAMLGenerator(); assert(G); @@ -100,8 +101,8 @@ I.TagType = TagTypeKind::TTK_Class; I.Bases.emplace_back(EmptySID, "F", "path/to/F", true, AccessSpecifier::AS_public, true); - I.Bases.back().ChildFunctions.emplace_back(); - I.Bases.back().ChildFunctions.back().Name = "InheritedFunctionOne"; + I.Bases.back().Children.Functions.emplace_back(); + I.Bases.back().Children.Functions.back().Name = "InheritedFunctionOne"; I.Bases.back().Members.emplace_back(TypeInfo("int", "path/to/int"), "N", AccessSpecifier::AS_private); // F is in the global namespace @@ -109,12 +110,12 @@ I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record, "path/to/G"); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, - "path/to/A/r"); - I.ChildFunctions.emplace_back(); - I.ChildFunctions.back().Name = "OneFunction"; - I.ChildEnums.emplace_back(); - I.ChildEnums.back().Name = "OneEnum"; + I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, + "path/to/A/r"); + I.Children.Functions.emplace_back(); + I.Children.Functions.back().Name = "OneFunction"; + I.Children.Enums.emplace_back(); + I.Children.Enums.back().Name = "OneEnum"; auto G = getYAMLGenerator(); assert(G); @@ -330,6 +331,30 @@ EXPECT_EQ(Expected, Actual.str()); } +TEST(YAMLGeneratorTest, enumTypedefYAML) { + TypedefInfo I; + I.Name = "MyUsing"; + I.Underlying = TypeInfo("int"); + I.IsUsing = true; + + auto G = getYAMLGenerator(); + assert(G); + std::string Buffer; + llvm::raw_string_ostream Actual(Buffer); + auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext()); + assert(!Err); + std::string Expected = + R"raw(--- +USR: '0000000000000000000000000000000000000000' +Name: 'MyUsing' +Underlying: + Name: 'int' +IsUsing: true +... +)raw"; + EXPECT_EQ(Expected, Actual.str()); +} + TEST(YAMLGeneratorTest, emitCommentYAML) { FunctionInfo I; I.Name = "f";