Index: unittests/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(change-namespace) add_subdirectory(clang-apply-replacements) +add_subdirectory(clang-doc) add_subdirectory(clang-move) add_subdirectory(clang-query) add_subdirectory(clang-tidy) Index: unittests/clang-doc/CMakeLists.txt =================================================================== --- unittests/clang-doc/CMakeLists.txt +++ unittests/clang-doc/CMakeLists.txt @@ -0,0 +1,29 @@ +set(LLVM_LINK_COMPONENTS + support + BitReader + BitWriter + ) + +get_filename_component(CLANG_DOC_SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-doc REALPATH) +include_directories( + ${CLANG_DOC_SOURCE_DIR} + ) + +add_extra_unittest(ClangDocTests + ClangDocTest.cpp + SerializeTest.cpp + ) + +target_link_libraries(ClangDocTests + PRIVATE + clangAST + clangASTMatchers + clangBasic + clangDoc + clangFormat + clangFrontend + clangRewrite + clangTooling + clangToolingCore + ) Index: unittests/clang-doc/ClangDocTest.h =================================================================== --- unittests/clang-doc/ClangDocTest.h +++ unittests/clang-doc/ClangDocTest.h @@ -0,0 +1,51 @@ +//===-- clang-doc/ClangDocTest.h ------------------------------------------===// +// +// 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_UNITTESTS_CLANG_DOC_CLANGDOCTEST_H +#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_DOC_CLANGDOCTEST_H + +#include "ClangDocTest.h" +#include "Representation.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "gtest/gtest.h" + +namespace clang { +namespace doc { + +using EmittedInfoList = std::vector>; + +static const SymbolID EmptySID = SymbolID(); +static const SymbolID NonEmptySID = + SymbolID{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +NamespaceInfo *InfoAsNamespace(Info *I); +RecordInfo *InfoAsRecord(Info *I); +FunctionInfo *InfoAsFunction(Info *I); +EnumInfo *InfoAsEnum(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. +void CheckReference(Reference &Expected, Reference &Actual); +void CheckTypeInfo(TypeInfo *Expected, TypeInfo *Actual); +void CheckFieldTypeInfo(FieldTypeInfo *Expected, FieldTypeInfo *Actual); +void CheckMemberTypeInfo(MemberTypeInfo *Expected, MemberTypeInfo *Actual); + +// This function explicitly does not check USRs, as that may change and it would +// be better to not rely on its implementation. +void CheckBaseInfo(Info *Expected, Info *Actual); +void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual); +void CheckFunctionInfo(FunctionInfo *Expected, FunctionInfo *Actual); +void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual); +void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual); +void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual); + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_DOC_CLANGDOCTEST_H Index: unittests/clang-doc/ClangDocTest.cpp =================================================================== --- unittests/clang-doc/ClangDocTest.cpp +++ unittests/clang-doc/ClangDocTest.cpp @@ -0,0 +1,182 @@ +//===-- clang-doc/ClangDocTest.cpp ----------------------------------------===// +// +// 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 "clang/AST/RecursiveASTVisitor.h" +#include "gtest/gtest.h" + +namespace clang { +namespace doc { + +NamespaceInfo *InfoAsNamespace(Info *I) { + assert(I->IT == InfoType::IT_namespace); + return static_cast(I); +} + +RecordInfo *InfoAsRecord(Info *I) { + assert(I->IT == InfoType::IT_record); + return static_cast(I); +} + +FunctionInfo *InfoAsFunction(Info *I) { + assert(I->IT == InfoType::IT_function); + return static_cast(I); +} + +EnumInfo *InfoAsEnum(Info *I) { + assert(I->IT == InfoType::IT_enum); + return static_cast(I); +} + +void CheckCommentInfo(CommentInfo &Expected, CommentInfo &Actual) { + EXPECT_EQ(Expected.Kind, Actual.Kind); + EXPECT_EQ(Expected.Text, Actual.Text); + EXPECT_EQ(Expected.Name, Actual.Name); + EXPECT_EQ(Expected.Direction, Actual.Direction); + EXPECT_EQ(Expected.ParamName, Actual.ParamName); + EXPECT_EQ(Expected.CloseName, Actual.CloseName); + EXPECT_EQ(Expected.SelfClosing, Actual.SelfClosing); + EXPECT_EQ(Expected.Explicit, Actual.Explicit); + + ASSERT_EQ(Expected.AttrKeys.size(), Actual.AttrKeys.size()); + for (size_t Idx = 0; Idx < Actual.AttrKeys.size(); ++Idx) + EXPECT_EQ(Expected.AttrKeys[Idx], Actual.AttrKeys[Idx]); + + ASSERT_EQ(Expected.AttrValues.size(), Actual.AttrValues.size()); + for (size_t Idx = 0; Idx < Actual.AttrValues.size(); ++Idx) + EXPECT_EQ(Expected.AttrValues[Idx], Actual.AttrValues[Idx]); + + ASSERT_EQ(Expected.Args.size(), Actual.Args.size()); + for (size_t Idx = 0; Idx < Actual.Args.size(); ++Idx) + EXPECT_EQ(Expected.Args[Idx], Actual.Args[Idx]); + + ASSERT_EQ(Expected.Children.size(), Actual.Children.size()); + for (size_t Idx = 0; Idx < Actual.Children.size(); ++Idx) + CheckCommentInfo(*Expected.Children[Idx], *Actual.Children[Idx]); +} + +void CheckReference(Reference &Expected, Reference &Actual) { + EXPECT_EQ(Expected.Name, Actual.Name); + EXPECT_EQ(Expected.RefType, Actual.RefType); +} + +void CheckTypeInfo(TypeInfo *Expected, TypeInfo *Actual) { + CheckReference(Expected->Type, Actual->Type); +} + +void CheckFieldTypeInfo(FieldTypeInfo *Expected, FieldTypeInfo *Actual) { + CheckTypeInfo(Expected, Actual); + EXPECT_EQ(Expected->Name, Actual->Name); +} + +void CheckMemberTypeInfo(MemberTypeInfo *Expected, MemberTypeInfo *Actual) { + CheckFieldTypeInfo(Expected, Actual); + EXPECT_EQ(Expected->Access, Actual->Access); +} + +void CheckBaseInfo(Info *Expected, Info *Actual) { + EXPECT_EQ(size_t(20), Actual->USR.size()); + EXPECT_EQ(Expected->Name, Actual->Name); + ASSERT_EQ(Expected->Namespace.size(), Actual->Namespace.size()); + for (size_t Idx = 0; Idx < Actual->Namespace.size(); ++Idx) + CheckReference(Expected->Namespace[Idx], Actual->Namespace[Idx]); + ASSERT_EQ(Expected->Description.size(), Actual->Description.size()); + for (size_t Idx = 0; Idx < Actual->Description.size(); ++Idx) + CheckCommentInfo(Expected->Description[Idx], Actual->Description[Idx]); +} + +void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual) { + CheckBaseInfo(Expected, Actual); + EXPECT_EQ(Expected->DefLoc.hasValue(), Actual->DefLoc.hasValue()); + if (Expected->DefLoc.hasValue() && Actual->DefLoc.hasValue()) { + EXPECT_EQ(Expected->DefLoc->LineNumber, Actual->DefLoc->LineNumber); + EXPECT_EQ(Expected->DefLoc->Filename, Actual->DefLoc->Filename); + } + ASSERT_EQ(Expected->Loc.size(), Actual->Loc.size()); + for (size_t Idx = 0; Idx < Actual->Loc.size(); ++Idx) + EXPECT_EQ(Expected->Loc[Idx], Actual->Loc[Idx]); +} + +void CheckFunctionInfo(FunctionInfo *Expected, FunctionInfo *Actual) { + CheckSymbolInfo(Expected, Actual); + + EXPECT_EQ(Expected->IsMethod, Actual->IsMethod); + CheckReference(Expected->Parent, Actual->Parent); + CheckTypeInfo(&Expected->ReturnType, &Actual->ReturnType); + + ASSERT_EQ(Expected->Params.size(), Actual->Params.size()); + for (size_t Idx = 0; Idx < Actual->Params.size(); ++Idx) + EXPECT_EQ(Expected->Params[Idx], Actual->Params[Idx]); + + EXPECT_EQ(Expected->Access, Actual->Access); +} + +void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual) { + CheckSymbolInfo(Expected, Actual); + + EXPECT_EQ(Expected->Scoped, Actual->Scoped); + ASSERT_EQ(Expected->Members.size(), Actual->Members.size()); + for (size_t Idx = 0; Idx < Actual->Members.size(); ++Idx) + EXPECT_EQ(Expected->Members[Idx], Actual->Members[Idx]); +} + +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) + EXPECT_EQ(Expected->ChildNamespaces[Idx], Actual->ChildNamespaces[Idx]); + + ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size()); + for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx) + EXPECT_EQ(Expected->ChildRecords[Idx], Actual->ChildRecords[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->ChildEnums.size(), Actual->ChildEnums.size()); + for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx) + CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]); +} + +void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) { + CheckSymbolInfo(Expected, Actual); + + EXPECT_EQ(Expected->TagType, Actual->TagType); + + ASSERT_EQ(Expected->Members.size(), Actual->Members.size()); + for (size_t Idx = 0; Idx < Actual->Members.size(); ++Idx) + EXPECT_EQ(Expected->Members[Idx], Actual->Members[Idx]); + + ASSERT_EQ(Expected->Parents.size(), Actual->Parents.size()); + for (size_t Idx = 0; Idx < Actual->Parents.size(); ++Idx) + CheckReference(Expected->Parents[Idx], Actual->Parents[Idx]); + + ASSERT_EQ(Expected->VirtualParents.size(), Actual->VirtualParents.size()); + for (size_t Idx = 0; Idx < Actual->VirtualParents.size(); ++Idx) + CheckReference(Expected->VirtualParents[Idx], Actual->VirtualParents[Idx]); + + ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size()); + for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx) + EXPECT_EQ(Expected->ChildRecords[Idx], Actual->ChildRecords[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->ChildEnums.size(), Actual->ChildEnums.size()); + for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx) + CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]); +} + +} // namespace doc +} // namespace clang Index: unittests/clang-doc/SerializeTest.cpp =================================================================== --- unittests/clang-doc/SerializeTest.cpp +++ unittests/clang-doc/SerializeTest.cpp @@ -0,0 +1,346 @@ +//===-- clang-doc/SerializeTest.cpp ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Serialize.h" +#include "ClangDocTest.h" +#include "Representation.h" +#include "clang/AST/Comment.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "gtest/gtest.h" + +namespace clang { +namespace doc { + +class ClangDocSerializeTestVisitor + : public RecursiveASTVisitor { + + EmittedInfoList &EmittedInfos; + bool Public; + + comments::FullComment *getComment(const NamedDecl *D) const { + if (RawComment *Comment = + D->getASTContext().getRawCommentForDeclNoCache(D)) { + Comment->setAttached(); + return Comment->parse(D->getASTContext(), nullptr, D); + } + return nullptr; + } + +public: + ClangDocSerializeTestVisitor(EmittedInfoList &EmittedInfos, bool Public) + : EmittedInfos(EmittedInfos), Public(Public) {} + + bool VisitNamespaceDecl(const NamespaceDecl *D) { + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } + + bool VisitFunctionDecl(const FunctionDecl *D) { + // Don't visit CXXMethodDecls twice + if (dyn_cast(D)) + return true; + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } + + bool VisitCXXMethodDecl(const CXXMethodDecl *D) { + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } + + bool VisitRecordDecl(const RecordDecl *D) { + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } + + bool VisitEnumDecl(const EnumDecl *D) { + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } +}; + +void ExtractInfosFromCode(StringRef Code, size_t NumExpectedInfos, bool Public, + EmittedInfoList &EmittedInfos) { + auto ASTUnit = clang::tooling::buildASTFromCode(Code); + auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); + ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public); + Visitor.TraverseTranslationUnitDecl(TU); + ASSERT_EQ(NumExpectedInfos, EmittedInfos.size()); +} + +void ExtractInfosFromCodeWithArgs(StringRef Code, size_t NumExpectedInfos, + bool Public, EmittedInfoList &EmittedInfos, + std::vector &Args) { + auto ASTUnit = clang::tooling::buildASTFromCodeWithArgs(Code, Args); + auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); + ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public); + Visitor.TraverseTranslationUnitDecl(TU); + ASSERT_EQ(NumExpectedInfos, EmittedInfos.size()); +} + +// Test serialization of namespace declarations. +TEST(SerializeTest, emitNamespaceInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("namespace A { namespace B { void f() {} } }", 3, + /*Public=*/false, Infos); + + NamespaceInfo *A = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedA(EmptySID, "A"); + CheckNamespaceInfo(&ExpectedA, A); + + NamespaceInfo *B = InfoAsNamespace(Infos[1].get()); + NamespaceInfo ExpectedB(EmptySID, "B"); + ExpectedB.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); + CheckNamespaceInfo(&ExpectedB, B); + + NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[2].get()); + NamespaceInfo ExpectedBWithFunction(EmptySID); + FunctionInfo F; + F.Name = "f"; + F.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); + F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + F.Namespace.emplace_back(EmptySID, "B", InfoType::IT_namespace); + F.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); + ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); +} + +TEST(SerializeTest, emitAnonymousNamespaceInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("namespace { }", 1, /*Public=*/false, Infos); + + NamespaceInfo *A = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedA(EmptySID); + CheckNamespaceInfo(&ExpectedA, A); +} + +// Test serialization of record declarations. +TEST(SerializeTest, emitRecordInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode(R"raw(class E { +public: + E() {} +protected: + void ProtectedMethod(); +};)raw", 3, /*Public=*/false, Infos); + + RecordInfo *E = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.TagType = TagTypeKind::TTK_Class; + ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + CheckRecordInfo(&ExpectedE, E); + + RecordInfo *RecordWithEConstructor = InfoAsRecord(Infos[1].get()); + RecordInfo ExpectedRecordWithEConstructor(EmptySID); + FunctionInfo EConstructor; + EConstructor.Name = "E"; + EConstructor.Parent = Reference(EmptySID, "E", InfoType::IT_record); + EConstructor.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); + EConstructor.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + EConstructor.Namespace.emplace_back(EmptySID, "E", InfoType::IT_record); + EConstructor.Access = AccessSpecifier::AS_public; + EConstructor.IsMethod = true; + ExpectedRecordWithEConstructor.ChildFunctions.emplace_back( + std::move(EConstructor)); + CheckRecordInfo(&ExpectedRecordWithEConstructor, RecordWithEConstructor); + + RecordInfo *RecordWithMethod = InfoAsRecord(Infos[2].get()); + RecordInfo ExpectedRecordWithMethod(EmptySID); + FunctionInfo Method; + Method.Name = "ProtectedMethod"; + Method.Parent = Reference(EmptySID, "E", InfoType::IT_record); + Method.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); + Method.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"}); + Method.Namespace.emplace_back(EmptySID, "E", InfoType::IT_record); + Method.Access = AccessSpecifier::AS_protected; + Method.IsMethod = true; + ExpectedRecordWithMethod.ChildFunctions.emplace_back(std::move(Method)); + CheckRecordInfo(&ExpectedRecordWithMethod, RecordWithMethod); +} + +// Test serialization of enum declarations. +TEST(SerializeTest, emitEnumInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("enum E { X, Y }; enum class G { A, B };", 2, + /*Public=*/false, Infos); + + NamespaceInfo *NamespaceWithEnum = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedNamespaceWithEnum(EmptySID); + EnumInfo E; + E.Name = "E"; + E.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + E.Members.emplace_back("X"); + E.Members.emplace_back("Y"); + ExpectedNamespaceWithEnum.ChildEnums.emplace_back(std::move(E)); + CheckNamespaceInfo(&ExpectedNamespaceWithEnum, NamespaceWithEnum); + + NamespaceInfo *NamespaceWithScopedEnum = InfoAsNamespace(Infos[1].get()); + NamespaceInfo ExpectedNamespaceWithScopedEnum(EmptySID); + EnumInfo G; + G.Name = "G"; + G.Scoped = true; + G.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + G.Members.emplace_back("A"); + G.Members.emplace_back("B"); + ExpectedNamespaceWithScopedEnum.ChildEnums.emplace_back(std::move(G)); + CheckNamespaceInfo(&ExpectedNamespaceWithScopedEnum, NamespaceWithScopedEnum); +} + +TEST(SerializeTest, emitUndefinedRecordInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("class E;", 1, /*Public=*/false, Infos); + + RecordInfo *E = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.TagType = TagTypeKind::TTK_Class; + ExpectedE.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"}); + CheckRecordInfo(&ExpectedE, E); +} + +TEST(SerializeTest, emitRecordMemberInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("struct E { int I; };", 1, /*Public=*/false, Infos); + + RecordInfo *E = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.TagType = TagTypeKind::TTK_Struct; + ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedE.Members.emplace_back("int", "I", AccessSpecifier::AS_public); + CheckRecordInfo(&ExpectedE, E); +} + +TEST(SerializeTest, emitInternalRecordInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("class E { class G {}; };", 2, /*Public=*/false, Infos); + + RecordInfo *E = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedE.TagType = TagTypeKind::TTK_Class; + CheckRecordInfo(&ExpectedE, E); + + RecordInfo *G = InfoAsRecord(Infos[1].get()); + RecordInfo ExpectedG(EmptySID, "G"); + ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedG.TagType = TagTypeKind::TTK_Class; + ExpectedG.Namespace.emplace_back(EmptySID, "E", InfoType::IT_record); + CheckRecordInfo(&ExpectedG, G); +} + +TEST(SerializeTest, emitPublicAnonymousNamespaceInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("namespace { class A; }", 0, /*Public=*/true, Infos); +} + +TEST(SerializeTest, emitPublicFunctionInternalInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("int F() { class G {}; return 0; };", 1, /*Public=*/true, + Infos); + + NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedBWithFunction(EmptySID); + FunctionInfo F; + F.Name = "F"; + F.ReturnType = TypeInfo(EmptySID, "int", InfoType::IT_default); + F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); +} + +TEST(SerializeTest, emitInlinedFunctionInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("inline void F(int I) { };", 1, /*Public=*/true, Infos); + + NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedBWithFunction(EmptySID); + FunctionInfo F; + F.Name = "F"; + F.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); + F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + F.Params.emplace_back("int", "I"); + ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); +} + +TEST(SerializeTest, emitInheritedRecordInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode( + "class F {}; class G{} ; class E : public F, virtual private G {};", 3, + /*Public=*/false, Infos); + + RecordInfo *F = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedF(EmptySID, "F"); + ExpectedF.TagType = TagTypeKind::TTK_Class; + ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + CheckRecordInfo(&ExpectedF, F); + + RecordInfo *G = InfoAsRecord(Infos[1].get()); + RecordInfo ExpectedG(EmptySID, "G"); + ExpectedG.TagType = TagTypeKind::TTK_Class; + ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + CheckRecordInfo(&ExpectedG, G); + + RecordInfo *E = InfoAsRecord(Infos[2].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.Parents.emplace_back(EmptySID, "F", InfoType::IT_record); + ExpectedE.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); + ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedE.TagType = TagTypeKind::TTK_Class; + CheckRecordInfo(&ExpectedE, E); +} + +TEST(SerializeTest, emitModulePublicLFunctions) { + EmittedInfoList Infos; + std::vector Args; + Args.push_back("-fmodules-ts"); + ExtractInfosFromCodeWithArgs(R"raw(export module M; +int moduleFunction(int x); +static int staticModuleFunction(int x); +export double exportedModuleFunction(double y);)raw", + 2, /*Public=*/true, Infos, Args); + + NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedBWithFunction(EmptySID); + FunctionInfo F; + F.Name = "moduleFunction"; + F.ReturnType = TypeInfo(EmptySID, "int", InfoType::IT_default); + F.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"}); + F.Params.emplace_back("int", "x"); + ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); + + NamespaceInfo *BWithExportedFunction = InfoAsNamespace(Infos[1].get()); + NamespaceInfo ExpectedBWithExportedFunction(EmptySID); + FunctionInfo ExportedF; + ExportedF.Name = "exportedModuleFunction"; + ExportedF.ReturnType = TypeInfo(EmptySID, "double", InfoType::IT_default); + ExportedF.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"}); + ExportedF.Params.emplace_back("double", "y"); + ExpectedBWithExportedFunction.ChildFunctions.emplace_back( + std::move(ExportedF)); + CheckNamespaceInfo(&ExpectedBWithExportedFunction, BWithExportedFunction); +} + +} // namespace doc +} // end namespace clang