[clang-doc] Serialize child namespaces and records

Serialization of child namespaces and records is now handled.
Namespaces can have child records and child namespaces.
Records can only have child records.

Committed on behalf of Diego Astiazarán (diegoaat97@gmail.com).

Differential Revision: https://reviews.llvm.org/D63911

llvm-svn: 364963
This commit is contained in:
Julie Hockett 2019-07-02 19:59:56 +00:00
parent 8055cbc449
commit 097aedc9d9
5 changed files with 176 additions and 93 deletions

View file

@ -43,9 +43,12 @@ template <typename T> bool MapASTVisitor::mapDecl(const T *D) {
// A null in place of I indicates that the serializer is skipping this decl
// for some reason (e.g. we're only reporting public decls).
if (I)
CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I->USR)),
serialize::serialize(I));
if (I.first)
CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I.first->USR)),
serialize::serialize(I.first));
if (I.second)
CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I.second->USR)),
serialize::serialize(I.second));
return true;
}

View file

@ -335,30 +335,39 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
parseParameters(I, D);
}
std::unique_ptr<Info> emitInfo(const NamespaceDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
auto I = llvm::make_unique<NamespaceInfo>();
bool IsInAnonymousNamespace = false;
populateInfo(*I, D, FC, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace || D->isAnonymousNamespace()) ||
!isPublic(D->getAccess(), D->getLinkageInternal())))
return nullptr;
return {};
I->Name = D->isAnonymousNamespace()
? llvm::SmallString<16>("@nonymous_namespace")
: I->Name;
return std::unique_ptr<Info>{std::move(I)};
if (I->Namespace.empty() && I->USR == SymbolID())
return {std::unique_ptr<Info>{std::move(I)}, nullptr};
SymbolID ParentUSR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR;
auto Parent = llvm::make_unique<NamespaceInfo>();
Parent->USR = ParentUSR;
Parent->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace);
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(Parent)}};
}
std::unique_ptr<Info> emitInfo(const RecordDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
auto I = llvm::make_unique<RecordInfo>();
bool IsInAnonymousNamespace = false;
populateSymbolInfo(*I, D, FC, LineNumber, File, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return nullptr;
return {};
I->TagType = D->getTagKind();
parseFields(*I, D, PublicOnly);
@ -369,18 +378,44 @@ std::unique_ptr<Info> emitInfo(const RecordDecl *D, const FullComment *FC,
}
parseBases(*I, C);
}
return std::unique_ptr<Info>{std::move(I)};
if (I->Namespace.empty()) {
auto Parent = llvm::make_unique<NamespaceInfo>();
Parent->USR = SymbolID();
Parent->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record);
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(Parent)}};
}
switch (I->Namespace[0].RefType) {
case InfoType::IT_namespace: {
auto Parent = llvm::make_unique<NamespaceInfo>();
Parent->USR = I->Namespace[0].USR;
Parent->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record);
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(Parent)}};
}
case InfoType::IT_record: {
auto Parent = llvm::make_unique<RecordInfo>();
Parent->USR = I->Namespace[0].USR;
Parent->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record);
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(Parent)}};
}
default:
llvm_unreachable("Invalid reference type");
}
}
std::unique_ptr<Info> emitInfo(const FunctionDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
FunctionInfo Func;
bool IsInAnonymousNamespace = false;
populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return nullptr;
return {};
Func.Access = clang::AccessSpecifier::AS_none;
@ -391,18 +426,19 @@ std::unique_ptr<Info> emitInfo(const FunctionDecl *D, const FullComment *FC,
else
I->USR = SymbolID();
I->ChildFunctions.emplace_back(std::move(Func));
return std::unique_ptr<Info>{std::move(I)};
// Info es wrapped in its parent scope so it's returned in the second position
return {nullptr, std::unique_ptr<Info>{std::move(I)}};
}
std::unique_ptr<Info> emitInfo(const CXXMethodDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
FunctionInfo Func;
bool IsInAnonymousNamespace = false;
populateFunctionInfo(Func, D, FC, LineNumber, File, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return nullptr;
return {};
Func.IsMethod = true;
@ -422,18 +458,19 @@ std::unique_ptr<Info> emitInfo(const CXXMethodDecl *D, const FullComment *FC,
auto I = llvm::make_unique<RecordInfo>();
I->USR = ParentUSR;
I->ChildFunctions.emplace_back(std::move(Func));
return std::unique_ptr<Info>{std::move(I)};
// Info is wrapped in its parent scope so it's returned in the second position
return {nullptr, std::unique_ptr<Info>{std::move(I)}};
}
std::unique_ptr<Info> emitInfo(const EnumDecl *D, const FullComment *FC,
int LineNumber, llvm::StringRef File,
bool PublicOnly) {
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
llvm::StringRef File, bool PublicOnly) {
EnumInfo Enum;
bool IsInAnonymousNamespace = false;
populateSymbolInfo(Enum, D, FC, LineNumber, File, IsInAnonymousNamespace);
if (PublicOnly && ((IsInAnonymousNamespace ||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
return nullptr;
return {};
Enum.Scoped = D->isScoped();
parseEnumerators(Enum, D);
@ -445,13 +482,17 @@ std::unique_ptr<Info> emitInfo(const EnumDecl *D, const FullComment *FC,
auto I = llvm::make_unique<NamespaceInfo>();
I->USR = Enum.Namespace[0].USR;
I->ChildEnums.emplace_back(std::move(Enum));
return std::unique_ptr<Info>{std::move(I)};
// Info is wrapped in its parent scope so it's returned in the second
// position
return {nullptr, std::unique_ptr<Info>{std::move(I)}};
}
case InfoType::IT_record: {
auto I = llvm::make_unique<RecordInfo>();
I->USR = Enum.Namespace[0].USR;
I->ChildEnums.emplace_back(std::move(Enum));
return std::unique_ptr<Info>{std::move(I)};
// Info is wrapped in its parent scope so it's returned in the second
// position
return {nullptr, std::unique_ptr<Info>{std::move(I)}};
}
default:
break;
@ -462,7 +503,8 @@ std::unique_ptr<Info> emitInfo(const EnumDecl *D, const FullComment *FC,
auto I = llvm::make_unique<NamespaceInfo>();
I->USR = SymbolID();
I->ChildEnums.emplace_back(std::move(Enum));
return std::unique_ptr<Info>{std::move(I)};
// Info is wrapped in its parent scope so it's returned in the second position
return {nullptr, std::unique_ptr<Info>{std::move(I)}};
}
} // namespace serialize

View file

@ -27,16 +27,30 @@ namespace clang {
namespace doc {
namespace serialize {
std::unique_ptr<Info> emitInfo(const NamespaceDecl *D, const FullComment *FC,
int LineNumber, StringRef File, bool PublicOnly);
std::unique_ptr<Info> emitInfo(const RecordDecl *D, const FullComment *FC,
int LineNumber, StringRef File, bool PublicOnly);
std::unique_ptr<Info> emitInfo(const EnumDecl *D, const FullComment *FC,
int LineNumber, StringRef File, bool PublicOnly);
std::unique_ptr<Info> emitInfo(const FunctionDecl *D, const FullComment *FC,
int LineNumber, StringRef File, bool PublicOnly);
std::unique_ptr<Info> emitInfo(const CXXMethodDecl *D, const FullComment *FC,
int LineNumber, StringRef File, bool PublicOnly);
// The first element will contain the relevant information about the declaration
// passed as parameter.
// The second element will contain the relevant information about the
// declaration's parent, it can be a NamespaceInfo or RecordInfo.
// Both elements can be nullptrs if the declaration shouldn't be handled.
// When the declaration is handled, the first element will be a nullptr for
// EnumDecl, FunctionDecl and CXXMethodDecl; they are only returned wrapped in
// its parent scope. For NamespaceDecl and RecordDecl both elements are not
// nullptr.
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool PublicOnly);
// Function to hash a given USR value for storage.
// As USRs (Unified Symbol Resolution) could be large, especially for functions

View file

@ -130,11 +130,12 @@ void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *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]);
CheckReference(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]);
CheckReference(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]);
ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size());
for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx)
@ -167,7 +168,7 @@ void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) {
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]);
CheckReference(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]);
ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size());
for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx)

View file

@ -35,48 +35,30 @@ public:
ClangDocSerializeTestVisitor(EmittedInfoList &EmittedInfos, bool Public)
: EmittedInfos(EmittedInfos), Public(Public) {}
bool VisitNamespaceDecl(const NamespaceDecl *D) {
template <typename T> bool mapDecl(const T *D) {
auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0,
/*File=*/"test.cpp", Public);
if (I)
EmittedInfos.emplace_back(std::move(I));
if (I.first)
EmittedInfos.emplace_back(std::move(I.first));
if (I.second)
EmittedInfos.emplace_back(std::move(I.second));
return true;
}
bool VisitNamespaceDecl(const NamespaceDecl *D) { return mapDecl(D); }
bool VisitFunctionDecl(const FunctionDecl *D) {
// Don't visit CXXMethodDecls twice
if (dyn_cast<CXXMethodDecl>(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;
return mapDecl(D);
}
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 VisitCXXMethodDecl(const CXXMethodDecl *D) { return mapDecl(D); }
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 VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); }
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;
}
bool VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); }
};
void ExtractInfosFromCode(StringRef Code, size_t NumExpectedInfos, bool Public,
@ -101,19 +83,19 @@ void ExtractInfosFromCodeWithArgs(StringRef Code, size_t NumExpectedInfos,
// Test serialization of namespace declarations.
TEST(SerializeTest, emitNamespaceInfo) {
EmittedInfoList Infos;
ExtractInfosFromCode("namespace A { namespace B { void f() {} } }", 3,
ExtractInfosFromCode("namespace A { namespace B { void f() {} } }", 5,
/*Public=*/false, Infos);
NamespaceInfo *A = InfoAsNamespace(Infos[0].get());
NamespaceInfo ExpectedA(EmptySID, "A");
CheckNamespaceInfo(&ExpectedA, A);
NamespaceInfo *B = InfoAsNamespace(Infos[1].get());
NamespaceInfo *B = InfoAsNamespace(Infos[2].get());
NamespaceInfo ExpectedB(EmptySID, "B");
ExpectedB.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
CheckNamespaceInfo(&ExpectedB, B);
NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[2].get());
NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[4].get());
NamespaceInfo ExpectedBWithFunction(EmptySID);
FunctionInfo F;
F.Name = "f";
@ -127,7 +109,7 @@ TEST(SerializeTest, emitNamespaceInfo) {
TEST(SerializeTest, emitAnonymousNamespaceInfo) {
EmittedInfoList Infos;
ExtractInfosFromCode("namespace { }", 1, /*Public=*/false, Infos);
ExtractInfosFromCode("namespace { }", 2, /*Public=*/false, Infos);
NamespaceInfo *A = InfoAsNamespace(Infos[0].get());
NamespaceInfo ExpectedA(EmptySID);
@ -151,7 +133,7 @@ struct F {
template <>
void F<int>::TemplateMethod();
typedef struct {} G;)raw",
7, /*Public=*/false, Infos);
10, /*Public=*/false, Infos);
RecordInfo *E = InfoAsRecord(Infos[0].get());
RecordInfo ExpectedE(EmptySID, "E");
@ -159,7 +141,7 @@ typedef struct {} G;)raw",
ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
CheckRecordInfo(&ExpectedE, E);
RecordInfo *RecordWithEConstructor = InfoAsRecord(Infos[1].get());
RecordInfo *RecordWithEConstructor = InfoAsRecord(Infos[2].get());
RecordInfo ExpectedRecordWithEConstructor(EmptySID);
FunctionInfo EConstructor;
EConstructor.Name = "E";
@ -173,7 +155,7 @@ typedef struct {} G;)raw",
std::move(EConstructor));
CheckRecordInfo(&ExpectedRecordWithEConstructor, RecordWithEConstructor);
RecordInfo *RecordWithMethod = InfoAsRecord(Infos[2].get());
RecordInfo *RecordWithMethod = InfoAsRecord(Infos[3].get());
RecordInfo ExpectedRecordWithMethod(EmptySID);
FunctionInfo Method;
Method.Name = "ProtectedMethod";
@ -186,13 +168,13 @@ typedef struct {} G;)raw",
ExpectedRecordWithMethod.ChildFunctions.emplace_back(std::move(Method));
CheckRecordInfo(&ExpectedRecordWithMethod, RecordWithMethod);
RecordInfo *F = InfoAsRecord(Infos[3].get());
RecordInfo *F = InfoAsRecord(Infos[4].get());
RecordInfo ExpectedF(EmptySID, "F");
ExpectedF.TagType = TagTypeKind::TTK_Struct;
ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
CheckRecordInfo(&ExpectedF, F);
RecordInfo *RecordWithTemplateMethod = InfoAsRecord(Infos[4].get());
RecordInfo *RecordWithTemplateMethod = InfoAsRecord(Infos[6].get());
RecordInfo ExpectedRecordWithTemplateMethod(EmptySID);
FunctionInfo TemplateMethod;
TemplateMethod.Name = "TemplateMethod";
@ -206,7 +188,7 @@ typedef struct {} G;)raw",
std::move(TemplateMethod));
CheckRecordInfo(&ExpectedRecordWithTemplateMethod, RecordWithTemplateMethod);
RecordInfo *TemplatedRecord = InfoAsRecord(Infos[5].get());
RecordInfo *TemplatedRecord = InfoAsRecord(Infos[7].get());
RecordInfo ExpectedTemplatedRecord(EmptySID);
FunctionInfo SpecializedTemplateMethod;
SpecializedTemplateMethod.Name = "TemplateMethod";
@ -224,7 +206,7 @@ typedef struct {} G;)raw",
std::move(SpecializedTemplateMethod));
CheckRecordInfo(&ExpectedTemplatedRecord, TemplatedRecord);
RecordInfo *G = InfoAsRecord(Infos[6].get());
RecordInfo *G = InfoAsRecord(Infos[8].get());
RecordInfo ExpectedG(EmptySID, "G");
ExpectedG.TagType = TagTypeKind::TTK_Struct;
ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
@ -262,7 +244,7 @@ TEST(SerializeTest, emitEnumInfo) {
TEST(SerializeTest, emitUndefinedRecordInfo) {
EmittedInfoList Infos;
ExtractInfosFromCode("class E;", 1, /*Public=*/false, Infos);
ExtractInfosFromCode("class E;", 2, /*Public=*/false, Infos);
RecordInfo *E = InfoAsRecord(Infos[0].get());
RecordInfo ExpectedE(EmptySID, "E");
@ -273,7 +255,7 @@ TEST(SerializeTest, emitUndefinedRecordInfo) {
TEST(SerializeTest, emitRecordMemberInfo) {
EmittedInfoList Infos;
ExtractInfosFromCode("struct E { int I; };", 1, /*Public=*/false, Infos);
ExtractInfosFromCode("struct E { int I; };", 2, /*Public=*/false, Infos);
RecordInfo *E = InfoAsRecord(Infos[0].get());
RecordInfo ExpectedE(EmptySID, "E");
@ -285,7 +267,7 @@ TEST(SerializeTest, emitRecordMemberInfo) {
TEST(SerializeTest, emitInternalRecordInfo) {
EmittedInfoList Infos;
ExtractInfosFromCode("class E { class G {}; };", 2, /*Public=*/false, Infos);
ExtractInfosFromCode("class E { class G {}; };", 4, /*Public=*/false, Infos);
RecordInfo *E = InfoAsRecord(Infos[0].get());
RecordInfo ExpectedE(EmptySID, "E");
@ -293,7 +275,7 @@ TEST(SerializeTest, emitInternalRecordInfo) {
ExpectedE.TagType = TagTypeKind::TTK_Class;
CheckRecordInfo(&ExpectedE, E);
RecordInfo *G = InfoAsRecord(Infos[1].get());
RecordInfo *G = InfoAsRecord(Infos[2].get());
RecordInfo ExpectedG(EmptySID, "G");
ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
ExpectedG.TagType = TagTypeKind::TTK_Class;
@ -336,7 +318,7 @@ TEST(SerializeTest, emitInlinedFunctionInfo) {
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
}
TEST(SerializeTest, emitInheritedRecordInfo) {
TEST(SerializeTest, ) {
EmittedInfoList Infos;
ExtractInfosFromCode(R"raw(class F {};
class G {} ;
@ -344,7 +326,7 @@ class E : public F, virtual private G {};
template <typename T>
class H {} ;
class I : public H<int> {} ;)raw",
5, /*Public=*/false, Infos);
10, /*Public=*/false, Infos);
RecordInfo *F = InfoAsRecord(Infos[0].get());
RecordInfo ExpectedF(EmptySID, "F");
@ -352,13 +334,13 @@ class I : public H<int> {} ;)raw",
ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
CheckRecordInfo(&ExpectedF, F);
RecordInfo *G = InfoAsRecord(Infos[1].get());
RecordInfo *G = InfoAsRecord(Infos[2].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 *E = InfoAsRecord(Infos[4].get());
RecordInfo ExpectedE(EmptySID, "E");
ExpectedE.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
ExpectedE.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
@ -366,13 +348,13 @@ class I : public H<int> {} ;)raw",
ExpectedE.TagType = TagTypeKind::TTK_Class;
CheckRecordInfo(&ExpectedE, E);
RecordInfo *H = InfoAsRecord(Infos[3].get());
RecordInfo *H = InfoAsRecord(Infos[6].get());
RecordInfo ExpectedH(EmptySID, "H");
ExpectedH.TagType = TagTypeKind::TTK_Class;
ExpectedH.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
CheckRecordInfo(&ExpectedH, H);
RecordInfo *I = InfoAsRecord(Infos[4].get());
RecordInfo *I = InfoAsRecord(Infos[8].get());
RecordInfo ExpectedI(EmptySID, "I");
ExpectedI.Parents.emplace_back(EmptySID, "H<int>", InfoType::IT_record);
ExpectedI.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
@ -412,5 +394,46 @@ export double exportedModuleFunction(double y);)raw",
CheckNamespaceInfo(&ExpectedBWithExportedFunction, BWithExportedFunction);
}
// Test serialization of child records in namespaces and other records
TEST(SerializeTest, emitChildRecords) {
EmittedInfoList Infos;
ExtractInfosFromCode("class A { class B {}; }; namespace { class C {}; } ", 8,
/*Public=*/false, Infos);
NamespaceInfo *ParentA = InfoAsNamespace(Infos[1].get());
NamespaceInfo ExpectedParentA(EmptySID);
ExpectedParentA.ChildRecords.emplace_back(EmptySID, "A", InfoType::IT_record);
CheckNamespaceInfo(&ExpectedParentA, ParentA);
RecordInfo *ParentB = InfoAsRecord(Infos[3].get());
RecordInfo ExpectedParentB(EmptySID);
ExpectedParentB.ChildRecords.emplace_back(EmptySID, "B", InfoType::IT_record);
CheckRecordInfo(&ExpectedParentB, ParentB);
NamespaceInfo *ParentC = InfoAsNamespace(Infos[7].get());
NamespaceInfo ExpectedParentC(EmptySID);
ExpectedParentC.ChildRecords.emplace_back(EmptySID, "C", InfoType::IT_record);
CheckNamespaceInfo(&ExpectedParentC, ParentC);
}
// Test serialization of child namespaces
TEST(SerializeTest, emitChildNamespaces) {
EmittedInfoList Infos;
ExtractInfosFromCode("namespace A { namespace B { } }", 4, /*Public=*/false,
Infos);
NamespaceInfo *ParentA = InfoAsNamespace(Infos[1].get());
NamespaceInfo ExpectedParentA(EmptySID);
ExpectedParentA.ChildNamespaces.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);
CheckNamespaceInfo(&ExpectedParentB, ParentB);
}
} // namespace doc
} // end namespace clang