[clang-doc] Add typedef/using information.

Read typedef and "using" type alias declarations and serialize into the internal structures. Emit this information in the YAML output. The HTML and MD generators are unchanged.

Separate out the logic to create the parent namespace or record object and insert the newly created child into it. This logic was previously duplicated for every "info" type and is now shared.

To help this, a struct containing the child vectors was separated out so children can be added generically and without having too many templates.

A small change was made to populateParentNamespaces() to allow using types that aren't themselves DeclContexts (typedefs are the first example of this).

Reviewed By: paulkirth, haowei

Differential Revision: https://reviews.llvm.org/D134371
This commit is contained in:
Brett Wilson 2022-09-27 23:33:29 +00:00 committed by Paul Kirth
parent 0afc60858e
commit eed22583fd
20 changed files with 649 additions and 339 deletions

View File

@ -24,12 +24,6 @@ llvm::Error decodeRecord(const Record &R, llvm::SmallVectorImpl<char> &Field,
return llvm::Error::success();
}
llvm::Error decodeRecord(const Record &R, std::string &Field,
llvm::StringRef Blob) {
Field.assign(Blob.begin(), Blob.end());
return llvm::Error::success();
}
llvm::Error decodeRecord(const Record &R, SymbolID &Field,
llvm::StringRef Blob) {
if (R[0] != BitCodeConstants::USRHashSize)
@ -104,6 +98,7 @@ llvm::Error decodeRecord(const Record &R, InfoType &Field,
case InfoType::IT_function:
case InfoType::IT_default:
case InfoType::IT_enum:
case InfoType::IT_typedef:
Field = IT;
return llvm::Error::success();
}
@ -233,6 +228,23 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
}
}
llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
TypedefInfo *I) {
switch (ID) {
case TYPEDEF_USR:
return decodeRecord(R, I->USR, Blob);
case TYPEDEF_NAME:
return decodeRecord(R, I->Name, Blob);
case TYPEDEF_DEFLOCATION:
return decodeRecord(R, I->DefLoc, Blob);
case TYPEDEF_IS_USING:
return decodeRecord(R, I->IsUsing, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for TypedefInfo");
}
}
llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
EnumValueInfo *I) {
switch (ID) {
@ -424,6 +436,11 @@ template <> llvm::Error addTypeInfo(EnumInfo *I, TypeInfo &&T) {
return llvm::Error::success();
}
template <> llvm::Error addTypeInfo(TypedefInfo *I, TypeInfo &&T) {
I->Underlying = std::move(T);
return llvm::Error::success();
}
template <typename T> llvm::Error addReference(T I, Reference &&R, FieldId F) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
@ -475,6 +492,17 @@ template <> llvm::Error addReference(EnumInfo *I, Reference &&R, FieldId F) {
}
}
template <> llvm::Error addReference(TypedefInfo *I, Reference &&R, FieldId F) {
switch (F) {
case FieldId::F_namespace:
I->Namespace.emplace_back(std::move(R));
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid type cannot contain Reference");
}
}
template <>
llvm::Error addReference(NamespaceInfo *I, Reference &&R, FieldId F) {
switch (F) {
@ -482,10 +510,10 @@ llvm::Error addReference(NamespaceInfo *I, Reference &&R, FieldId F) {
I->Namespace.emplace_back(std::move(R));
return llvm::Error::success();
case FieldId::F_child_namespace:
I->ChildNamespaces.emplace_back(std::move(R));
I->Children.Namespaces.emplace_back(std::move(R));
return llvm::Error::success();
case FieldId::F_child_record:
I->ChildRecords.emplace_back(std::move(R));
I->Children.Records.emplace_back(std::move(R));
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
@ -520,7 +548,7 @@ template <> llvm::Error addReference(RecordInfo *I, Reference &&R, FieldId F) {
I->VirtualParents.emplace_back(std::move(R));
return llvm::Error::success();
case FieldId::F_child_record:
I->ChildRecords.emplace_back(std::move(R));
I->Children.Records.emplace_back(std::move(R));
return llvm::Error::success();
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
@ -534,32 +562,37 @@ void addChild(T I, ChildInfoType &&R) {
exit(1);
}
// Namespace children:
template <> void addChild(NamespaceInfo *I, FunctionInfo &&R) {
I->ChildFunctions.emplace_back(std::move(R));
I->Children.Functions.emplace_back(std::move(R));
}
template <> void addChild(NamespaceInfo *I, EnumInfo &&R) {
I->ChildEnums.emplace_back(std::move(R));
I->Children.Enums.emplace_back(std::move(R));
}
template <> void addChild(NamespaceInfo *I, TypedefInfo &&R) {
I->Children.Typedefs.emplace_back(std::move(R));
}
// Record children:
template <> void addChild(RecordInfo *I, FunctionInfo &&R) {
I->ChildFunctions.emplace_back(std::move(R));
I->Children.Functions.emplace_back(std::move(R));
}
template <> void addChild(RecordInfo *I, EnumInfo &&R) {
I->ChildEnums.emplace_back(std::move(R));
I->Children.Enums.emplace_back(std::move(R));
}
template <> void addChild(RecordInfo *I, TypedefInfo &&R) {
I->Children.Typedefs.emplace_back(std::move(R));
}
// Other types of children:
template <> void addChild(EnumInfo *I, EnumValueInfo &&R) {
I->Members.emplace_back(std::move(R));
}
template <> void addChild(RecordInfo *I, BaseRecordInfo &&R) {
I->Bases.emplace_back(std::move(R));
}
template <> void addChild(BaseRecordInfo *I, FunctionInfo &&R) {
I->ChildFunctions.emplace_back(std::move(R));
I->Children.Functions.emplace_back(std::move(R));
}
// Read records from bitcode into a given info.
@ -686,6 +719,13 @@ llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
addChild(I, std::move(EV));
return llvm::Error::success();
}
case BI_TYPEDEF_BLOCK_ID: {
TypedefInfo TI;
if (auto Err = readBlock(ID, &TI))
return Err;
addChild(I, std::move(TI));
return llvm::Error::success();
}
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid subblock type");
@ -786,6 +826,8 @@ ClangDocBitcodeReader::readBlockToInfo(unsigned ID) {
return createInfo<RecordInfo>(ID);
case BI_ENUM_BLOCK_ID:
return createInfo<EnumInfo>(ID);
case BI_TYPEDEF_BLOCK_ID:
return createInfo<TypedefInfo>(ID);
case BI_FUNCTION_BLOCK_ID:
return createInfo<FunctionInfo>(ID);
default:
@ -825,6 +867,7 @@ ClangDocBitcodeReader::readBitcode() {
case BI_NAMESPACE_BLOCK_ID:
case BI_RECORD_BLOCK_ID:
case BI_ENUM_BLOCK_ID:
case BI_TYPEDEF_BLOCK_ID:
case BI_FUNCTION_BLOCK_ID: {
auto InfoOrErr = readBlockToInfo(ID);
if (!InfoOrErr)

View File

@ -113,6 +113,7 @@ static const llvm::IndexedMap<llvm::StringRef, BlockIdToIndexFunctor>
{BI_NAMESPACE_BLOCK_ID, "NamespaceBlock"},
{BI_ENUM_BLOCK_ID, "EnumBlock"},
{BI_ENUM_VALUE_BLOCK_ID, "EnumValueBlock"},
{BI_TYPEDEF_BLOCK_ID, "TypedefBlock"},
{BI_TYPE_BLOCK_ID, "TypeBlock"},
{BI_FIELD_TYPE_BLOCK_ID, "FieldTypeBlock"},
{BI_MEMBER_TYPE_BLOCK_ID, "MemberTypeBlock"},
@ -187,7 +188,11 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{REFERENCE_NAME, {"Name", &StringAbbrev}},
{REFERENCE_TYPE, {"RefType", &IntAbbrev}},
{REFERENCE_PATH, {"Path", &StringAbbrev}},
{REFERENCE_FIELD, {"Field", &IntAbbrev}}};
{REFERENCE_FIELD, {"Field", &IntAbbrev}},
{TYPEDEF_USR, {"USR", &SymbolIDAbbrev}},
{TYPEDEF_NAME, {"Name", &StringAbbrev}},
{TYPEDEF_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
{TYPEDEF_IS_USING, {"IsUsing", &BoolAbbrev}}};
assert(Inits.size() == RecordIdCount);
for (const auto &Init : Inits) {
RecordIdNameMap[Init.first] = Init.second;
@ -218,6 +223,9 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
// Enum Value Block
{BI_ENUM_VALUE_BLOCK_ID,
{ENUM_VALUE_NAME, ENUM_VALUE_VALUE, ENUM_VALUE_EXPR}},
// Typedef Block
{BI_TYPEDEF_BLOCK_ID,
{TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DEFLOCATION, TYPEDEF_IS_USING}},
// Namespace Block
{BI_NAMESPACE_BLOCK_ID,
{NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
@ -418,6 +426,18 @@ void ClangDocBitcodeWriter::emitBlock(const TypeInfo &T) {
emitBlock(T.Type, FieldId::F_type);
}
void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) {
StreamSubBlockGuard Block(Stream, BI_TYPEDEF_BLOCK_ID);
emitRecord(T.USR, TYPEDEF_USR);
emitRecord(T.Name, TYPEDEF_NAME);
for (const auto &N : T.Namespace)
emitBlock(N, FieldId::F_namespace);
if (T.DefLoc)
emitRecord(*T.DefLoc, TYPEDEF_DEFLOCATION);
emitRecord(T.IsUsing, TYPEDEF_IS_USING);
emitBlock(T.Underlying);
}
void ClangDocBitcodeWriter::emitBlock(const FieldTypeInfo &T) {
StreamSubBlockGuard Block(Stream, BI_FIELD_TYPE_BLOCK_ID);
emitBlock(T.Type, FieldId::F_type);
@ -465,13 +485,15 @@ void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) {
emitBlock(N, FieldId::F_namespace);
for (const auto &CI : I.Description)
emitBlock(CI);
for (const auto &C : I.ChildNamespaces)
for (const auto &C : I.Children.Namespaces)
emitBlock(C, FieldId::F_child_namespace);
for (const auto &C : I.ChildRecords)
for (const auto &C : I.Children.Records)
emitBlock(C, FieldId::F_child_record);
for (const auto &C : I.ChildFunctions)
for (const auto &C : I.Children.Functions)
emitBlock(C);
for (const auto &C : I.ChildEnums)
for (const auto &C : I.Children.Enums)
emitBlock(C);
for (const auto &C : I.Children.Typedefs)
emitBlock(C);
}
@ -524,11 +546,13 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
emitBlock(P, FieldId::F_vparent);
for (const auto &PB : I.Bases)
emitBlock(PB);
for (const auto &C : I.ChildRecords)
for (const auto &C : I.Children.Records)
emitBlock(C, FieldId::F_child_record);
for (const auto &C : I.ChildFunctions)
for (const auto &C : I.Children.Functions)
emitBlock(C);
for (const auto &C : I.ChildEnums)
for (const auto &C : I.Children.Enums)
emitBlock(C);
for (const auto &C : I.Children.Typedefs)
emitBlock(C);
}
@ -543,7 +567,7 @@ void ClangDocBitcodeWriter::emitBlock(const BaseRecordInfo &I) {
emitRecord(I.IsParent, BASE_RECORD_IS_PARENT);
for (const auto &M : I.Members)
emitBlock(M);
for (const auto &C : I.ChildFunctions)
for (const auto &C : I.Children.Functions)
emitBlock(C);
}
@ -581,6 +605,9 @@ bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) {
case InfoType::IT_function:
emitBlock(*static_cast<clang::doc::FunctionInfo *>(I));
break;
case InfoType::IT_typedef:
emitBlock(*static_cast<clang::doc::TypedefInfo *>(I));
break;
default:
llvm::errs() << "Unexpected info, unable to write.\n";
return true;

View File

@ -64,6 +64,7 @@ enum BlockId {
BI_FUNCTION_BLOCK_ID,
BI_COMMENT_BLOCK_ID,
BI_REFERENCE_BLOCK_ID,
BI_TYPEDEF_BLOCK_ID,
BI_LAST,
BI_FIRST = BI_VERSION_BLOCK_ID
};
@ -123,6 +124,10 @@ enum RecordId {
REFERENCE_TYPE,
REFERENCE_PATH,
REFERENCE_FIELD,
TYPEDEF_USR,
TYPEDEF_NAME,
TYPEDEF_DEFLOCATION,
TYPEDEF_IS_USING,
RI_LAST,
RI_FIRST = VERSION
};
@ -160,8 +165,9 @@ public:
void emitBlock(const EnumInfo &I);
void emitBlock(const EnumValueInfo &I);
void emitBlock(const TypeInfo &B);
void emitBlock(const TypedefInfo &B);
void emitBlock(const FieldTypeInfo &B);
void emitBlock(const MemberTypeInfo &B);
void emitBlock(const MemberTypeInfo &T);
void emitBlock(const CommentInfo &B);
void emitBlock(const Reference &B, FieldId F);

View File

@ -734,28 +734,29 @@ genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
genReferencesBlock(I.ChildNamespaces, "Namespaces", BasePath);
genReferencesBlock(I.Children.Namespaces, "Namespaces", BasePath);
AppendVector(std::move(ChildNamespaces), Out);
std::vector<std::unique_ptr<TagNode>> ChildRecords =
genReferencesBlock(I.ChildRecords, "Records", BasePath);
genReferencesBlock(I.Children.Records, "Records", BasePath);
AppendVector(std::move(ChildRecords), Out);
std::vector<std::unique_ptr<TagNode>> ChildFunctions =
genFunctionsBlock(I.ChildFunctions, CDCtx, BasePath);
genFunctionsBlock(I.Children.Functions, CDCtx, BasePath);
AppendVector(std::move(ChildFunctions), Out);
std::vector<std::unique_ptr<TagNode>> ChildEnums =
genEnumsBlock(I.ChildEnums, CDCtx);
genEnumsBlock(I.Children.Enums, CDCtx);
AppendVector(std::move(ChildEnums), Out);
if (!I.ChildNamespaces.empty())
if (!I.Children.Namespaces.empty())
InfoIndex.Children.emplace_back("Namespaces", "Namespaces");
if (!I.ChildRecords.empty())
if (!I.Children.Records.empty())
InfoIndex.Children.emplace_back("Records", "Records");
if (!I.ChildFunctions.empty())
if (!I.Children.Functions.empty())
InfoIndex.Children.emplace_back(
genInfoIndexItem(I.ChildFunctions, "Functions"));
if (!I.ChildEnums.empty())
InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums"));
genInfoIndexItem(I.Children.Functions, "Functions"));
if (!I.Children.Enums.empty())
InfoIndex.Children.emplace_back(
genInfoIndexItem(I.Children.Enums, "Enums"));
return Out;
}
@ -802,25 +803,26 @@ genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
genRecordMembersBlock(I.Members, I.Path);
AppendVector(std::move(Members), Out);
std::vector<std::unique_ptr<TagNode>> ChildRecords =
genReferencesBlock(I.ChildRecords, "Records", I.Path);
genReferencesBlock(I.Children.Records, "Records", I.Path);
AppendVector(std::move(ChildRecords), Out);
std::vector<std::unique_ptr<TagNode>> ChildFunctions =
genFunctionsBlock(I.ChildFunctions, CDCtx, I.Path);
genFunctionsBlock(I.Children.Functions, CDCtx, I.Path);
AppendVector(std::move(ChildFunctions), Out);
std::vector<std::unique_ptr<TagNode>> ChildEnums =
genEnumsBlock(I.ChildEnums, CDCtx);
genEnumsBlock(I.Children.Enums, CDCtx);
AppendVector(std::move(ChildEnums), Out);
if (!I.Members.empty())
InfoIndex.Children.emplace_back("Members", "Members");
if (!I.ChildRecords.empty())
if (!I.Children.Records.empty())
InfoIndex.Children.emplace_back("Records", "Records");
if (!I.ChildFunctions.empty())
if (!I.Children.Functions.empty())
InfoIndex.Children.emplace_back(
genInfoIndexItem(I.ChildFunctions, "Functions"));
if (!I.ChildEnums.empty())
InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums"));
genInfoIndexItem(I.Children.Functions, "Functions"));
if (!I.Children.Enums.empty())
InfoIndex.Children.emplace_back(
genInfoIndexItem(I.Children.Enums, "Enums"));
return Out;
}

View File

@ -189,9 +189,9 @@ static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
if (!I.ChildNamespaces.empty()) {
if (!I.Children.Namespaces.empty()) {
writeHeader("Namespaces", 2, OS);
for (const auto &R : I.ChildNamespaces) {
for (const auto &R : I.Children.Namespaces) {
OS << "* ";
writeNameLink(BasePath, R, OS);
OS << "\n";
@ -199,9 +199,9 @@ static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
writeNewLine(OS);
}
if (!I.ChildRecords.empty()) {
if (!I.Children.Records.empty()) {
writeHeader("Records", 2, OS);
for (const auto &R : I.ChildRecords) {
for (const auto &R : I.Children.Records) {
OS << "* ";
writeNameLink(BasePath, R, OS);
OS << "\n";
@ -209,15 +209,15 @@ static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
writeNewLine(OS);
}
if (!I.ChildFunctions.empty()) {
if (!I.Children.Functions.empty()) {
writeHeader("Functions", 2, OS);
for (const auto &F : I.ChildFunctions)
for (const auto &F : I.Children.Functions)
genMarkdown(CDCtx, F, OS);
writeNewLine(OS);
}
if (!I.ChildEnums.empty()) {
if (!I.Children.Enums.empty()) {
writeHeader("Enums", 2, OS);
for (const auto &E : I.ChildEnums)
for (const auto &E : I.Children.Enums)
genMarkdown(CDCtx, E, OS);
writeNewLine(OS);
}
@ -259,21 +259,21 @@ static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I,
writeNewLine(OS);
}
if (!I.ChildRecords.empty()) {
if (!I.Children.Records.empty()) {
writeHeader("Records", 2, OS);
for (const auto &R : I.ChildRecords)
for (const auto &R : I.Children.Records)
writeLine(R.Name, OS);
writeNewLine(OS);
}
if (!I.ChildFunctions.empty()) {
if (!I.Children.Functions.empty()) {
writeHeader("Functions", 2, OS);
for (const auto &F : I.ChildFunctions)
for (const auto &F : I.Children.Functions)
genMarkdown(CDCtx, F, OS);
writeNewLine(OS);
}
if (!I.ChildEnums.empty()) {
if (!I.Children.Enums.empty()) {
writeHeader("Enums", 2, OS);
for (const auto &E : I.ChildEnums)
for (const auto &E : I.Children.Enums)
genMarkdown(CDCtx, E, OS);
writeNewLine(OS);
}

View File

@ -71,6 +71,14 @@ bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl *D) {
return mapDecl(D);
}
bool MapASTVisitor::VisitTypedefDecl(const TypedefDecl *D) {
return mapDecl(D);
}
bool MapASTVisitor::VisitTypeAliasDecl(const TypeAliasDecl *D) {
return mapDecl(D);
}
comments::FullComment *
MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const {
RawComment *Comment = Context.getRawCommentForDeclNoCache(D);

View File

@ -39,6 +39,8 @@ public:
bool VisitEnumDecl(const EnumDecl *D);
bool VisitCXXMethodDecl(const CXXMethodDecl *D);
bool VisitFunctionDecl(const FunctionDecl *D);
bool VisitTypedefDecl(const TypedefDecl *D);
bool VisitTypeAliasDecl(const TypeAliasDecl *D);
private:
template <typename T> bool mapDecl(const T *D);

View File

@ -90,6 +90,18 @@ void reduceChildren(std::vector<EnumInfo> &Children,
}
}
void reduceChildren(std::vector<TypedefInfo> &Children,
std::vector<TypedefInfo> &&ChildrenToMerge) {
for (auto &ChildToMerge : ChildrenToMerge) {
int mergeIdx = getChildIndexIfExists(Children, ChildToMerge);
if (mergeIdx == -1) {
Children.push_back(std::move(ChildToMerge));
continue;
}
Children[mergeIdx].merge(std::move(ChildToMerge));
}
}
} // namespace
// Dispatch function.
@ -108,6 +120,8 @@ mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
return reduce<EnumInfo>(Values);
case InfoType::IT_function:
return reduce<FunctionInfo>(Values);
case InfoType::IT_typedef:
return reduce<TypedefInfo>(Values);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unexpected info type");
@ -209,10 +223,11 @@ void SymbolInfo::merge(SymbolInfo &&Other) {
void NamespaceInfo::merge(NamespaceInfo &&Other) {
assert(mergeable(Other));
// Reduce children if necessary.
reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces));
reduceChildren(ChildRecords, std::move(Other.ChildRecords));
reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
reduceChildren(ChildEnums, std::move(Other.ChildEnums));
reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
reduceChildren(Children.Records, std::move(Other.Children.Records));
reduceChildren(Children.Functions, std::move(Other.Children.Functions));
reduceChildren(Children.Enums, std::move(Other.Children.Enums));
reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
mergeBase(std::move(Other));
}
@ -230,9 +245,10 @@ void RecordInfo::merge(RecordInfo &&Other) {
if (VirtualParents.empty())
VirtualParents = std::move(Other.VirtualParents);
// Reduce children if necessary.
reduceChildren(ChildRecords, std::move(Other.ChildRecords));
reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
reduceChildren(ChildEnums, std::move(Other.ChildEnums));
reduceChildren(Children.Records, std::move(Other.Children.Records));
reduceChildren(Children.Functions, std::move(Other.Children.Functions));
reduceChildren(Children.Enums, std::move(Other.Children.Enums));
reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
SymbolInfo::merge(std::move(Other));
}
@ -260,6 +276,15 @@ void FunctionInfo::merge(FunctionInfo &&Other) {
SymbolInfo::merge(std::move(Other));
}
void TypedefInfo::merge(TypedefInfo &&Other) {
assert(mergeable(Other));
if (!IsUsing)
IsUsing = Other.IsUsing;
if (Underlying.Type.Name == "")
Underlying = Other.Underlying;
SymbolInfo::merge(std::move(Other));
}
llvm::SmallString<16> Info::extractName() const {
if (!Name.empty())
return Name;
@ -282,6 +307,9 @@ llvm::SmallString<16> Info::extractName() const {
case InfoType::IT_enum:
return llvm::SmallString<16>("@nonymous_enum_" +
toHex(llvm::toStringRef(USR)));
case InfoType::IT_typedef:
return llvm::SmallString<16>("@nonymous_typedef_" +
toHex(llvm::toStringRef(USR)));
case InfoType::IT_function:
return llvm::SmallString<16>("@nonymous_function_" +
toHex(llvm::toStringRef(USR)));

View File

@ -30,17 +30,19 @@ namespace doc {
// SHA1'd hash of a USR.
using SymbolID = std::array<uint8_t, 20>;
struct Info;
struct FunctionInfo;
struct EnumInfo;
struct BaseRecordInfo;
struct EnumInfo;
struct FunctionInfo;
struct Info;
struct TypedefInfo;
enum class InfoType {
IT_default,
IT_namespace,
IT_record,
IT_function,
IT_enum
IT_enum,
IT_typedef
};
// A representation of a parsed comment.
@ -142,6 +144,22 @@ struct Reference {
llvm::SmallString<128> Path;
};
// Holds the children of a record or namespace.
struct ScopeChildren {
// Namespaces and Records are references because they will be properly
// documented in their own info, while the entirety of Functions and Enums are
// included here because they should not have separate documentation from
// their scope.
//
// Namespaces are not syntactically valid as children of records, but making
// this general for all possible container types reduces code complexity.
std::vector<Reference> Namespaces;
std::vector<Reference> Records;
std::vector<FunctionInfo> Functions;
std::vector<EnumInfo> Enums;
std::vector<TypedefInfo> Typedefs;
};
// A base struct for TypeInfos
struct TypeInfo {
TypeInfo() = default;
@ -199,7 +217,7 @@ struct MemberTypeInfo : public FieldTypeInfo {
struct Location {
Location(int LineNumber = 0, StringRef Filename = StringRef(),
bool IsFileInRootDir = false)
: LineNumber(LineNumber), Filename(Filename),
: LineNumber(LineNumber), Filename(std::move(Filename)),
IsFileInRootDir(IsFileInRootDir) {}
bool operator==(const Location &Other) const {
@ -266,14 +284,7 @@ struct NamespaceInfo : public Info {
void merge(NamespaceInfo &&I);
// Namespaces and Records are references because they will be properly
// documented in their own info, while the entirety of Functions and Enums are
// included here because they should not have separate documentation from
// their scope.
std::vector<Reference> ChildNamespaces;
std::vector<Reference> ChildRecords;
std::vector<FunctionInfo> ChildFunctions;
std::vector<EnumInfo> ChildEnums;
ScopeChildren Children;
};
// Info for symbols.
@ -338,12 +349,23 @@ struct RecordInfo : public SymbolInfo {
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;
ScopeChildren Children;
};
// Info for typedef and using statements.
struct TypedefInfo : public SymbolInfo {
TypedefInfo(SymbolID USR = SymbolID())
: SymbolInfo(InfoType::IT_typedef, USR) {}
void merge(TypedefInfo &&I);
TypeInfo Underlying;
// Inidicates if this is a new C++ "using"-style typedef:
// using MyVector = std::vector<int>
// False means it's a C-style typedef:
// typedef std::vector<int> MyVector;
bool IsUsing;
};
struct BaseRecordInfo : public RecordInfo {

View File

@ -273,6 +273,75 @@ static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
}
// The InsertChild functions insert the given info into the given scope using
// the method appropriate for that type. Some types are moved into the
// appropriate vector, while other types have Reference objects generated to
// refer to them.
//
// See MakeAndInsertIntoParent().
static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
getInfoRelativePath(Info.Namespace));
}
static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
getInfoRelativePath(Info.Namespace));
}
static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
Scope.Enums.push_back(std::move(Info));
}
static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
Scope.Functions.push_back(std::move(Info));
}
static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
Scope.Typedefs.push_back(std::move(Info));
}
// Creates a parent of the correct type for the given child and inserts it into
// that parent.
//
// This is complicated by the fact that namespaces and records are inserted by
// reference (constructing a "Reference" object with that namespace/record's
// info), while everything else is inserted by moving it directly into the child
// vectors.
//
// For namespaces and records, explicitly specify a const& template parameter
// when invoking this function:
// MakeAndInsertIntoParent<const Record&>(...);
// Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
// parameter. Since each variant is used once, it's not worth having a more
// elaborate system to automatically deduce this information.
template <typename ChildType>
std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {
if (Child.Namespace.empty()) {
// Insert into unnamed parent namespace.
auto ParentNS = std::make_unique<NamespaceInfo>();
InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
return ParentNS;
}
switch (Child.Namespace[0].RefType) {
case InfoType::IT_namespace: {
auto ParentNS = std::make_unique<NamespaceInfo>();
ParentNS->USR = Child.Namespace[0].USR;
InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
return ParentNS;
}
case InfoType::IT_record: {
auto ParentRec = std::make_unique<RecordInfo>();
ParentRec->USR = Child.Namespace[0].USR;
InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
return ParentRec;
}
default:
llvm_unreachable("Invalid reference type for parent namespace");
}
}
// There are two uses for this function.
// 1) Getting the resulting mode of inheritance of a record.
// Example: class A {}; class B : private A {}; class C : public B {};
@ -376,8 +445,8 @@ template <typename T>
static void
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
const T *D, bool &IsInAnonymousNamespace) {
const auto *DC = cast<DeclContext>(D);
while ((DC = DC->getParent())) {
const DeclContext *DC = D->getDeclContext();
do {
if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
std::string Namespace;
if (N->isAnonymousNamespace()) {
@ -396,7 +465,7 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
else if (const auto *N = dyn_cast<EnumDecl>(DC))
Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
InfoType::IT_enum);
}
} while ((DC = DC->getParent()));
// The global namespace should be added to the list of namespaces if the decl
// corresponds to a Record and if it doesn't have any namespace (because this
// means it's in the global namespace). Also if its outermost namespace is a
@ -501,7 +570,7 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
IsInAnonymousNamespace);
FI.Access =
getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
BI.ChildFunctions.emplace_back(std::move(FI));
BI.Children.Functions.emplace_back(std::move(FI));
}
I.Bases.emplace_back(std::move(BI));
// Call this function recursively to get the inherited classes of
@ -530,14 +599,9 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
if (I->Namespace.empty() && I->USR == SymbolID())
return {std::unique_ptr<Info>{std::move(I)}, nullptr};
auto ParentI = std::make_unique<NamespaceInfo>();
ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR;
ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace,
getInfoRelativePath(I->Namespace));
if (I->Namespace.empty())
ParentI->Path = getInfoRelativePath(ParentI->Namespace);
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(ParentI)}};
// Namespaces are inserted into the parent by reference, so we need to return
// both the parent and the record itself.
return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
@ -563,26 +627,10 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
}
I->Path = getInfoRelativePath(I->Namespace);
switch (I->Namespace[0].RefType) {
case InfoType::IT_namespace: {
auto ParentI = std::make_unique<NamespaceInfo>();
ParentI->USR = I->Namespace[0].USR;
ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
getInfoRelativePath(I->Namespace));
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(ParentI)}};
}
case InfoType::IT_record: {
auto ParentI = std::make_unique<RecordInfo>();
ParentI->USR = I->Namespace[0].USR;
ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
getInfoRelativePath(I->Namespace));
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(ParentI)}};
}
default:
llvm_unreachable("Invalid reference type for parent namespace");
}
// Records are inserted into the parent by reference, so we need to return
// both the parent and the record itself.
auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
return {std::move(I), std::move(Parent)};
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
@ -596,17 +644,8 @@ emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
// Wrap in enclosing scope
auto ParentI = std::make_unique<NamespaceInfo>();
if (!Func.Namespace.empty())
ParentI->USR = Func.Namespace[0].USR;
else
ParentI->USR = SymbolID();
if (Func.Namespace.empty())
ParentI->Path = getInfoRelativePath(ParentI->Namespace);
ParentI->ChildFunctions.emplace_back(std::move(Func));
// Info is wrapped in its parent scope so it's returned in the second position
return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
// Info is wrapped in its parent scope so is returned in the second position.
return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
@ -633,12 +672,52 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record};
Func.Access = D->getAccess();
// Wrap in enclosing scope
auto ParentI = std::make_unique<RecordInfo>();
ParentI->USR = ParentUSR;
ParentI->ChildFunctions.emplace_back(std::move(Func));
// Info is wrapped in its parent scope so it's returned in the second position
return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
// Info is wrapped in its parent scope so is returned in the second position.
return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly) {
TypedefInfo Info;
bool IsInAnonymousNamespace = false;
populateInfo(Info, D, FC, IsInAnonymousNamespace);
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
if (Info.Underlying.Type.Name.empty()) {
// Typedef for an unnamed type. This is like "typedef struct { } Foo;"
// The record serializer explicitly checks for this syntax and constructs
// a record with that name, so we don't want to emit a duplicate here.
return {};
}
Info.IsUsing = false;
// Info is wrapped in its parent scope so is returned in the second position.
return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
}
// A type alias is a C++ "using" declaration for a type. It gets mapped to a
// TypedefInfo with the IsUsing flag set.
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly) {
TypedefInfo Info;
bool IsInAnonymousNamespace = false;
populateInfo(Info, D, FC, IsInAnonymousNamespace);
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
return {};
Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
Info.IsUsing = true;
// Info is wrapped in its parent scope so is returned in the second position.
return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
}
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
@ -656,38 +735,8 @@ emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
Enum.BaseType = TypeInfo(D->getIntegerType().getAsString());
parseEnumerators(Enum, D);
// Put in global namespace
if (Enum.Namespace.empty()) {
auto ParentI = std::make_unique<NamespaceInfo>();
ParentI->USR = SymbolID();
ParentI->ChildEnums.emplace_back(std::move(Enum));
ParentI->Path = getInfoRelativePath(ParentI->Namespace);
// Info is wrapped in its parent scope so it's returned in the second
// position
return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
}
// Wrap in enclosing scope
switch (Enum.Namespace[0].RefType) {
case InfoType::IT_namespace: {
auto ParentI = std::make_unique<NamespaceInfo>();
ParentI->USR = Enum.Namespace[0].USR;
ParentI->ChildEnums.emplace_back(std::move(Enum));
// Info is wrapped in its parent scope so it's returned in the second
// position
return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
}
case InfoType::IT_record: {
auto ParentI = std::make_unique<RecordInfo>();
ParentI->USR = Enum.Namespace[0].USR;
ParentI->ChildEnums.emplace_back(std::move(Enum));
// Info is wrapped in its parent scope so it's returned in the second
// position
return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
}
default:
llvm_unreachable("Invalid reference type for parent namespace");
}
// Info is wrapped in its parent scope so is returned in the second position.
return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
}
} // namespace serialize

View File

@ -39,19 +39,31 @@ namespace serialize {
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly);
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
StringRef File, bool IsFileInRootDir, bool PublicOnly);
// Function to hash a given USR value for storage.
// As USRs (Unified Symbol Resolution) could be large, especially for functions
// with long type arguments, we use 160-bits SHA1(USR) values to

View File

@ -23,6 +23,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(EnumValueInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(TypedefInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<CommentInfo>)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>)
@ -137,9 +138,10 @@ static void RecordInfoMapping(IO &IO, RecordInfo &I) {
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);
IO.mapOptional("ChildRecords", I.Children.Records, std::vector<Reference>());
IO.mapOptional("ChildFunctions", I.Children.Functions);
IO.mapOptional("ChildEnums", I.Children.Enums);
IO.mapOptional("ChildTypedefs", I.Children.Typedefs);
}
static void CommentInfoMapping(IO &IO, CommentInfo &I) {
@ -203,11 +205,13 @@ template <> struct MappingTraits<MemberTypeInfo> {
template <> struct MappingTraits<NamespaceInfo> {
static void mapping(IO &IO, NamespaceInfo &I) {
InfoMapping(IO, I);
IO.mapOptional("ChildNamespaces", I.ChildNamespaces,
IO.mapOptional("ChildNamespaces", I.Children.Namespaces,
std::vector<Reference>());
IO.mapOptional("ChildRecords", I.ChildRecords, std::vector<Reference>());
IO.mapOptional("ChildFunctions", I.ChildFunctions);
IO.mapOptional("ChildEnums", I.ChildEnums);
IO.mapOptional("ChildRecords", I.Children.Records,
std::vector<Reference>());
IO.mapOptional("ChildFunctions", I.Children.Functions);
IO.mapOptional("ChildEnums", I.Children.Enums);
IO.mapOptional("ChildTypedefs", I.Children.Typedefs);
}
};
@ -244,6 +248,14 @@ template <> struct MappingTraits<EnumInfo> {
}
};
template <> struct MappingTraits<TypedefInfo> {
static void mapping(IO &IO, TypedefInfo &I) {
SymbolInfoMapping(IO, I);
IO.mapOptional("Underlying", I.Underlying.Type);
IO.mapOptional("IsUsing", I.IsUsing, false);
}
};
template <> struct MappingTraits<FunctionInfo> {
static void mapping(IO &IO, FunctionInfo &I) {
SymbolInfoMapping(IO, I);
@ -302,6 +314,9 @@ llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
case InfoType::IT_function:
InfoYAML << *static_cast<clang::doc::FunctionInfo *>(I);
break;
case InfoType::IT_typedef:
InfoYAML << *static_cast<clang::doc::TypedefInfo *>(I);
break;
case InfoType::IT_default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unexpected InfoType");

View File

@ -35,6 +35,8 @@ std::string writeInfo(Info *I) {
return writeInfo(*static_cast<EnumInfo *>(I));
case InfoType::IT_function:
return writeInfo(*static_cast<FunctionInfo *>(I));
case InfoType::IT_typedef:
return writeInfo(*static_cast<TypedefInfo *>(I));
default:
return "";
}
@ -57,11 +59,11 @@ TEST(BitcodeTest, emitNamespaceInfoBitcode) {
I.Name = "r";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
InfoType::IT_namespace);
I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.ChildFunctions.emplace_back();
I.ChildEnums.emplace_back();
I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
InfoType::IT_namespace);
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.Children.Functions.emplace_back();
I.Children.Enums.emplace_back();
std::string WriteResult = writeInfo(&I);
EXPECT_TRUE(WriteResult.size() > 0);
@ -83,7 +85,7 @@ TEST(BitcodeTest, emitRecordInfoBitcode) {
I.IsTypeDef = true;
I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
AccessSpecifier::AS_public, true);
I.Bases.back().ChildFunctions.emplace_back();
I.Bases.back().Children.Functions.emplace_back();
I.Bases.back().Members.emplace_back(TypeInfo("int"), "X",
AccessSpecifier::AS_private);
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
@ -101,9 +103,9 @@ TEST(BitcodeTest, emitRecordInfoBitcode) {
Brief->Children.back()->Text = "Value of the thing.";
I.Bases.back().Members.back().Description.emplace_back(std::move(TopComment));
I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.ChildFunctions.emplace_back();
I.ChildEnums.emplace_back();
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.Children.Functions.emplace_back();
I.Children.Enums.emplace_back();
std::string WriteResult = writeInfo(&I);
EXPECT_TRUE(WriteResult.size() > 0);
@ -172,6 +174,22 @@ TEST(BitcodeTest, emitEnumInfoBitcode) {
CheckEnumInfo(&I, InfoAsEnum(ReadResults[0].get()));
}
TEST(BitcodeTest, emitTypedefInfoBitcode) {
TypedefInfo I;
I.Name = "MyInt";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
I.Underlying = TypeInfo("unsigned");
I.IsUsing = true;
std::string WriteResult = writeInfo(&I);
EXPECT_TRUE(WriteResult.size() > 0);
std::vector<std::unique_ptr<Info>> ReadResults = readInfo(WriteResult, 1);
CheckTypedefInfo(&I, InfoAsTypedef(ReadResults[0].get()));
}
TEST(SerializeTest, emitInfoWithCommentBitcode) {
FunctionInfo F;
F.Name = "F";

View File

@ -34,6 +34,11 @@ EnumInfo *InfoAsEnum(Info *I) {
return static_cast<EnumInfo *>(I);
}
TypedefInfo *InfoAsTypedef(Info *I) {
assert(I->IT == InfoType::IT_typedef);
return static_cast<TypedefInfo *>(I);
}
void CheckCommentInfo(const std::vector<CommentInfo> &Expected,
const std::vector<CommentInfo> &Actual);
void CheckCommentInfo(const std::vector<std::unique_ptr<CommentInfo>> &Expected,
@ -144,26 +149,35 @@ void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual) {
EXPECT_EQ(Expected->Members[Idx], Actual->Members[Idx]);
}
void CheckTypedefInfo(TypedefInfo *Expected, TypedefInfo *Actual) {
CheckSymbolInfo(Expected, Actual);
EXPECT_EQ(Expected->IsUsing, Actual->IsUsing);
CheckTypeInfo(&Expected->Underlying, &Actual->Underlying);
}
void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual) {
CheckBaseInfo(Expected, Actual);
ASSERT_EQ(Expected->ChildNamespaces.size(), Actual->ChildNamespaces.size());
for (size_t Idx = 0; Idx < Actual->ChildNamespaces.size(); ++Idx)
CheckReference(Expected->ChildNamespaces[Idx],
Actual->ChildNamespaces[Idx]);
ASSERT_EQ(Expected->Children.Namespaces.size(),
Actual->Children.Namespaces.size());
for (size_t Idx = 0; Idx < Actual->Children.Namespaces.size(); ++Idx)
CheckReference(Expected->Children.Namespaces[Idx],
Actual->Children.Namespaces[Idx]);
ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size());
for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx)
CheckReference(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]);
ASSERT_EQ(Expected->Children.Records.size(), Actual->Children.Records.size());
for (size_t Idx = 0; Idx < Actual->Children.Records.size(); ++Idx)
CheckReference(Expected->Children.Records[Idx],
Actual->Children.Records[Idx]);
ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size());
for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx)
CheckFunctionInfo(&Expected->ChildFunctions[Idx],
&Actual->ChildFunctions[Idx]);
ASSERT_EQ(Expected->Children.Functions.size(),
Actual->Children.Functions.size());
for (size_t Idx = 0; Idx < Actual->Children.Functions.size(); ++Idx)
CheckFunctionInfo(&Expected->Children.Functions[Idx],
&Actual->Children.Functions[Idx]);
ASSERT_EQ(Expected->ChildEnums.size(), Actual->ChildEnums.size());
for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx)
CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]);
ASSERT_EQ(Expected->Children.Enums.size(), Actual->Children.Enums.size());
for (size_t Idx = 0; Idx < Actual->Children.Enums.size(); ++Idx)
CheckEnumInfo(&Expected->Children.Enums[Idx], &Actual->Children.Enums[Idx]);
}
void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) {
@ -189,18 +203,20 @@ void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) {
for (size_t Idx = 0; Idx < Actual->Bases.size(); ++Idx)
CheckBaseRecordInfo(&Expected->Bases[Idx], &Actual->Bases[Idx]);
ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size());
for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx)
CheckReference(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]);
ASSERT_EQ(Expected->Children.Records.size(), Actual->Children.Records.size());
for (size_t Idx = 0; Idx < Actual->Children.Records.size(); ++Idx)
CheckReference(Expected->Children.Records[Idx],
Actual->Children.Records[Idx]);
ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size());
for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx)
CheckFunctionInfo(&Expected->ChildFunctions[Idx],
&Actual->ChildFunctions[Idx]);
ASSERT_EQ(Expected->Children.Functions.size(),
Actual->Children.Functions.size());
for (size_t Idx = 0; Idx < Actual->Children.Functions.size(); ++Idx)
CheckFunctionInfo(&Expected->Children.Functions[Idx],
&Actual->Children.Functions[Idx]);
ASSERT_EQ(Expected->ChildEnums.size(), Actual->ChildEnums.size());
for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx)
CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]);
ASSERT_EQ(Expected->Children.Enums.size(), Actual->Children.Enums.size());
for (size_t Idx = 0; Idx < Actual->Children.Enums.size(); ++Idx)
CheckEnumInfo(&Expected->Children.Enums[Idx], &Actual->Children.Enums[Idx]);
}
void CheckBaseRecordInfo(BaseRecordInfo *Expected, BaseRecordInfo *Actual) {

View File

@ -27,6 +27,7 @@ NamespaceInfo *InfoAsNamespace(Info *I);
RecordInfo *InfoAsRecord(Info *I);
FunctionInfo *InfoAsFunction(Info *I);
EnumInfo *InfoAsEnum(Info *I);
TypedefInfo *InfoAsTypedef(Info *I);
// Unlike the operator==, these functions explicitly does not check USRs, as
// that may change and it would be better to not rely on its implementation.
@ -41,6 +42,7 @@ void CheckBaseInfo(Info *Expected, Info *Actual);
void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual);
void CheckFunctionInfo(FunctionInfo *Expected, FunctionInfo *Actual);
void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual);
void CheckTypedefInfo(TypedefInfo *Expected, TypedefInfo *Actual);
void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual);
void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual);
void CheckBaseRecordInfo(BaseRecordInfo *Expected, BaseRecordInfo *Actual);

View File

@ -43,15 +43,15 @@ TEST(HTMLGeneratorTest, emitNamespaceHTML) {
I.Name = "Namespace";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
InfoType::IT_namespace, "Namespace");
I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"Namespace");
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Access = AccessSpecifier::AS_none;
I.ChildFunctions.back().Name = "OneFunction";
I.ChildEnums.emplace_back();
I.ChildEnums.back().Name = "OneEnum";
I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
InfoType::IT_namespace, "Namespace");
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"Namespace");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Access = AccessSpecifier::AS_none;
I.Children.Functions.back().Name = "OneFunction";
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "OneEnum";
auto G = getHTMLGenerator();
assert(G);
@ -158,12 +158,12 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, PathTo);
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"X/Y/Z/r");
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Name = "OneFunction";
I.ChildEnums.emplace_back();
I.ChildEnums.back().Name = "OneEnum";
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"X/Y/Z/r");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "OneEnum";
auto G = getHTMLGenerator();
assert(G);

View File

@ -26,14 +26,14 @@ TEST(MDGeneratorTest, emitNamespaceMD) {
I.Name = "Namespace";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
InfoType::IT_namespace);
I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Name = "OneFunction";
I.ChildFunctions.back().Access = AccessSpecifier::AS_none;
I.ChildEnums.emplace_back();
I.ChildEnums.back().Name = "OneEnum";
I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
InfoType::IT_namespace);
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Functions.back().Access = AccessSpecifier::AS_none;
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "OneEnum";
auto G = getMDGenerator();
assert(G);
@ -90,11 +90,11 @@ TEST(MDGeneratorTest, emitRecordMD) {
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Name = "OneFunction";
I.ChildEnums.emplace_back();
I.ChildEnums.back().Name = "OneEnum";
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "OneEnum";
auto G = getMDGenerator();
assert(G);

View File

@ -18,29 +18,29 @@ TEST(MergeTest, mergeNamespaceInfos) {
One.Name = "Namespace";
One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
One.ChildNamespaces.emplace_back(NonEmptySID, "ChildNamespace",
InfoType::IT_namespace);
One.ChildRecords.emplace_back(NonEmptySID, "ChildStruct",
InfoType::IT_record);
One.ChildFunctions.emplace_back();
One.ChildFunctions.back().Name = "OneFunction";
One.ChildFunctions.back().USR = NonEmptySID;
One.ChildEnums.emplace_back();
One.ChildEnums.back().Name = "OneEnum";
One.ChildEnums.back().USR = NonEmptySID;
One.Children.Namespaces.emplace_back(NonEmptySID, "ChildNamespace",
InfoType::IT_namespace);
One.Children.Records.emplace_back(NonEmptySID, "ChildStruct",
InfoType::IT_record);
One.Children.Functions.emplace_back();
One.Children.Functions.back().Name = "OneFunction";
One.Children.Functions.back().USR = NonEmptySID;
One.Children.Enums.emplace_back();
One.Children.Enums.back().Name = "OneEnum";
One.Children.Enums.back().USR = NonEmptySID;
NamespaceInfo Two;
Two.Name = "Namespace";
Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
Two.ChildNamespaces.emplace_back(EmptySID, "OtherChildNamespace",
InfoType::IT_namespace);
Two.ChildRecords.emplace_back(EmptySID, "OtherChildStruct",
InfoType::IT_record);
Two.ChildFunctions.emplace_back();
Two.ChildFunctions.back().Name = "TwoFunction";
Two.ChildEnums.emplace_back();
Two.ChildEnums.back().Name = "TwoEnum";
Two.Children.Namespaces.emplace_back(EmptySID, "OtherChildNamespace",
InfoType::IT_namespace);
Two.Children.Records.emplace_back(EmptySID, "OtherChildStruct",
InfoType::IT_record);
Two.Children.Functions.emplace_back();
Two.Children.Functions.back().Name = "TwoFunction";
Two.Children.Enums.emplace_back();
Two.Children.Enums.back().Name = "TwoEnum";
std::vector<std::unique_ptr<Info>> Infos;
Infos.emplace_back(std::make_unique<NamespaceInfo>(std::move(One)));
@ -50,24 +50,24 @@ TEST(MergeTest, mergeNamespaceInfos) {
Expected->Name = "Namespace";
Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
Expected->ChildNamespaces.emplace_back(NonEmptySID, "ChildNamespace",
InfoType::IT_namespace);
Expected->ChildRecords.emplace_back(NonEmptySID, "ChildStruct",
InfoType::IT_record);
Expected->ChildNamespaces.emplace_back(EmptySID, "OtherChildNamespace",
InfoType::IT_namespace);
Expected->ChildRecords.emplace_back(EmptySID, "OtherChildStruct",
InfoType::IT_record);
Expected->ChildFunctions.emplace_back();
Expected->ChildFunctions.back().Name = "OneFunction";
Expected->ChildFunctions.back().USR = NonEmptySID;
Expected->ChildFunctions.emplace_back();
Expected->ChildFunctions.back().Name = "TwoFunction";
Expected->ChildEnums.emplace_back();
Expected->ChildEnums.back().Name = "OneEnum";
Expected->ChildEnums.back().USR = NonEmptySID;
Expected->ChildEnums.emplace_back();
Expected->ChildEnums.back().Name = "TwoEnum";
Expected->Children.Namespaces.emplace_back(NonEmptySID, "ChildNamespace",
InfoType::IT_namespace);
Expected->Children.Records.emplace_back(NonEmptySID, "ChildStruct",
InfoType::IT_record);
Expected->Children.Namespaces.emplace_back(EmptySID, "OtherChildNamespace",
InfoType::IT_namespace);
Expected->Children.Records.emplace_back(EmptySID, "OtherChildStruct",
InfoType::IT_record);
Expected->Children.Functions.emplace_back();
Expected->Children.Functions.back().Name = "OneFunction";
Expected->Children.Functions.back().USR = NonEmptySID;
Expected->Children.Functions.emplace_back();
Expected->Children.Functions.back().Name = "TwoFunction";
Expected->Children.Enums.emplace_back();
Expected->Children.Enums.back().Name = "OneEnum";
Expected->Children.Enums.back().USR = NonEmptySID;
Expected->Children.Enums.emplace_back();
Expected->Children.Enums.back().Name = "TwoEnum";
auto Actual = mergeInfos(Infos);
assert(Actual);
@ -90,14 +90,14 @@ TEST(MergeTest, mergeRecordInfos) {
One.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
AccessSpecifier::AS_protected, true);
One.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
InfoType::IT_record);
One.ChildFunctions.emplace_back();
One.ChildFunctions.back().Name = "OneFunction";
One.ChildFunctions.back().USR = NonEmptySID;
One.ChildEnums.emplace_back();
One.ChildEnums.back().Name = "OneEnum";
One.ChildEnums.back().USR = NonEmptySID;
One.Children.Records.emplace_back(NonEmptySID, "SharedChildStruct",
InfoType::IT_record);
One.Children.Functions.emplace_back();
One.Children.Functions.back().Name = "OneFunction";
One.Children.Functions.back().USR = NonEmptySID;
One.Children.Enums.emplace_back();
One.Children.Enums.back().Name = "OneEnum";
One.Children.Enums.back().USR = NonEmptySID;
RecordInfo Two;
Two.Name = "r";
@ -107,12 +107,12 @@ TEST(MergeTest, mergeRecordInfos) {
Two.TagType = TagTypeKind::TTK_Class;
Two.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
InfoType::IT_record, "path");
Two.ChildFunctions.emplace_back();
Two.ChildFunctions.back().Name = "TwoFunction";
Two.ChildEnums.emplace_back();
Two.ChildEnums.back().Name = "TwoEnum";
Two.Children.Records.emplace_back(NonEmptySID, "SharedChildStruct",
InfoType::IT_record, "path");
Two.Children.Functions.emplace_back();
Two.Children.Functions.back().Name = "TwoFunction";
Two.Children.Enums.emplace_back();
Two.Children.Enums.back().Name = "TwoEnum";
std::vector<std::unique_ptr<Info>> Infos;
Infos.emplace_back(std::make_unique<RecordInfo>(std::move(One)));
@ -134,18 +134,18 @@ TEST(MergeTest, mergeRecordInfos) {
Expected->Bases.emplace_back(EmptySID, "F", "path/to/F", true,
AccessSpecifier::AS_protected, true);
Expected->ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
InfoType::IT_record, "path");
Expected->ChildFunctions.emplace_back();
Expected->ChildFunctions.back().Name = "OneFunction";
Expected->ChildFunctions.back().USR = NonEmptySID;
Expected->ChildFunctions.emplace_back();
Expected->ChildFunctions.back().Name = "TwoFunction";
Expected->ChildEnums.emplace_back();
Expected->ChildEnums.back().Name = "OneEnum";
Expected->ChildEnums.back().USR = NonEmptySID;
Expected->ChildEnums.emplace_back();
Expected->ChildEnums.back().Name = "TwoEnum";
Expected->Children.Records.emplace_back(NonEmptySID, "SharedChildStruct",
InfoType::IT_record, "path");
Expected->Children.Functions.emplace_back();
Expected->Children.Functions.back().Name = "OneFunction";
Expected->Children.Functions.back().USR = NonEmptySID;
Expected->Children.Functions.emplace_back();
Expected->Children.Functions.back().Name = "TwoFunction";
Expected->Children.Enums.emplace_back();
Expected->Children.Enums.back().Name = "OneEnum";
Expected->Children.Enums.back().USR = NonEmptySID;
Expected->Children.Enums.emplace_back();
Expected->Children.Enums.back().Name = "TwoEnum";
auto Actual = mergeInfos(Infos);
assert(Actual);

View File

@ -59,6 +59,10 @@ public:
bool VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); }
bool VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); }
bool VisitTypedefDecl(const TypedefDecl *D) { return mapDecl(D); }
bool VisitTypeAliasDecl(const TypeAliasDecl *D) { return mapDecl(D); }
};
void ExtractInfosFromCode(StringRef Code, size_t NumExpectedInfos, bool Public,
@ -124,7 +128,7 @@ TEST(SerializeTest, emitNamespaceInfo) {
F.Namespace.emplace_back(EmptySID, "B", InfoType::IT_namespace);
F.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
F.Access = AccessSpecifier::AS_none;
ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F));
ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
}
@ -183,7 +187,7 @@ typedef struct {} G;)raw",
InfoType::IT_namespace);
EConstructor.Access = AccessSpecifier::AS_public;
EConstructor.IsMethod = true;
ExpectedRecordWithEConstructor.ChildFunctions.emplace_back(
ExpectedRecordWithEConstructor.Children.Functions.emplace_back(
std::move(EConstructor));
CheckRecordInfo(&ExpectedRecordWithEConstructor, RecordWithEConstructor);
@ -199,7 +203,7 @@ typedef struct {} G;)raw",
InfoType::IT_namespace);
Method.Access = AccessSpecifier::AS_protected;
Method.IsMethod = true;
ExpectedRecordWithMethod.ChildFunctions.emplace_back(std::move(Method));
ExpectedRecordWithMethod.Children.Functions.emplace_back(std::move(Method));
CheckRecordInfo(&ExpectedRecordWithMethod, RecordWithMethod);
RecordInfo *F = InfoAsRecord(Infos[4].get());
@ -222,7 +226,7 @@ typedef struct {} G;)raw",
InfoType::IT_namespace);
TemplateMethod.Access = AccessSpecifier::AS_public;
TemplateMethod.IsMethod = true;
ExpectedRecordWithTemplateMethod.ChildFunctions.emplace_back(
ExpectedRecordWithTemplateMethod.Children.Functions.emplace_back(
std::move(TemplateMethod));
CheckRecordInfo(&ExpectedRecordWithTemplateMethod, RecordWithTemplateMethod);
@ -241,7 +245,7 @@ typedef struct {} G;)raw",
InfoType::IT_namespace);
SpecializedTemplateMethod.Access = AccessSpecifier::AS_public;
SpecializedTemplateMethod.IsMethod = true;
ExpectedTemplatedRecord.ChildFunctions.emplace_back(
ExpectedTemplatedRecord.Children.Functions.emplace_back(
std::move(SpecializedTemplateMethod));
CheckRecordInfo(&ExpectedTemplatedRecord, TemplatedRecord);
@ -268,7 +272,7 @@ TEST(SerializeTest, emitEnumInfo) {
E.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
E.Members.emplace_back("X", "0");
E.Members.emplace_back("Y", "1");
ExpectedNamespaceWithEnum.ChildEnums.emplace_back(std::move(E));
ExpectedNamespaceWithEnum.Children.Enums.emplace_back(std::move(E));
CheckNamespaceInfo(&ExpectedNamespaceWithEnum, NamespaceWithEnum);
NamespaceInfo *NamespaceWithScopedEnum = InfoAsNamespace(Infos[1].get());
@ -279,7 +283,7 @@ TEST(SerializeTest, emitEnumInfo) {
G.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
G.Members.emplace_back("A", "0");
G.Members.emplace_back("B", "1");
ExpectedNamespaceWithScopedEnum.ChildEnums.emplace_back(std::move(G));
ExpectedNamespaceWithScopedEnum.Children.Enums.emplace_back(std::move(G));
CheckNamespaceInfo(&ExpectedNamespaceWithScopedEnum, NamespaceWithScopedEnum);
}
@ -352,7 +356,7 @@ TEST(SerializeTest, emitPublicFunctionInternalInfo) {
F.ReturnType = TypeInfo("int");
F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
F.Access = AccessSpecifier::AS_none;
ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F));
ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
}
@ -368,7 +372,7 @@ TEST(SerializeTest, emitInlinedFunctionInfo) {
F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
F.Params.emplace_back(TypeInfo("int"), "I");
F.Access = AccessSpecifier::AS_none;
ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F));
ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
}
@ -422,7 +426,8 @@ class J : public I<int> {} ;)raw",
InfoType::IT_namespace);
FunctionSet.Access = AccessSpecifier::AS_protected;
FunctionSet.IsMethod = true;
ExpectedE.Bases.back().ChildFunctions.emplace_back(std::move(FunctionSet));
ExpectedE.Bases.back().Children.Functions.emplace_back(
std::move(FunctionSet));
ExpectedE.Bases.emplace_back(EmptySID, /*Name=*/"G",
/*Path=*/"GlobalNamespace", true,
AccessSpecifier::AS_private, true);
@ -435,7 +440,8 @@ class J : public I<int> {} ;)raw",
InfoType::IT_namespace);
FunctionGet.Access = AccessSpecifier::AS_private;
FunctionGet.IsMethod = true;
ExpectedE.Bases.back().ChildFunctions.emplace_back(std::move(FunctionGet));
ExpectedE.Bases.back().Children.Functions.emplace_back(
std::move(FunctionGet));
ExpectedE.Bases.back().Members.emplace_back(TypeInfo("int"), "I",
AccessSpecifier::AS_private);
ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
@ -468,7 +474,8 @@ class J : public I<int> {} ;)raw",
InfoType::IT_namespace);
FunctionSetNew.Access = AccessSpecifier::AS_private;
FunctionSetNew.IsMethod = true;
ExpectedH.Bases.back().ChildFunctions.emplace_back(std::move(FunctionSetNew));
ExpectedH.Bases.back().Children.Functions.emplace_back(
std::move(FunctionSetNew));
ExpectedH.Bases.emplace_back(EmptySID, /*Name=*/"G",
/*Path=*/"GlobalNamespace", true,
AccessSpecifier::AS_private, false);
@ -481,7 +488,8 @@ class J : public I<int> {} ;)raw",
InfoType::IT_namespace);
FunctionGetNew.Access = AccessSpecifier::AS_private;
FunctionGetNew.IsMethod = true;
ExpectedH.Bases.back().ChildFunctions.emplace_back(std::move(FunctionGetNew));
ExpectedH.Bases.back().Children.Functions.emplace_back(
std::move(FunctionGetNew));
ExpectedH.Bases.back().Members.emplace_back(TypeInfo("int"), "I",
AccessSpecifier::AS_private);
CheckRecordInfo(&ExpectedH, H);
@ -528,7 +536,7 @@ export double exportedModuleFunction(double y);)raw",
F.Params.emplace_back(TypeInfo("double"), "d");
F.Params.back().DefaultValue = "3.2 - 1.0";
F.Access = AccessSpecifier::AS_none;
ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F));
ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
NamespaceInfo *BWithExportedFunction = InfoAsNamespace(Infos[1].get());
@ -540,7 +548,7 @@ export double exportedModuleFunction(double y);)raw",
ExportedF.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"});
ExportedF.Params.emplace_back(TypeInfo("double"), "y");
ExportedF.Access = AccessSpecifier::AS_none;
ExpectedBWithExportedFunction.ChildFunctions.emplace_back(
ExpectedBWithExportedFunction.Children.Functions.emplace_back(
std::move(ExportedF));
CheckNamespaceInfo(&ExpectedBWithExportedFunction, BWithExportedFunction);
}
@ -553,22 +561,22 @@ TEST(SerializeTest, emitChildRecords) {
NamespaceInfo *ParentA = InfoAsNamespace(Infos[1].get());
NamespaceInfo ExpectedParentA(EmptySID);
ExpectedParentA.ChildRecords.emplace_back(EmptySID, "A", InfoType::IT_record,
"GlobalNamespace");
ExpectedParentA.Children.Records.emplace_back(
EmptySID, "A", InfoType::IT_record, "GlobalNamespace");
CheckNamespaceInfo(&ExpectedParentA, ParentA);
RecordInfo *ParentB = InfoAsRecord(Infos[3].get());
RecordInfo ExpectedParentB(EmptySID);
llvm::SmallString<128> ExpectedParentBPath("GlobalNamespace/A");
llvm::sys::path::native(ExpectedParentBPath);
ExpectedParentB.ChildRecords.emplace_back(EmptySID, "B", InfoType::IT_record,
ExpectedParentBPath);
ExpectedParentB.Children.Records.emplace_back(
EmptySID, "B", InfoType::IT_record, ExpectedParentBPath);
CheckRecordInfo(&ExpectedParentB, ParentB);
NamespaceInfo *ParentC = InfoAsNamespace(Infos[7].get());
NamespaceInfo ExpectedParentC(EmptySID);
ExpectedParentC.ChildRecords.emplace_back(EmptySID, "C", InfoType::IT_record,
"@nonymous_namespace");
ExpectedParentC.Children.Records.emplace_back(
EmptySID, "C", InfoType::IT_record, "@nonymous_namespace");
CheckNamespaceInfo(&ExpectedParentC, ParentC);
}
@ -580,16 +588,43 @@ TEST(SerializeTest, emitChildNamespaces) {
NamespaceInfo *ParentA = InfoAsNamespace(Infos[1].get());
NamespaceInfo ExpectedParentA(EmptySID);
ExpectedParentA.ChildNamespaces.emplace_back(EmptySID, "A",
InfoType::IT_namespace);
ExpectedParentA.Children.Namespaces.emplace_back(EmptySID, "A",
InfoType::IT_namespace);
CheckNamespaceInfo(&ExpectedParentA, ParentA);
NamespaceInfo *ParentB = InfoAsNamespace(Infos[3].get());
NamespaceInfo ExpectedParentB(EmptySID);
ExpectedParentB.ChildNamespaces.emplace_back(EmptySID, "B",
InfoType::IT_namespace, "A");
ExpectedParentB.Children.Namespaces.emplace_back(EmptySID, "B",
InfoType::IT_namespace, "A");
CheckNamespaceInfo(&ExpectedParentB, ParentB);
}
TEST(SerializeTests, emitTypedefs) {
EmittedInfoList Infos;
ExtractInfosFromCode("typedef int MyInt; using MyDouble = double;", 2,
/*Public=*/false, Infos);
// First info will be the global namespace with the typedef in it.
NamespaceInfo *GlobalNS1 = InfoAsNamespace(Infos[0].get());
ASSERT_EQ(1u, GlobalNS1->Children.Typedefs.size());
const TypedefInfo &FirstTD = GlobalNS1->Children.Typedefs[0];
EXPECT_EQ("MyInt", FirstTD.Name);
EXPECT_FALSE(FirstTD.IsUsing);
EXPECT_EQ("int", FirstTD.Underlying.Type.Name);
// The second will be another global namespace with the using in it (the
// global namespace is duplicated because the items haven't been merged at the
// serialization phase of processing).
NamespaceInfo *GlobalNS2 = InfoAsNamespace(Infos[1].get());
ASSERT_EQ(1u, GlobalNS2->Children.Typedefs.size());
// Second is the "using" typedef.
const TypedefInfo &SecondTD = GlobalNS2->Children.Typedefs[0];
EXPECT_EQ("MyDouble", SecondTD.Name);
EXPECT_TRUE(SecondTD.IsUsing);
EXPECT_EQ("double", SecondTD.Underlying.Type.Name);
}
} // namespace doc
} // end namespace clang

View File

@ -28,15 +28,16 @@ TEST(YAMLGeneratorTest, emitNamespaceYAML) {
I.Path = "path/to/A";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
InfoType::IT_namespace, "path/to/A/Namespace");
I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"path/to/A/Namespace");
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Name = "OneFunction";
I.ChildFunctions.back().Access = AccessSpecifier::AS_none;
I.ChildEnums.emplace_back();
I.ChildEnums.back().Name = "OneEnum";
I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
InfoType::IT_namespace,
"path/to/A/Namespace");
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"path/to/A/Namespace");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Functions.back().Access = AccessSpecifier::AS_none;
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "OneEnum";
auto G = getYAMLGenerator();
assert(G);
@ -100,8 +101,8 @@ TEST(YAMLGeneratorTest, emitRecordYAML) {
I.TagType = TagTypeKind::TTK_Class;
I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
AccessSpecifier::AS_public, true);
I.Bases.back().ChildFunctions.emplace_back();
I.Bases.back().ChildFunctions.back().Name = "InheritedFunctionOne";
I.Bases.back().Children.Functions.emplace_back();
I.Bases.back().Children.Functions.back().Name = "InheritedFunctionOne";
I.Bases.back().Members.emplace_back(TypeInfo("int", "path/to/int"), "N",
AccessSpecifier::AS_private);
// F is in the global namespace
@ -109,12 +110,12 @@ TEST(YAMLGeneratorTest, emitRecordYAML) {
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
"path/to/G");
I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"path/to/A/r");
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Name = "OneFunction";
I.ChildEnums.emplace_back();
I.ChildEnums.back().Name = "OneEnum";
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"path/to/A/r");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "OneEnum";
auto G = getYAMLGenerator();
assert(G);
@ -330,6 +331,30 @@ Members:
EXPECT_EQ(Expected, Actual.str());
}
TEST(YAMLGeneratorTest, enumTypedefYAML) {
TypedefInfo I;
I.Name = "MyUsing";
I.Underlying = TypeInfo("int");
I.IsUsing = true;
auto G = getYAMLGenerator();
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
assert(!Err);
std::string Expected =
R"raw(---
USR: '0000000000000000000000000000000000000000'
Name: 'MyUsing'
Underlying:
Name: 'int'
IsUsing: true
...
)raw";
EXPECT_EQ(Expected, Actual.str());
}
TEST(YAMLGeneratorTest, emitCommentYAML) {
FunctionInfo I;
I.Name = "f";