forked from OSchip/llvm-project
[clang-doc] Serialize inherited attributes and methods
clang-doc now serializes the inherited attributes and methods, not only the name of the base class. All inherited are tracked, if B:A and C:B, info of A is included in C. This data is stored in attribute Bases in a RecordInfo. Previously tracked inheritance data, stored in Parents and VParents, hasn't been removed to reduce review load. Differential revision: https://reviews.llvm.org/D66238 llvm-svn: 369075
This commit is contained in:
parent
76053297bd
commit
ba3d595f93
|
@ -179,6 +179,29 @@ llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
|
|||
}
|
||||
}
|
||||
|
||||
llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
|
||||
BaseRecordInfo *I) {
|
||||
switch (ID) {
|
||||
case BASE_RECORD_USR:
|
||||
return decodeRecord(R, I->USR, Blob);
|
||||
case BASE_RECORD_NAME:
|
||||
return decodeRecord(R, I->Name, Blob);
|
||||
case BASE_RECORD_PATH:
|
||||
return decodeRecord(R, I->Path, Blob);
|
||||
case BASE_RECORD_TAG_TYPE:
|
||||
return decodeRecord(R, I->TagType, Blob);
|
||||
case BASE_RECORD_IS_VIRTUAL:
|
||||
return decodeRecord(R, I->IsVirtual, Blob);
|
||||
case BASE_RECORD_ACCESS:
|
||||
return decodeRecord(R, I->Access, Blob);
|
||||
case BASE_RECORD_IS_PARENT:
|
||||
return decodeRecord(R, I->IsParent, Blob);
|
||||
default:
|
||||
return llvm::make_error<llvm::StringError>(
|
||||
"Invalid field for BaseRecordInfo.\n", llvm::inconvertibleErrorCode());
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob,
|
||||
EnumInfo *I) {
|
||||
switch (ID) {
|
||||
|
@ -350,6 +373,11 @@ template <> llvm::Error addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) {
|
|||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
template <> llvm::Error addTypeInfo(BaseRecordInfo *I, MemberTypeInfo &&T) {
|
||||
I->Members.emplace_back(std::move(T));
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
template <> llvm::Error addTypeInfo(FunctionInfo *I, TypeInfo &&T) {
|
||||
I->ReturnType = std::move(T);
|
||||
return llvm::Error::success();
|
||||
|
@ -494,6 +522,14 @@ template <> void addChild(RecordInfo *I, EnumInfo &&R) {
|
|||
I->ChildEnums.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));
|
||||
}
|
||||
|
||||
// Read records from bitcode into a given info.
|
||||
template <typename T>
|
||||
llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, T I) {
|
||||
|
@ -598,6 +634,13 @@ llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
|
|||
addChild(I, std::move(F));
|
||||
return llvm::Error::success();
|
||||
}
|
||||
case BI_BASE_RECORD_BLOCK_ID: {
|
||||
BaseRecordInfo BR;
|
||||
if (auto Err = readBlock(ID, &BR))
|
||||
return Err;
|
||||
addChild(I, std::move(BR));
|
||||
return llvm::Error::success();
|
||||
}
|
||||
case BI_ENUM_BLOCK_ID: {
|
||||
EnumInfo E;
|
||||
if (auto Err = readBlock(ID, &E))
|
||||
|
|
|
@ -116,6 +116,7 @@ static const llvm::IndexedMap<llvm::StringRef, BlockIdToIndexFunctor>
|
|||
{BI_FIELD_TYPE_BLOCK_ID, "FieldTypeBlock"},
|
||||
{BI_MEMBER_TYPE_BLOCK_ID, "MemberTypeBlock"},
|
||||
{BI_RECORD_BLOCK_ID, "RecordBlock"},
|
||||
{BI_BASE_RECORD_BLOCK_ID, "BaseRecordBlock"},
|
||||
{BI_FUNCTION_BLOCK_ID, "FunctionBlock"},
|
||||
{BI_COMMENT_BLOCK_ID, "CommentBlock"},
|
||||
{BI_REFERENCE_BLOCK_ID, "ReferenceBlock"}};
|
||||
|
@ -165,6 +166,13 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
|
|||
{RECORD_LOCATION, {"Location", &LocationAbbrev}},
|
||||
{RECORD_TAG_TYPE, {"TagType", &IntAbbrev}},
|
||||
{RECORD_IS_TYPE_DEF, {"IsTypeDef", &BoolAbbrev}},
|
||||
{BASE_RECORD_USR, {"USR", &SymbolIDAbbrev}},
|
||||
{BASE_RECORD_NAME, {"Name", &StringAbbrev}},
|
||||
{BASE_RECORD_PATH, {"Path", &StringAbbrev}},
|
||||
{BASE_RECORD_TAG_TYPE, {"TagType", &IntAbbrev}},
|
||||
{BASE_RECORD_IS_VIRTUAL, {"IsVirtual", &BoolAbbrev}},
|
||||
{BASE_RECORD_ACCESS, {"Access", &IntAbbrev}},
|
||||
{BASE_RECORD_IS_PARENT, {"IsParent", &BoolAbbrev}},
|
||||
{FUNCTION_USR, {"USR", &SymbolIDAbbrev}},
|
||||
{FUNCTION_NAME, {"Name", &StringAbbrev}},
|
||||
{FUNCTION_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
|
||||
|
@ -213,6 +221,11 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
|
|||
{BI_RECORD_BLOCK_ID,
|
||||
{RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION,
|
||||
RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF}},
|
||||
// BaseRecord Block
|
||||
{BI_BASE_RECORD_BLOCK_ID,
|
||||
{BASE_RECORD_USR, BASE_RECORD_NAME, BASE_RECORD_PATH,
|
||||
BASE_RECORD_TAG_TYPE, BASE_RECORD_IS_VIRTUAL, BASE_RECORD_ACCESS,
|
||||
BASE_RECORD_IS_PARENT}},
|
||||
// Function Block
|
||||
{BI_FUNCTION_BLOCK_ID,
|
||||
{FUNCTION_USR, FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION,
|
||||
|
@ -494,6 +507,8 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
|
|||
emitBlock(P, FieldId::F_parent);
|
||||
for (const auto &P : I.VirtualParents)
|
||||
emitBlock(P, FieldId::F_vparent);
|
||||
for (const auto &PB : I.Bases)
|
||||
emitBlock(PB);
|
||||
for (const auto &C : I.ChildRecords)
|
||||
emitBlock(C, FieldId::F_child_record);
|
||||
for (const auto &C : I.ChildFunctions)
|
||||
|
@ -502,6 +517,21 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
|
|||
emitBlock(C);
|
||||
}
|
||||
|
||||
void ClangDocBitcodeWriter::emitBlock(const BaseRecordInfo &I) {
|
||||
StreamSubBlockGuard Block(Stream, BI_BASE_RECORD_BLOCK_ID);
|
||||
emitRecord(I.USR, BASE_RECORD_USR);
|
||||
emitRecord(I.Name, BASE_RECORD_NAME);
|
||||
emitRecord(I.Path, BASE_RECORD_PATH);
|
||||
emitRecord(I.TagType, BASE_RECORD_TAG_TYPE);
|
||||
emitRecord(I.IsVirtual, BASE_RECORD_IS_VIRTUAL);
|
||||
emitRecord(I.Access, BASE_RECORD_ACCESS);
|
||||
emitRecord(I.IsParent, BASE_RECORD_IS_PARENT);
|
||||
for (const auto &M : I.Members)
|
||||
emitBlock(M);
|
||||
for (const auto &C : I.ChildFunctions)
|
||||
emitBlock(C);
|
||||
}
|
||||
|
||||
void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) {
|
||||
StreamSubBlockGuard Block(Stream, BI_FUNCTION_BLOCK_ID);
|
||||
emitRecord(I.USR, FUNCTION_USR);
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace doc {
|
|||
// Current version number of clang-doc bitcode.
|
||||
// Should be bumped when removing or changing BlockIds, RecordIds, or
|
||||
// BitCodeConstants, though they can be added without breaking it.
|
||||
static const unsigned VersionNumber = 2;
|
||||
static const unsigned VersionNumber = 3;
|
||||
|
||||
struct BitCodeConstants {
|
||||
static constexpr unsigned RecordSize = 32U;
|
||||
|
@ -58,6 +58,7 @@ enum BlockId {
|
|||
BI_FIELD_TYPE_BLOCK_ID,
|
||||
BI_MEMBER_TYPE_BLOCK_ID,
|
||||
BI_RECORD_BLOCK_ID,
|
||||
BI_BASE_RECORD_BLOCK_ID,
|
||||
BI_FUNCTION_BLOCK_ID,
|
||||
BI_COMMENT_BLOCK_ID,
|
||||
BI_REFERENCE_BLOCK_ID,
|
||||
|
@ -105,6 +106,13 @@ enum RecordId {
|
|||
RECORD_LOCATION,
|
||||
RECORD_TAG_TYPE,
|
||||
RECORD_IS_TYPE_DEF,
|
||||
BASE_RECORD_USR,
|
||||
BASE_RECORD_NAME,
|
||||
BASE_RECORD_PATH,
|
||||
BASE_RECORD_TAG_TYPE,
|
||||
BASE_RECORD_IS_VIRTUAL,
|
||||
BASE_RECORD_ACCESS,
|
||||
BASE_RECORD_IS_PARENT,
|
||||
REFERENCE_USR,
|
||||
REFERENCE_NAME,
|
||||
REFERENCE_TYPE,
|
||||
|
@ -143,6 +151,7 @@ public:
|
|||
// Block emission of different info types.
|
||||
void emitBlock(const NamespaceInfo &I);
|
||||
void emitBlock(const RecordInfo &I);
|
||||
void emitBlock(const BaseRecordInfo &I);
|
||||
void emitBlock(const FunctionInfo &I);
|
||||
void emitBlock(const EnumInfo &I);
|
||||
void emitBlock(const TypeInfo &B);
|
||||
|
|
|
@ -178,6 +178,8 @@ void RecordInfo::merge(RecordInfo &&Other) {
|
|||
TagType = Other.TagType;
|
||||
if (Members.empty())
|
||||
Members = std::move(Other.Members);
|
||||
if (Bases.empty())
|
||||
Bases = std::move(Other.Bases);
|
||||
if (Parents.empty())
|
||||
Parents = std::move(Other.Parents);
|
||||
if (VirtualParents.empty())
|
||||
|
|
|
@ -32,6 +32,7 @@ using SymbolID = std::array<uint8_t, 20>;
|
|||
struct Info;
|
||||
struct FunctionInfo;
|
||||
struct EnumInfo;
|
||||
struct BaseRecordInfo;
|
||||
|
||||
enum class InfoType {
|
||||
IT_default,
|
||||
|
@ -345,15 +346,33 @@ struct RecordInfo : public SymbolInfo {
|
|||
llvm::SmallVector<Reference, 4>
|
||||
VirtualParents; // List of virtual base/parent records.
|
||||
|
||||
// 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<BaseRecordInfo>
|
||||
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<Reference> ChildRecords;
|
||||
std::vector<FunctionInfo> ChildFunctions;
|
||||
std::vector<EnumInfo> ChildEnums;
|
||||
};
|
||||
|
||||
struct BaseRecordInfo : public RecordInfo {
|
||||
BaseRecordInfo() : RecordInfo() {}
|
||||
BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path, bool IsVirtual,
|
||||
AccessSpecifier Access, bool IsParent)
|
||||
: RecordInfo(USR, Name, Path), IsVirtual(IsVirtual), Access(Access),
|
||||
IsParent(IsParent) {}
|
||||
|
||||
// Indicates if base corresponds to a virtual inheritance
|
||||
bool IsVirtual = false;
|
||||
// Access level associated with this inherited info (public, protected,
|
||||
// private).
|
||||
AccessSpecifier Access = AccessSpecifier::AS_public;
|
||||
bool IsParent = false; // Indicates if this base is a direct parent
|
||||
};
|
||||
|
||||
// TODO: Expand to allow for documenting templating.
|
||||
// Info for types.
|
||||
struct EnumInfo : public SymbolInfo {
|
||||
|
|
|
@ -230,27 +230,72 @@ static bool isPublic(const clang::AccessSpecifier AS,
|
|||
return false; // otherwise, linkage is some form of internal linkage
|
||||
}
|
||||
|
||||
static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly) {
|
||||
static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
|
||||
const NamedDecl *D) {
|
||||
bool IsAnonymousNamespace = false;
|
||||
if (const auto *N = dyn_cast<NamespaceDecl>(D))
|
||||
IsAnonymousNamespace = N->isAnonymousNamespace();
|
||||
return !PublicOnly ||
|
||||
(!IsInAnonymousNamespace && !IsAnonymousNamespace &&
|
||||
isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
|
||||
}
|
||||
|
||||
// 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 {};
|
||||
// It's explicit that C is publicly inherited from C and B is privately
|
||||
// inherited from A. It's not explicit but C is also privately inherited from
|
||||
// A. This is the AS that this function calculates. FirstAS is the
|
||||
// inheritance mode of `class C : B` and SecondAS is the inheritance mode of
|
||||
// `class B : A`.
|
||||
// 2) Getting the inheritance mode of an inherited attribute / method.
|
||||
// Example : class A { public: int M; }; class B : private A {};
|
||||
// Class B is inherited from class A, which has a public attribute. This
|
||||
// attribute is now part of the derived class B but it's not public. This
|
||||
// will be private because the inheritance is private. This is the AS that
|
||||
// this function calculates. FirstAS is the inheritance mode and SecondAS is
|
||||
// the AS of the attribute / method.
|
||||
static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
|
||||
AccessSpecifier SecondAS) {
|
||||
if (FirstAS == AccessSpecifier::AS_none ||
|
||||
SecondAS == AccessSpecifier::AS_none)
|
||||
return AccessSpecifier::AS_none;
|
||||
if (FirstAS == AccessSpecifier::AS_private ||
|
||||
SecondAS == AccessSpecifier::AS_private)
|
||||
return AccessSpecifier::AS_private;
|
||||
if (FirstAS == AccessSpecifier::AS_protected ||
|
||||
SecondAS == AccessSpecifier::AS_protected)
|
||||
return AccessSpecifier::AS_protected;
|
||||
return AccessSpecifier::AS_public;
|
||||
}
|
||||
|
||||
// The Access parameter is only provided when parsing the field of an inherited
|
||||
// record, the access specification of the field depends on the inheritance mode
|
||||
static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
|
||||
AccessSpecifier Access = AccessSpecifier::AS_public) {
|
||||
for (const FieldDecl *F : D->fields()) {
|
||||
if (PublicOnly && !isPublic(F->getAccessUnsafe(), F->getLinkageInternal()))
|
||||
if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
|
||||
continue;
|
||||
if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) {
|
||||
// Use getAccessUnsafe so that we just get the default AS_none if it's not
|
||||
// valid, as opposed to an assert.
|
||||
if (const auto *N = dyn_cast<EnumDecl>(T)) {
|
||||
I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
|
||||
InfoType::IT_enum, getInfoRelativePath(N),
|
||||
F->getNameAsString(), N->getAccessUnsafe());
|
||||
I.Members.emplace_back(
|
||||
getUSRForDecl(T), N->getNameAsString(), InfoType::IT_enum,
|
||||
getInfoRelativePath(N), F->getNameAsString(),
|
||||
getFinalAccessSpecifier(Access, N->getAccessUnsafe()));
|
||||
continue;
|
||||
} else if (const auto *N = dyn_cast<RecordDecl>(T)) {
|
||||
I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
|
||||
InfoType::IT_record, getInfoRelativePath(N),
|
||||
F->getNameAsString(), N->getAccessUnsafe());
|
||||
I.Members.emplace_back(
|
||||
getUSRForDecl(T), N->getNameAsString(), InfoType::IT_record,
|
||||
getInfoRelativePath(N), F->getNameAsString(),
|
||||
getFinalAccessSpecifier(Access, N->getAccessUnsafe()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
I.Members.emplace_back(F->getTypeSourceInfo()->getType().getAsString(),
|
||||
F->getNameAsString(), F->getAccessUnsafe());
|
||||
I.Members.emplace_back(
|
||||
F->getTypeSourceInfo()->getType().getAsString(), F->getNameAsString(),
|
||||
getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,6 +324,8 @@ static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Remove the serialization of Parents and VirtualParents, this
|
||||
// information is also extracted in the other definition of parseBases.
|
||||
static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
|
||||
// Don't parse bases if this isn't a definition.
|
||||
if (!D->isThisDeclarationADefinition())
|
||||
|
@ -376,15 +423,71 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
|
|||
parseParameters(I, D);
|
||||
}
|
||||
|
||||
static void
|
||||
parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
|
||||
bool PublicOnly, bool IsParent,
|
||||
AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
|
||||
// Don't parse bases if this isn't a definition.
|
||||
if (!D->isThisDeclarationADefinition())
|
||||
return;
|
||||
for (const CXXBaseSpecifier &B : D->bases()) {
|
||||
if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
|
||||
if (const CXXRecordDecl *Base =
|
||||
cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
|
||||
// Initialized without USR and name, this will be set in the following
|
||||
// if-else stmt.
|
||||
BaseRecordInfo BI(
|
||||
{}, "", getInfoRelativePath(Base), B.isVirtual(),
|
||||
getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
|
||||
IsParent);
|
||||
if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
|
||||
const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
|
||||
BI.USR = getUSRForDecl(D);
|
||||
BI.Name = B.getType().getAsString();
|
||||
} else {
|
||||
BI.USR = getUSRForDecl(Base);
|
||||
BI.Name = Base->getNameAsString();
|
||||
}
|
||||
parseFields(BI, Base, PublicOnly, BI.Access);
|
||||
for (const auto &Decl : Base->decls())
|
||||
if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
|
||||
// Don't serialize private methods
|
||||
if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
|
||||
!MD->isUserProvided())
|
||||
continue;
|
||||
FunctionInfo FI;
|
||||
FI.IsMethod = true;
|
||||
// The seventh arg in populateFunctionInfo is a boolean passed by
|
||||
// reference, its value is not relevant in here so it's not used
|
||||
// anywhere besides the function call.
|
||||
bool IsInAnonymousNamespace;
|
||||
populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
|
||||
/*FileName=*/{}, IsFileInRootDir,
|
||||
IsInAnonymousNamespace);
|
||||
FI.Access =
|
||||
getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
|
||||
BI.ChildFunctions.emplace_back(std::move(FI));
|
||||
}
|
||||
I.Bases.emplace_back(std::move(BI));
|
||||
// Call this function recursively to get the inherited classes of
|
||||
// this base; these new bases will also get stored in the original
|
||||
// RecordInfo: I.
|
||||
parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
|
||||
I.Bases.back().Access);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
||||
emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
|
||||
llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
||||
auto I = std::make_unique<NamespaceInfo>();
|
||||
bool IsInAnonymousNamespace = false;
|
||||
populateInfo(*I, D, FC, IsInAnonymousNamespace);
|
||||
if (PublicOnly && ((IsInAnonymousNamespace || D->isAnonymousNamespace()) ||
|
||||
!isPublic(D->getAccess(), D->getLinkageInternal())))
|
||||
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
||||
return {};
|
||||
|
||||
I->Name = D->isAnonymousNamespace()
|
||||
? llvm::SmallString<16>("@nonymous_namespace")
|
||||
: I->Name;
|
||||
|
@ -409,8 +512,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
|
|||
bool IsInAnonymousNamespace = false;
|
||||
populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
|
||||
IsInAnonymousNamespace);
|
||||
if (PublicOnly && ((IsInAnonymousNamespace ||
|
||||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
|
||||
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
||||
return {};
|
||||
|
||||
I->TagType = D->getTagKind();
|
||||
|
@ -420,7 +522,9 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
|
|||
I->Name = TD->getNameAsString();
|
||||
I->IsTypeDef = true;
|
||||
}
|
||||
// TODO: remove first call to parseBases, that function should be deleted
|
||||
parseBases(*I, C);
|
||||
parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
|
||||
}
|
||||
I->Path = getInfoRelativePath(I->Namespace);
|
||||
|
||||
|
@ -464,8 +568,7 @@ emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
|
|||
populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
|
||||
IsInAnonymousNamespace);
|
||||
Func.Access = clang::AccessSpecifier::AS_none;
|
||||
if (PublicOnly && ((IsInAnonymousNamespace ||
|
||||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
|
||||
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
||||
return {};
|
||||
|
||||
// Wrap in enclosing scope
|
||||
|
@ -488,8 +591,7 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
|
|||
bool IsInAnonymousNamespace = false;
|
||||
populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
|
||||
IsInAnonymousNamespace);
|
||||
if (PublicOnly && ((IsInAnonymousNamespace ||
|
||||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
|
||||
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
||||
return {};
|
||||
|
||||
Func.IsMethod = true;
|
||||
|
@ -523,8 +625,7 @@ emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
|
|||
bool IsInAnonymousNamespace = false;
|
||||
populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
|
||||
IsInAnonymousNamespace);
|
||||
if (PublicOnly && ((IsInAnonymousNamespace ||
|
||||
!isPublic(D->getAccess(), D->getLinkageInternal()))))
|
||||
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
||||
return {};
|
||||
|
||||
Enum.Scoped = D->isScoped();
|
||||
|
|
|
@ -21,6 +21,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Location)
|
|||
LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<CommentInfo>)
|
||||
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>)
|
||||
|
||||
|
@ -124,6 +125,19 @@ static void SymbolInfoMapping(IO &IO, SymbolInfo &I) {
|
|||
IO.mapOptional("Location", I.Loc, llvm::SmallVector<Location, 2>());
|
||||
}
|
||||
|
||||
static void RecordInfoMapping(IO &IO, RecordInfo &I) {
|
||||
SymbolInfoMapping(IO, I);
|
||||
IO.mapOptional("TagType", I.TagType, clang::TagTypeKind::TTK_Struct);
|
||||
IO.mapOptional("Members", I.Members);
|
||||
IO.mapOptional("Bases", I.Bases);
|
||||
IO.mapOptional("Parents", I.Parents, llvm::SmallVector<Reference, 4>());
|
||||
IO.mapOptional("VirtualParents", I.VirtualParents,
|
||||
llvm::SmallVector<Reference, 4>());
|
||||
IO.mapOptional("ChildRecords", I.ChildRecords, std::vector<Reference>());
|
||||
IO.mapOptional("ChildFunctions", I.ChildFunctions);
|
||||
IO.mapOptional("ChildEnums", I.ChildEnums);
|
||||
}
|
||||
|
||||
static void CommentInfoMapping(IO &IO, CommentInfo &I) {
|
||||
IO.mapOptional("Kind", I.Kind, SmallString<16>());
|
||||
IO.mapOptional("Text", I.Text, SmallString<64>());
|
||||
|
@ -193,16 +207,18 @@ template <> struct MappingTraits<NamespaceInfo> {
|
|||
};
|
||||
|
||||
template <> struct MappingTraits<RecordInfo> {
|
||||
static void mapping(IO &IO, RecordInfo &I) {
|
||||
SymbolInfoMapping(IO, I);
|
||||
IO.mapOptional("TagType", I.TagType, clang::TagTypeKind::TTK_Struct);
|
||||
IO.mapOptional("Members", I.Members);
|
||||
IO.mapOptional("Parents", I.Parents, llvm::SmallVector<Reference, 4>());
|
||||
IO.mapOptional("VirtualParents", I.VirtualParents,
|
||||
llvm::SmallVector<Reference, 4>());
|
||||
IO.mapOptional("ChildRecords", I.ChildRecords, std::vector<Reference>());
|
||||
IO.mapOptional("ChildFunctions", I.ChildFunctions);
|
||||
IO.mapOptional("ChildEnums", I.ChildEnums);
|
||||
static void mapping(IO &IO, RecordInfo &I) { RecordInfoMapping(IO, I); }
|
||||
};
|
||||
|
||||
template <> struct MappingTraits<BaseRecordInfo> {
|
||||
static void mapping(IO &IO, BaseRecordInfo &I) {
|
||||
RecordInfoMapping(IO, I);
|
||||
IO.mapOptional("IsVirtual", I.IsVirtual, false);
|
||||
// clang::AccessSpecifier::AS_none is used as the default here because it's
|
||||
// the AS that shouldn't be part of the output. Even though AS_public is the
|
||||
// default in the struct, it should be displayed in the YAML output.
|
||||
IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none);
|
||||
IO.mapOptional("IsParent", I.IsParent, false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -81,6 +81,10 @@ TEST(BitcodeTest, emitRecordInfoBitcode) {
|
|||
I.Members.emplace_back("int", "X", AccessSpecifier::AS_private);
|
||||
I.TagType = TagTypeKind::TTK_Class;
|
||||
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().Members.emplace_back("int", "X", AccessSpecifier::AS_private);
|
||||
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
|
||||
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ClangDocTest.h"
|
||||
#include "Representation.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
@ -168,6 +169,10 @@ void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) {
|
|||
for (size_t Idx = 0; Idx < Actual->VirtualParents.size(); ++Idx)
|
||||
CheckReference(Expected->VirtualParents[Idx], Actual->VirtualParents[Idx]);
|
||||
|
||||
ASSERT_EQ(Expected->Bases.size(), Actual->Bases.size());
|
||||
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]);
|
||||
|
@ -182,6 +187,14 @@ void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) {
|
|||
CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]);
|
||||
}
|
||||
|
||||
void CheckBaseRecordInfo(BaseRecordInfo *Expected, BaseRecordInfo *Actual) {
|
||||
CheckRecordInfo(Expected, Actual);
|
||||
|
||||
EXPECT_EQ(Expected->IsVirtual, Actual->IsVirtual);
|
||||
EXPECT_EQ(Expected->Access, Actual->Access);
|
||||
EXPECT_EQ(Expected->IsParent, Actual->IsParent);
|
||||
}
|
||||
|
||||
void CheckIndex(Index &Expected, Index &Actual) {
|
||||
CheckReference(Expected, Actual);
|
||||
ASSERT_EQ(Expected.Children.size(), Actual.Children.size());
|
||||
|
|
|
@ -43,6 +43,7 @@ void CheckFunctionInfo(FunctionInfo *Expected, FunctionInfo *Actual);
|
|||
void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual);
|
||||
void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual);
|
||||
void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual);
|
||||
void CheckBaseRecordInfo(BaseRecordInfo *Expected, BaseRecordInfo *Actual);
|
||||
|
||||
void CheckIndex(Index &Expected, Index &Actual);
|
||||
|
||||
|
|
|
@ -87,6 +87,8 @@ TEST(MergeTest, mergeRecordInfos) {
|
|||
One.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
|
||||
One.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
|
||||
|
||||
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();
|
||||
|
@ -126,6 +128,8 @@ TEST(MergeTest, mergeRecordInfos) {
|
|||
Expected->TagType = TagTypeKind::TTK_Class;
|
||||
Expected->Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
|
||||
Expected->VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
|
||||
Expected->Bases.emplace_back(EmptySID, "F", "path/to/F", true,
|
||||
AccessSpecifier::AS_protected, true);
|
||||
|
||||
Expected->ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
|
||||
InfoType::IT_record, "path");
|
||||
|
|
|
@ -321,15 +321,16 @@ TEST(SerializeTest, emitInlinedFunctionInfo) {
|
|||
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
|
||||
}
|
||||
|
||||
TEST(SerializeTest, ) {
|
||||
TEST(SerializeTest, emitInheritedRecordInfo) {
|
||||
EmittedInfoList Infos;
|
||||
ExtractInfosFromCode(R"raw(class F {};
|
||||
class G {} ;
|
||||
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 H {} ;
|
||||
class I : public H<int> {} ;)raw",
|
||||
10, /*Public=*/false, Infos);
|
||||
class I {} ;
|
||||
class J : public I<int> {} ;)raw",
|
||||
14, /*Public=*/false, Infos);
|
||||
|
||||
RecordInfo *F = InfoAsRecord(Infos[0].get());
|
||||
RecordInfo ExpectedF(EmptySID, "F");
|
||||
|
@ -337,32 +338,91 @@ class I : public H<int> {} ;)raw",
|
|||
ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
|
||||
CheckRecordInfo(&ExpectedF, F);
|
||||
|
||||
RecordInfo *G = InfoAsRecord(Infos[2].get());
|
||||
RecordInfo *G = InfoAsRecord(Infos[3].get());
|
||||
RecordInfo ExpectedG(EmptySID, "G");
|
||||
ExpectedG.TagType = TagTypeKind::TTK_Class;
|
||||
ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
|
||||
ExpectedG.Members.emplace_back("int", "I", AccessSpecifier::AS_protected);
|
||||
CheckRecordInfo(&ExpectedG, G);
|
||||
|
||||
RecordInfo *E = InfoAsRecord(Infos[4].get());
|
||||
RecordInfo *E = InfoAsRecord(Infos[6].get());
|
||||
RecordInfo ExpectedE(EmptySID, "E");
|
||||
ExpectedE.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
|
||||
ExpectedE.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
|
||||
ExpectedE.Bases.emplace_back(EmptySID, "F", "", false,
|
||||
AccessSpecifier::AS_public, true);
|
||||
FunctionInfo FunctionSet;
|
||||
FunctionSet.Name = "set";
|
||||
FunctionSet.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default);
|
||||
FunctionSet.Loc.emplace_back();
|
||||
FunctionSet.Params.emplace_back("int", "N");
|
||||
FunctionSet.Namespace.emplace_back(EmptySID, "F", InfoType::IT_record);
|
||||
FunctionSet.Access = AccessSpecifier::AS_protected;
|
||||
FunctionSet.IsMethod = true;
|
||||
ExpectedE.Bases.back().ChildFunctions.emplace_back(std::move(FunctionSet));
|
||||
ExpectedE.Bases.emplace_back(EmptySID, "G", "", true,
|
||||
AccessSpecifier::AS_private, true);
|
||||
FunctionInfo FunctionGet;
|
||||
FunctionGet.Name = "get";
|
||||
FunctionGet.ReturnType = TypeInfo(EmptySID, "int", InfoType::IT_default);
|
||||
FunctionGet.DefLoc = Location();
|
||||
FunctionGet.Namespace.emplace_back(EmptySID, "G", InfoType::IT_record);
|
||||
FunctionGet.Access = AccessSpecifier::AS_private;
|
||||
FunctionGet.IsMethod = true;
|
||||
ExpectedE.Bases.back().ChildFunctions.emplace_back(std::move(FunctionGet));
|
||||
ExpectedE.Bases.back().Members.emplace_back("int", "I",
|
||||
AccessSpecifier::AS_private);
|
||||
ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
|
||||
ExpectedE.TagType = TagTypeKind::TTK_Class;
|
||||
CheckRecordInfo(&ExpectedE, E);
|
||||
|
||||
RecordInfo *H = InfoAsRecord(Infos[6].get());
|
||||
RecordInfo *H = InfoAsRecord(Infos[8].get());
|
||||
RecordInfo ExpectedH(EmptySID, "H");
|
||||
ExpectedH.TagType = TagTypeKind::TTK_Class;
|
||||
ExpectedH.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
|
||||
ExpectedH.Parents.emplace_back(EmptySID, "E", InfoType::IT_record);
|
||||
ExpectedH.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
|
||||
ExpectedH.Bases.emplace_back(EmptySID, "E", "", false,
|
||||
AccessSpecifier::AS_private, true);
|
||||
ExpectedH.Bases.emplace_back(EmptySID, "F", "", false,
|
||||
AccessSpecifier::AS_private, false);
|
||||
FunctionInfo FunctionSetNew;
|
||||
FunctionSetNew.Name = "set";
|
||||
FunctionSetNew.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default);
|
||||
FunctionSetNew.Loc.emplace_back();
|
||||
FunctionSetNew.Params.emplace_back("int", "N");
|
||||
FunctionSetNew.Namespace.emplace_back(EmptySID, "F", InfoType::IT_record);
|
||||
FunctionSetNew.Access = AccessSpecifier::AS_private;
|
||||
FunctionSetNew.IsMethod = true;
|
||||
ExpectedH.Bases.back().ChildFunctions.emplace_back(std::move(FunctionSetNew));
|
||||
ExpectedH.Bases.emplace_back(EmptySID, "G", "", true,
|
||||
AccessSpecifier::AS_private, false);
|
||||
FunctionInfo FunctionGetNew;
|
||||
FunctionGetNew.Name = "get";
|
||||
FunctionGetNew.ReturnType = TypeInfo(EmptySID, "int", InfoType::IT_default);
|
||||
FunctionGetNew.DefLoc = Location();
|
||||
FunctionGetNew.Namespace.emplace_back(EmptySID, "G", InfoType::IT_record);
|
||||
FunctionGetNew.Access = AccessSpecifier::AS_private;
|
||||
FunctionGetNew.IsMethod = true;
|
||||
ExpectedH.Bases.back().ChildFunctions.emplace_back(std::move(FunctionGetNew));
|
||||
ExpectedH.Bases.back().Members.emplace_back("int", "I",
|
||||
AccessSpecifier::AS_private);
|
||||
CheckRecordInfo(&ExpectedH, H);
|
||||
|
||||
RecordInfo *I = InfoAsRecord(Infos[8].get());
|
||||
RecordInfo *I = InfoAsRecord(Infos[10].get());
|
||||
RecordInfo ExpectedI(EmptySID, "I");
|
||||
ExpectedI.Parents.emplace_back(EmptySID, "H<int>", InfoType::IT_record);
|
||||
ExpectedI.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
|
||||
ExpectedI.TagType = TagTypeKind::TTK_Class;
|
||||
ExpectedI.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
|
||||
CheckRecordInfo(&ExpectedI, I);
|
||||
|
||||
RecordInfo *J = InfoAsRecord(Infos[12].get());
|
||||
RecordInfo ExpectedJ(EmptySID, "J");
|
||||
ExpectedJ.Parents.emplace_back(EmptySID, "I<int>", InfoType::IT_record);
|
||||
ExpectedJ.Bases.emplace_back(EmptySID, "I<int>", "", false,
|
||||
AccessSpecifier::AS_public, true);
|
||||
ExpectedJ.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
|
||||
ExpectedJ.TagType = TagTypeKind::TTK_Class;
|
||||
CheckRecordInfo(&ExpectedJ, J);
|
||||
}
|
||||
|
||||
TEST(SerializeTest, emitModulePublicLFunctions) {
|
||||
|
|
|
@ -84,6 +84,12 @@ TEST(YAMLGeneratorTest, emitRecordYAML) {
|
|||
I.Members.emplace_back("int", "path/to/int", "X",
|
||||
AccessSpecifier::AS_private);
|
||||
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().Members.emplace_back("int", "path/to/int", "N",
|
||||
AccessSpecifier::AS_private);
|
||||
// F is in the global namespace
|
||||
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "");
|
||||
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
|
||||
|
@ -123,6 +129,24 @@ Members:
|
|||
Path: 'path/to/int'
|
||||
Name: 'X'
|
||||
Access: Private
|
||||
Bases:
|
||||
- USR: '0000000000000000000000000000000000000000'
|
||||
Name: 'F'
|
||||
Path: 'path/to/F'
|
||||
Members:
|
||||
- Type:
|
||||
Name: 'int'
|
||||
Path: 'path/to/int'
|
||||
Name: 'N'
|
||||
Access: Private
|
||||
ChildFunctions:
|
||||
- USR: '0000000000000000000000000000000000000000'
|
||||
Name: 'InheritedFunctionOne'
|
||||
ReturnType: {}
|
||||
Access: Public
|
||||
IsVirtual: true
|
||||
Access: Public
|
||||
IsParent: true
|
||||
Parents:
|
||||
- Type: Record
|
||||
Name: 'F'
|
||||
|
|
Loading…
Reference in New Issue