Files
TheRock/compiler/amd-llvm/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

831 lines
33 KiB
C++

//===-- clang-doc/SerializeTest.cpp ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#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 SerializeTest : public ClangDocContextTest {};
class ClangDocSerializeTestVisitor
: public RecursiveASTVisitor<ClangDocSerializeTestVisitor> {
EmittedInfoList &EmittedInfos;
bool Public;
comments::FullComment *getComment(const NamedDecl *D) const {
if (RawComment *Comment = D->getASTContext().getRawCommentNoCache(D)) {
Comment->setAttached();
return Comment->parse(D->getASTContext(), nullptr, D);
}
return nullptr;
}
public:
ClangDocSerializeTestVisitor(EmittedInfoList &EmittedInfos, bool Public,
DiagnosticsEngine &Diags)
: EmittedInfos(EmittedInfos), Public(Public) {}
template <typename T> bool mapDecl(const T *D) {
Location Loc(0, 0, "test.cpp");
serialize::Serializer S;
auto [Child, Parent] = S.emitInfo(D, getComment(D), Loc, Public);
if (Child)
EmittedInfos.emplace_back(std::move(Child));
if (Parent)
EmittedInfos.emplace_back(std::move(Parent));
return true;
}
bool VisitNamespaceDecl(const NamespaceDecl *D) { return mapDecl(D); }
bool VisitFunctionDecl(const FunctionDecl *D) {
// Don't visit CXXMethodDecls twice
if (isa<CXXMethodDecl>(D))
return true;
return mapDecl(D);
}
bool VisitCXXMethodDecl(const CXXMethodDecl *D) { return mapDecl(D); }
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); }
};
static void extractInfosFromCode(StringRef Code, size_t NumExpectedInfos,
bool Public, EmittedInfoList &EmittedInfos,
DiagnosticsEngine &Diags) {
auto ASTUnit = clang::tooling::buildASTFromCode(Code);
TranslationUnitDecl *TU = ASTUnit->getASTContext().getTranslationUnitDecl();
ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public, Diags);
Visitor.TraverseTranslationUnitDecl(TU);
ASSERT_EQ(NumExpectedInfos, EmittedInfos.size());
}
static void extractInfosFromCodeWithArgs(StringRef Code,
size_t NumExpectedInfos, bool Public,
EmittedInfoList &EmittedInfos,
std::vector<std::string> &Args,
DiagnosticsEngine &Diags) {
auto ASTUnit = clang::tooling::buildASTFromCodeWithArgs(Code, Args);
TranslationUnitDecl *TU = ASTUnit->getASTContext().getTranslationUnitDecl();
ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public, Diags);
Visitor.TraverseTranslationUnitDecl(TU);
ASSERT_EQ(NumExpectedInfos, EmittedInfos.size());
}
// Constructs a comment definition as the parser would for one comment line.
/* TODO uncomment this when the missing comment is fixed in emitRecordInfo and
the code that calls this is re-enabled.
CommentInfo MakeOneLineCommentInfo(const std::string &Text) {
CommentInfo TopComment;
TopComment.Kind = "FullComment";
TopComment.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *Brief = TopComment.Children.back().get();
Brief->Kind = "ParagraphComment";
Brief->Children.emplace_back(allocatePtr<CommentInfo>());
Brief->Children.back()->Kind = "TextComment";
Brief->Children.back()->Name = "ParagraphComment";
Brief->Children.back()->Text = Text;
return TopComment;
}
*/
// Test serialization of namespace declarations.
TEST_F(SerializeTest, emitNamespaceInfo) {
EmittedInfoList Infos;
extractInfosFromCode("namespace A { namespace B { void f() {} } }", 5,
/*Public=*/false, Infos, this->Diags);
NamespaceInfo *A = InfoAsNamespace(Infos[0]);
NamespaceInfo ExpectedA(EmptySID, "A");
CheckNamespaceInfo(&ExpectedA, A);
NamespaceInfo *B = InfoAsNamespace(Infos[2]);
NamespaceInfo ExpectedB(EmptySID, /*Name=*/"B", /*Path=*/"A");
Reference NsB[] = {Reference(EmptySID, "A", InfoType::IT_namespace)};
ExpectedB.Namespace = llvm::ArrayRef(NsB);
CheckNamespaceInfo(&ExpectedB, B);
NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[4]);
NamespaceInfo ExpectedBWithFunction(EmptySID);
FunctionInfo F;
F.Name = "f";
F.ReturnType = TypeInfo("void");
F.DefLoc = Location(0, 0, "test.cpp");
Reference NsF[] = {Reference(EmptySID, "B", InfoType::IT_namespace),
Reference(EmptySID, "A", InfoType::IT_namespace)};
F.Namespace = llvm::ArrayRef(NsF);
F.Access = AccessSpecifier::AS_none;
InfoNode<FunctionInfo> FNode(&F);
ExpectedBWithFunction.Children.Functions.push_back(FNode);
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
}
TEST_F(SerializeTest, emitAnonymousNamespaceInfo) {
EmittedInfoList Infos;
extractInfosFromCode("namespace { }", 2, /*Public=*/false, Infos,
this->Diags);
NamespaceInfo *A = InfoAsNamespace(Infos[0]);
NamespaceInfo ExpectedA(EmptySID);
ExpectedA.Name = "@nonymous_namespace";
CheckNamespaceInfo(&ExpectedA, A);
}
TEST_F(SerializeTest, emitRecordInfo) {
EmittedInfoList Infos;
extractInfosFromCode(R"raw(class E {
public:
E() {}
// Some docs.
int value;
protected:
void ProtectedMethod();
};
template <typename T>
struct F {
void TemplateMethod();
};
template <>
void F<int>::TemplateMethod();
typedef struct {} G;)raw",
10, /*Public=*/false, Infos, this->Diags);
RecordInfo *E = InfoAsRecord(Infos[0]);
RecordInfo ExpectedE(EmptySID, /*Name=*/"E", /*Path=*/"GlobalNamespace");
Reference NsE[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedE.Namespace = llvm::ArrayRef(NsE);
ExpectedE.TagType = TagTypeKind::Class;
ExpectedE.DefLoc = Location(0, 0, "test.cpp");
MemberTypeInfo MemE[] = {
MemberTypeInfo(TypeInfo("int"), "value", AccessSpecifier::AS_public)};
ExpectedE.Members = llvm::ArrayRef(MemE);
// TODO the data member should have the docstring on it:
//ExpectedE.Members.back().Description.push_back(MakeOneLineCommentInfo(" Some docs"));
CheckRecordInfo(&ExpectedE, E);
RecordInfo *RecordWithEConstructor = InfoAsRecord(Infos[2]);
RecordInfo ExpectedRecordWithEConstructor(EmptySID);
FunctionInfo EConstructor;
EConstructor.Name = "E";
EConstructor.Parent = Reference(EmptySID, "E", InfoType::IT_record);
EConstructor.ReturnType = TypeInfo("void");
EConstructor.DefLoc = Location(0, 0, "test.cpp");
Reference NsEC[] = {
Reference(EmptySID, "E", InfoType::IT_record),
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
EConstructor.Namespace = llvm::ArrayRef(NsEC);
EConstructor.Access = AccessSpecifier::AS_public;
EConstructor.IsMethod = true;
InfoNode<FunctionInfo> EConstructorNode(&EConstructor);
ExpectedRecordWithEConstructor.Children.Functions.push_back(EConstructorNode);
CheckRecordInfo(&ExpectedRecordWithEConstructor, RecordWithEConstructor);
RecordInfo *RecordWithMethod = InfoAsRecord(Infos[3]);
RecordInfo ExpectedRecordWithMethod(EmptySID);
FunctionInfo Method;
Method.Name = "ProtectedMethod";
Method.Parent = Reference(EmptySID, "E", InfoType::IT_record);
Method.ReturnType = TypeInfo("void");
Location LMethod(0, 0, "test.cpp");
InfoNode<Location> LMethodNode(&LMethod);
Method.Loc.push_back(LMethodNode);
Reference NsMethod[] = {
Reference(EmptySID, "E", InfoType::IT_record),
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
Method.Namespace = llvm::ArrayRef(NsMethod);
Method.Access = AccessSpecifier::AS_protected;
Method.IsMethod = true;
InfoNode<FunctionInfo> MethodNode(&Method);
ExpectedRecordWithMethod.Children.Functions.push_back(MethodNode);
CheckRecordInfo(&ExpectedRecordWithMethod, RecordWithMethod);
RecordInfo *F = InfoAsRecord(Infos[4]);
RecordInfo ExpectedF(EmptySID, /*Name=*/"F", /*Path=*/"GlobalNamespace");
Reference NsF3[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedF.Namespace = llvm::ArrayRef(NsF3);
ExpectedF.TagType = TagTypeKind::Struct;
ExpectedF.DefLoc = Location(0, 0, "test.cpp");
CheckRecordInfo(&ExpectedF, F);
RecordInfo *RecordWithTemplateMethod = InfoAsRecord(Infos[6]);
RecordInfo ExpectedRecordWithTemplateMethod(EmptySID);
FunctionInfo TemplateMethod;
TemplateMethod.Name = "TemplateMethod";
TemplateMethod.Parent = Reference(EmptySID, "F", InfoType::IT_record);
TemplateMethod.ReturnType = TypeInfo("void");
Location LTemp1(0, 0, "test.cpp");
InfoNode<Location> LTemp1Node(&LTemp1);
TemplateMethod.Loc.push_back(LTemp1Node);
Reference NsT1[] = {
Reference(EmptySID, "F", InfoType::IT_record),
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
TemplateMethod.Namespace = llvm::ArrayRef(NsT1);
TemplateMethod.Access = AccessSpecifier::AS_public;
TemplateMethod.IsMethod = true;
InfoNode<FunctionInfo> TemplateMethodNode(&TemplateMethod);
ExpectedRecordWithTemplateMethod.Children.Functions.push_back(
TemplateMethodNode);
CheckRecordInfo(&ExpectedRecordWithTemplateMethod, RecordWithTemplateMethod);
RecordInfo *TemplatedRecord = InfoAsRecord(Infos[7]);
RecordInfo ExpectedTemplatedRecord(EmptySID);
FunctionInfo SpecializedTemplateMethod;
SpecializedTemplateMethod.Name = "TemplateMethod";
SpecializedTemplateMethod.Parent =
Reference(EmptySID, "F", InfoType::IT_record);
SpecializedTemplateMethod.ReturnType = TypeInfo("void");
Location LTemp2(0, 0, "test.cpp");
InfoNode<Location> LTemp2Node(&LTemp2);
SpecializedTemplateMethod.Loc.push_back(LTemp2Node);
Reference NsT2[] = {
Reference(EmptySID, "F", InfoType::IT_record),
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
SpecializedTemplateMethod.Namespace = llvm::ArrayRef(NsT2);
SpecializedTemplateMethod.Access = AccessSpecifier::AS_public;
SpecializedTemplateMethod.IsMethod = true;
InfoNode<FunctionInfo> SpecializedTemplateMethodNode(
&SpecializedTemplateMethod);
ExpectedTemplatedRecord.Children.Functions.push_back(
SpecializedTemplateMethodNode);
CheckRecordInfo(&ExpectedTemplatedRecord, TemplatedRecord);
RecordInfo *G = InfoAsRecord(Infos[8]);
RecordInfo ExpectedG(EmptySID, /*Name=*/"G", /*Path=*/"GlobalNamespace");
Reference NsG[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedG.Namespace = llvm::ArrayRef(NsG);
ExpectedG.TagType = TagTypeKind::Struct;
ExpectedG.DefLoc = Location(0, 0, "test.cpp");
ExpectedG.IsTypeDef = true;
CheckRecordInfo(&ExpectedG, G);
}
// Test serialization of enum declarations.
TEST_F(SerializeTest, emitEnumInfo) {
EmittedInfoList Infos;
extractInfosFromCode("enum E { X, Y }; enum class G { A, B };", 2,
/*Public=*/false, Infos, this->Diags);
NamespaceInfo *NamespaceWithEnum = InfoAsNamespace(Infos[0]);
NamespaceInfo ExpectedNamespaceWithEnum(EmptySID);
EnumInfo E;
E.Name = "E";
E.DefLoc = Location(0, 0, "test.cpp");
EnumValueInfo EMem[] = {EnumValueInfo("X", "0"), EnumValueInfo("Y", "1")};
E.Members = llvm::ArrayRef(EMem);
InfoNode<EnumInfo> ENode(&E);
ExpectedNamespaceWithEnum.Children.Enums.push_back(ENode);
CheckNamespaceInfo(&ExpectedNamespaceWithEnum, NamespaceWithEnum);
NamespaceInfo *NamespaceWithScopedEnum = InfoAsNamespace(Infos[1]);
NamespaceInfo ExpectedNamespaceWithScopedEnum(EmptySID);
EnumInfo G;
G.Name = "G";
G.Scoped = true;
G.DefLoc = Location(0, 0, "test.cpp");
EnumValueInfo GMem[] = {EnumValueInfo("A", "0"), EnumValueInfo("B", "1")};
G.Members = llvm::ArrayRef(GMem);
InfoNode<EnumInfo> GNode(&G);
ExpectedNamespaceWithScopedEnum.Children.Enums.push_back(GNode);
CheckNamespaceInfo(&ExpectedNamespaceWithScopedEnum, NamespaceWithScopedEnum);
}
TEST_F(SerializeTest, emitUndefinedRecordInfo) {
EmittedInfoList Infos;
extractInfosFromCode("class E;", 2, /*Public=*/false, Infos, this->Diags);
RecordInfo *E = InfoAsRecord(Infos[0]);
RecordInfo ExpectedE(EmptySID, /*Name=*/"E", /*Path=*/"GlobalNamespace");
Reference NsE[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedE.Namespace = llvm::ArrayRef(NsE);
ExpectedE.TagType = TagTypeKind::Class;
Location LE(0, 0, "test.cpp");
InfoNode<Location> LENode(&LE);
ExpectedE.Loc.push_back(LENode);
CheckRecordInfo(&ExpectedE, E);
}
TEST_F(SerializeTest, emitRecordMemberInfo) {
EmittedInfoList Infos;
extractInfosFromCode("struct E { int I; };", 2, /*Public=*/false, Infos,
this->Diags);
RecordInfo *E = InfoAsRecord(Infos[0]);
RecordInfo ExpectedE(EmptySID, /*Name=*/"E", /*Path=*/"GlobalNamespace");
Reference NsE[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedE.Namespace = llvm::ArrayRef(NsE);
ExpectedE.TagType = TagTypeKind::Struct;
ExpectedE.DefLoc = Location(0, 0, "test.cpp");
MemberTypeInfo MemE[] = {
MemberTypeInfo(TypeInfo("int"), "I", AccessSpecifier::AS_public)};
ExpectedE.Members = llvm::ArrayRef(MemE);
CheckRecordInfo(&ExpectedE, E);
}
TEST_F(SerializeTest, emitInternalRecordInfo) {
EmittedInfoList Infos;
extractInfosFromCode("class E { class G {}; };", 4, /*Public=*/false, Infos,
this->Diags);
RecordInfo *E = InfoAsRecord(Infos[0]);
RecordInfo ExpectedE(EmptySID, /*Name=*/"E", /*Path=*/"GlobalNamespace");
Reference NsE[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedE.Namespace = llvm::ArrayRef(NsE);
ExpectedE.DefLoc = Location(0, 0, "test.cpp");
ExpectedE.TagType = TagTypeKind::Class;
CheckRecordInfo(&ExpectedE, E);
RecordInfo *G = InfoAsRecord(Infos[2]);
llvm::SmallString<128> ExpectedGPath("GlobalNamespace/E");
llvm::sys::path::native(ExpectedGPath);
RecordInfo ExpectedG(EmptySID, /*Name=*/"G", /*Path=*/ExpectedGPath);
ExpectedG.DefLoc = Location(0, 0, "test.cpp");
ExpectedG.TagType = TagTypeKind::Class;
Reference NsG[] = {
Reference(EmptySID, "E", InfoType::IT_record),
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedG.Namespace = llvm::ArrayRef(NsG);
CheckRecordInfo(&ExpectedG, G);
}
TEST_F(SerializeTest, emitPublicAnonymousNamespaceInfo) {
EmittedInfoList Infos;
extractInfosFromCode("namespace { class A; }", 0, /*Public=*/true, Infos,
this->Diags);
}
TEST_F(SerializeTest, emitPublicFunctionInternalInfo) {
EmittedInfoList Infos;
extractInfosFromCode("int F() { class G {}; return 0; };", 1, /*Public=*/true,
Infos, this->Diags);
NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[0]);
NamespaceInfo ExpectedBWithFunction(EmptySID);
FunctionInfo F;
F.Name = "F";
F.ReturnType = TypeInfo("int");
F.DefLoc = Location(0, 0, "test.cpp");
F.Access = AccessSpecifier::AS_none;
InfoNode<FunctionInfo> FNode(&F);
ExpectedBWithFunction.Children.Functions.push_back(FNode);
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
}
TEST_F(SerializeTest, emitInlinedFunctionInfo) {
EmittedInfoList Infos;
extractInfosFromCode("inline void F(int I) { };", 1, /*Public=*/true, Infos,
this->Diags);
NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[0]);
NamespaceInfo ExpectedBWithFunction(EmptySID);
FunctionInfo F;
F.Name = "F";
F.ReturnType = TypeInfo("void");
F.DefLoc = Location(0, 0, "test.cpp");
FieldTypeInfo Params[] = {FieldTypeInfo(TypeInfo("int"), "I")};
F.Params = llvm::ArrayRef(Params);
F.Access = AccessSpecifier::AS_none;
InfoNode<FunctionInfo> FNode(&F);
ExpectedBWithFunction.Children.Functions.push_back(FNode);
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
}
TEST_F(SerializeTest, emitInheritedRecordInfo) {
EmittedInfoList Infos;
extractInfosFromCode(R"raw(class F { protected: void set(int N); };
class G { public: int get() { return 1; } protected: int I; };
class E : public F, virtual private G {};
class H : private E {};
template <typename T>
class I {} ;
class J : public I<int> {} ;)raw",
14, /*Public=*/false, Infos, this->Diags);
RecordInfo *F = InfoAsRecord(Infos[0]);
RecordInfo ExpectedF(EmptySID, /*Name=*/"F", /*Path=*/"GlobalNamespace");
Reference NsF[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedF.Namespace = llvm::ArrayRef(NsF);
ExpectedF.TagType = TagTypeKind::Class;
ExpectedF.DefLoc = Location(0, 0, "test.cpp");
CheckRecordInfo(&ExpectedF, F);
RecordInfo *G = InfoAsRecord(Infos[3]);
RecordInfo ExpectedG(EmptySID, /*Name=*/"G", /*Path=*/"GlobalNamespace");
Reference NsG[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedG.Namespace = llvm::ArrayRef(NsG);
ExpectedG.TagType = TagTypeKind::Class;
ExpectedG.DefLoc = Location(0, 0, "test.cpp");
MemberTypeInfo MemG[] = {
MemberTypeInfo(TypeInfo("int"), "I", AccessSpecifier::AS_protected)};
ExpectedG.Members = llvm::ArrayRef(MemG);
CheckRecordInfo(&ExpectedG, G);
RecordInfo *E = InfoAsRecord(Infos[6]);
RecordInfo ExpectedE(EmptySID, /*Name=*/"E", /*Path=*/"GlobalNamespace");
Reference NsE[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedE.Namespace = llvm::ArrayRef(NsE);
Reference ParE[] = {Reference(EmptySID, /*Name=*/"F", InfoType::IT_record,
/*QualName=*/"", /*Path=*/"GlobalNamespace")};
ExpectedE.Parents = llvm::ArrayRef(ParE);
Reference VParE[] = {Reference(EmptySID, /*Name=*/"G", InfoType::IT_record,
/*QualName=*/"G",
/*Path=*/"GlobalNamespace")};
ExpectedE.VirtualParents = llvm::ArrayRef(VParE);
BaseRecordInfo BaseF(EmptySID, /*Name=*/"F",
/*Path=*/"GlobalNamespace", false,
AccessSpecifier::AS_public, true);
FunctionInfo FunctionSet;
FunctionSet.Name = "set";
FunctionSet.ReturnType = TypeInfo("void");
Location LSet;
InfoNode<Location> LSetNode(&LSet);
FunctionSet.Loc.push_back(LSetNode);
FieldTypeInfo ParamsSet[] = {FieldTypeInfo(TypeInfo("int"), "N")};
FunctionSet.Params = llvm::ArrayRef(ParamsSet);
Reference NsSet[] = {
Reference(EmptySID, "F", InfoType::IT_record),
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
FunctionSet.Namespace = llvm::ArrayRef(NsSet);
FunctionSet.Access =
AccessSpecifier::AS_none; // Wait, previous had AS_protected, but wait,
// F.Access was AS_protected. FunctionSet.Access
// should be AS_protected if it was so. In the
// original it was AS_protected.
FunctionSet.Access = AccessSpecifier::AS_protected;
FunctionSet.IsMethod = true;
InfoNode<FunctionInfo> FunctionSetNode(&FunctionSet);
BaseF.Children.Functions.push_back(FunctionSetNode);
BaseRecordInfo BaseG(EmptySID, /*Name=*/"G",
/*Path=*/"GlobalNamespace", true,
AccessSpecifier::AS_private, true);
FunctionInfo FunctionGet;
FunctionGet.Name = "get";
FunctionGet.ReturnType = TypeInfo("int");
Location LGet;
FunctionGet.DefLoc = LGet;
Reference NsGet[] = {
Reference(EmptySID, "G", InfoType::IT_record),
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
FunctionGet.Namespace = llvm::ArrayRef(NsGet);
FunctionGet.Access = AccessSpecifier::AS_private;
FunctionGet.IsMethod = true;
InfoNode<FunctionInfo> FunctionGetNode(&FunctionGet);
BaseG.Children.Functions.push_back(FunctionGetNode);
MemberTypeInfo MemG2[] = {
MemberTypeInfo(TypeInfo("int"), "I", AccessSpecifier::AS_private)};
BaseG.Members = llvm::ArrayRef(MemG2);
BaseRecordInfo BasesE[] = {std::move(BaseF), std::move(BaseG)};
ExpectedE.Bases = llvm::ArrayRef(BasesE);
ExpectedE.DefLoc = Location(0, 0, "test.cpp");
ExpectedE.TagType = TagTypeKind::Class;
CheckRecordInfo(&ExpectedE, E);
RecordInfo *H = InfoAsRecord(Infos[8]);
RecordInfo ExpectedH(EmptySID, /*Name=*/"H", /*Path=*/"GlobalNamespace");
Reference NsH[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedH.Namespace = llvm::ArrayRef(NsH);
ExpectedH.TagType = TagTypeKind::Class;
ExpectedH.DefLoc = Location(0, 0, "test.cpp");
Reference ParH[] = {Reference(EmptySID, /*Name=*/"E", InfoType::IT_record,
/*QualName=*/"E", /*Path=*/"GlobalNamespace")};
ExpectedH.Parents = llvm::ArrayRef(ParH);
Reference VParH[] = {Reference(EmptySID, /*Name=*/"G", InfoType::IT_record,
/*QualName=*/"G",
/*Path=*/"GlobalNamespace")};
ExpectedH.VirtualParents = llvm::ArrayRef(VParH);
BaseRecordInfo BaseHE(EmptySID, /*Name=*/"E",
/*Path=*/"GlobalNamespace", false,
AccessSpecifier::AS_private, true);
BaseRecordInfo BaseHF(EmptySID, /*Name=*/"F",
/*Path=*/"GlobalNamespace", false,
AccessSpecifier::AS_private, false);
FunctionInfo FunctionSetNew;
FunctionSetNew.Name = "set";
FunctionSetNew.ReturnType = TypeInfo("void");
Location LSetNew;
InfoNode<Location> LSetNewNode(&LSetNew);
FunctionSetNew.Loc.push_back(LSetNewNode);
FieldTypeInfo ParamsSetNew[] = {FieldTypeInfo(TypeInfo("int"), "N")};
FunctionSetNew.Params = llvm::ArrayRef(ParamsSetNew);
Reference NsSetNew[] = {
Reference(EmptySID, "F", InfoType::IT_record),
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
FunctionSetNew.Namespace = llvm::ArrayRef(NsSetNew);
FunctionSetNew.Access = AccessSpecifier::AS_private;
FunctionSetNew.IsMethod = true;
InfoNode<FunctionInfo> FunctionSetNewNode(&FunctionSetNew);
BaseHF.Children.Functions.push_back(FunctionSetNewNode);
BaseRecordInfo BaseHG(EmptySID, /*Name=*/"G",
/*Path=*/"GlobalNamespace", true,
AccessSpecifier::AS_private, false);
FunctionInfo FunctionGetNew;
FunctionGetNew.Name = "get";
FunctionGetNew.ReturnType = TypeInfo("int");
Location LGetNew;
FunctionGetNew.DefLoc = LGetNew;
Reference NsGetNew[] = {
Reference(EmptySID, "G", InfoType::IT_record),
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
FunctionGetNew.Namespace = llvm::ArrayRef(NsGetNew);
FunctionGetNew.Access = AccessSpecifier::AS_private;
FunctionGetNew.IsMethod = true;
InfoNode<FunctionInfo> FunctionGetNewNode(&FunctionGetNew);
BaseHG.Children.Functions.push_back(FunctionGetNewNode);
MemberTypeInfo MemHG[] = {
MemberTypeInfo(TypeInfo("int"), "I", AccessSpecifier::AS_private)};
BaseHG.Members = llvm::ArrayRef(MemHG);
BaseRecordInfo BasesH[] = {std::move(BaseHE), std::move(BaseHF),
std::move(BaseHG)};
ExpectedH.Bases = llvm::ArrayRef(BasesH);
CheckRecordInfo(&ExpectedH, H);
RecordInfo *I = InfoAsRecord(Infos[10]);
RecordInfo ExpectedI(EmptySID, /*Name=*/"I", /*Path=*/"GlobalNamespace");
Reference NsI[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedI.Namespace = llvm::ArrayRef(NsI);
ExpectedI.TagType = TagTypeKind::Class;
ExpectedI.DefLoc = Location(0, 0, "test.cpp");
CheckRecordInfo(&ExpectedI, I);
RecordInfo *J = InfoAsRecord(Infos[12]);
RecordInfo ExpectedJ(EmptySID, /*Name=*/"J", /*Path=*/"GlobalNamespace");
Reference NsJ[] = {
Reference(EmptySID, "GlobalNamespace", InfoType::IT_namespace)};
ExpectedJ.Namespace = llvm::ArrayRef(NsJ);
Reference ParJ[] = {
Reference(EmptySID, /*Name=*/"I<int>", InfoType::IT_record)};
ExpectedJ.Parents = llvm::ArrayRef(ParJ);
BaseRecordInfo BasesJ[] = {BaseRecordInfo(EmptySID, /*Name=*/"I<int>",
/*Path=*/"GlobalNamespace", false,
AccessSpecifier::AS_public, true)};
ExpectedJ.Bases = llvm::ArrayRef(BasesJ);
ExpectedJ.DefLoc = Location(0, 0, "test.cpp");
ExpectedJ.TagType = TagTypeKind::Class;
CheckRecordInfo(&ExpectedJ, J);
}
TEST_F(SerializeTest, emitModulePublicLFunctions) {
EmittedInfoList Infos;
std::vector<std::string> Args;
Args.push_back("-fmodules-ts");
extractInfosFromCodeWithArgs(R"raw(export module M;
int moduleFunction(int x, double d = 3.2 - 1.0);
static int staticModuleFunction(int x);
export double exportedModuleFunction(double y);)raw",
2, /*Public=*/true, Infos, Args, this->Diags);
NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[0]);
NamespaceInfo ExpectedBWithFunction(EmptySID);
FunctionInfo F;
F.Name = "moduleFunction";
F.ReturnType = TypeInfo("int");
Location LF1(0, 0, "test.cpp");
InfoNode<Location> LF1Node(&LF1);
F.Loc.push_back(LF1Node);
FieldTypeInfo ParamsF[] = {FieldTypeInfo(TypeInfo("int"), "x"),
FieldTypeInfo(TypeInfo("double"), "d")};
ParamsF[1].DefaultValue = "3.2 - 1.0";
F.Params = llvm::ArrayRef(ParamsF);
F.Access = AccessSpecifier::AS_none;
InfoNode<FunctionInfo> FNode(&F);
ExpectedBWithFunction.Children.Functions.push_back(FNode);
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
NamespaceInfo *BWithExportedFunction = InfoAsNamespace(Infos[1]);
NamespaceInfo ExpectedBWithExportedFunction(EmptySID);
FunctionInfo ExportedF;
ExportedF.Name = "exportedModuleFunction";
ExportedF.ReturnType =
TypeInfo(Reference(EmptySID, "double", InfoType::IT_default));
Location LF2(0, 0, "test.cpp");
InfoNode<Location> LF2Node(&LF2);
ExportedF.Loc.push_back(LF2Node);
FieldTypeInfo ParamsExportedF[] = {FieldTypeInfo(TypeInfo("double"), "y")};
ExportedF.Params = llvm::ArrayRef(ParamsExportedF);
ExportedF.Access = AccessSpecifier::AS_none;
InfoNode<FunctionInfo> ExportedFNode(&ExportedF);
ExpectedBWithExportedFunction.Children.Functions.push_back(ExportedFNode);
CheckNamespaceInfo(&ExpectedBWithExportedFunction, BWithExportedFunction);
}
// Test serialization of child records in namespaces and other records
TEST_F(SerializeTest, emitChildRecords) {
EmittedInfoList Infos;
extractInfosFromCode("class A { class B {}; }; namespace { class C {}; } ", 8,
/*Public=*/false, Infos, this->Diags);
NamespaceInfo *ParentA = InfoAsNamespace(Infos[1]);
NamespaceInfo ExpectedParentA(EmptySID);
Reference RA(EmptySID, "A", InfoType::IT_record, "A", "GlobalNamespace");
InfoNode<Reference> RANode(&RA);
ExpectedParentA.Children.Records.push_back(RANode);
CheckNamespaceInfo(&ExpectedParentA, ParentA);
RecordInfo *ParentB = InfoAsRecord(Infos[3]);
RecordInfo ExpectedParentB(EmptySID);
llvm::SmallString<128> ExpectedParentBPath("GlobalNamespace/A");
llvm::sys::path::native(ExpectedParentBPath);
Reference RB(EmptySID, "B", InfoType::IT_record, "A::B", ExpectedParentBPath);
InfoNode<Reference> RBNode(&RB);
ExpectedParentB.Children.Records.push_back(RBNode);
CheckRecordInfo(&ExpectedParentB, ParentB);
NamespaceInfo *ParentC = InfoAsNamespace(Infos[7]);
NamespaceInfo ExpectedParentC(EmptySID);
Reference RC(EmptySID, "C", InfoType::IT_record, "C", "@nonymous_namespace");
InfoNode<Reference> RCNode(&RC);
ExpectedParentC.Children.Records.push_back(RCNode);
CheckNamespaceInfo(&ExpectedParentC, ParentC);
}
// Test serialization of child namespaces
TEST_F(SerializeTest, emitChildNamespaces) {
EmittedInfoList Infos;
extractInfosFromCode("namespace A { namespace B { } }", 4, /*Public=*/false,
Infos, this->Diags);
NamespaceInfo *ParentA = InfoAsNamespace(Infos[1]);
NamespaceInfo ExpectedParentA(EmptySID);
Reference RA(EmptySID, "A", InfoType::IT_namespace);
InfoNode<Reference> RANode(&RA);
ExpectedParentA.Children.Namespaces.push_back(RANode);
CheckNamespaceInfo(&ExpectedParentA, ParentA);
NamespaceInfo *ParentB = InfoAsNamespace(Infos[3]);
NamespaceInfo ExpectedParentB(EmptySID);
Reference RB(EmptySID, "B", InfoType::IT_namespace, "A::B", "A");
InfoNode<Reference> RBNode(&RB);
ExpectedParentB.Children.Namespaces.push_back(RBNode);
CheckNamespaceInfo(&ExpectedParentB, ParentB);
}
TEST_F(SerializeTest, emitTypedefs) {
EmittedInfoList Infos;
extractInfosFromCode("typedef int MyInt; using MyDouble = double;", 2,
/*Public=*/false, Infos, this->Diags);
// First info will be the global namespace with the typedef in it.
NamespaceInfo *GlobalNS1 = InfoAsNamespace(Infos[0]);
ASSERT_EQ(1u, GlobalNS1->Children.Typedefs.size());
const TypedefInfo &FirstTD = *GlobalNS1->Children.Typedefs.begin();
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]);
ASSERT_EQ(1u, GlobalNS2->Children.Typedefs.size());
// Second is the "using" typedef.
const TypedefInfo &SecondTD = *GlobalNS2->Children.Typedefs.begin();
EXPECT_EQ("MyDouble", SecondTD.Name);
EXPECT_TRUE(SecondTD.IsUsing);
EXPECT_EQ("double", SecondTD.Underlying.Type.Name);
}
TEST_F(SerializeTest, emitFunctionTemplate) {
EmittedInfoList Infos;
// A template and a specialization.
extractInfosFromCode("template<typename T = int> bool GetFoo(T);\n"
"template<> bool GetFoo<bool>(bool);",
2,
/*Public=*/false, Infos, this->Diags);
// First info will be the global namespace.
NamespaceInfo *GlobalNS1 = InfoAsNamespace(Infos[0]);
ASSERT_EQ(1u, GlobalNS1->Children.Functions.size());
const FunctionInfo &Func1 = *GlobalNS1->Children.Functions.begin();
EXPECT_EQ("GetFoo", Func1.Name);
ASSERT_TRUE(Func1.Template);
EXPECT_FALSE(Func1.Template->Specialization); // Not a specialization.
// Template parameter.
ASSERT_EQ(1u, Func1.Template->Params.size());
EXPECT_EQ("typename T = int", Func1.Template->Params[0].Contents);
// The second will be another global namespace with the function 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]);
ASSERT_EQ(1u, GlobalNS2->Children.Functions.size());
// This one is a template specialization.
const FunctionInfo &Func2 = *GlobalNS2->Children.Functions.begin();
EXPECT_EQ("GetFoo", Func2.Name);
ASSERT_TRUE(Func2.Template);
EXPECT_TRUE(Func2.Template->Params.empty()); // No template params.
ASSERT_TRUE(Func2.Template->Specialization);
// Specialization values.
ASSERT_EQ(1u, Func2.Template->Specialization->Params.size());
EXPECT_EQ("bool", Func2.Template->Specialization->Params[0].Contents);
EXPECT_EQ(Func1.USR, Func2.Template->Specialization->SpecializationOf);
EXPECT_EQ("bool", Func2.ReturnType.Type.Name);
}
TEST_F(SerializeTest, emitClassTemplate) {
EmittedInfoList Infos;
// This will generate 2x the number of infos: each Record will be followed by
// a copy of the global namespace containing it (this test checks the data
// pre-merge).
extractInfosFromCode(
"template<int I> class MyTemplate { int i[I]; };\n"
"template<> class MyTemplate<0> {};\n"
"template<typename T, int U = 1> class OtherTemplate {};\n"
"template<int U> class OtherTemplate<MyTemplate<0>, U> {};",
8,
/*Public=*/false, Infos, this->Diags);
// First record.
const RecordInfo *Rec1 = InfoAsRecord(Infos[0]);
EXPECT_EQ("MyTemplate", Rec1->Name);
ASSERT_TRUE(Rec1->Template);
EXPECT_FALSE(Rec1->Template->Specialization); // Not a specialization.
// First record template parameter.
ASSERT_EQ(1u, Rec1->Template->Params.size());
EXPECT_EQ("int I", Rec1->Template->Params[0].Contents);
// Second record.
const RecordInfo *Rec2 = InfoAsRecord(Infos[2]);
EXPECT_EQ("MyTemplate", Rec2->Name);
ASSERT_TRUE(Rec2->Template);
EXPECT_TRUE(Rec2->Template->Params.empty()); // No template params.
ASSERT_TRUE(Rec2->Template->Specialization);
// Second record specialization values.
ASSERT_EQ(1u, Rec2->Template->Specialization->Params.size());
EXPECT_EQ("0", Rec2->Template->Specialization->Params[0].Contents);
EXPECT_EQ(Rec1->USR, Rec2->Template->Specialization->SpecializationOf);
// Third record.
const RecordInfo *Rec3 = InfoAsRecord(Infos[4]);
EXPECT_EQ("OtherTemplate", Rec3->Name);
ASSERT_TRUE(Rec3->Template);
// Third record template parameters.
ASSERT_EQ(2u, Rec3->Template->Params.size());
EXPECT_EQ("typename T", Rec3->Template->Params[0].Contents);
EXPECT_EQ("int U = 1", Rec3->Template->Params[1].Contents);
// Fourth record.
const RecordInfo *Rec4 = InfoAsRecord(Infos[6]);
EXPECT_EQ("OtherTemplate", Rec3->Name);
ASSERT_TRUE(Rec4->Template);
ASSERT_TRUE(Rec4->Template->Specialization);
// Fourth record template + specialization parameters.
ASSERT_EQ(1u, Rec4->Template->Params.size());
EXPECT_EQ("int U", Rec4->Template->Params[0].Contents);
ASSERT_EQ(2u, Rec4->Template->Specialization->Params.size());
EXPECT_EQ("MyTemplate<0>",
Rec4->Template->Specialization->Params[0].Contents);
EXPECT_EQ("U", Rec4->Template->Specialization->Params[1].Contents);
}
} // namespace doc
} // end namespace clang