aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra')
-rw-r--r--clang-tools-extra/clang-doc/BitcodeReader.cpp79
-rw-r--r--clang-tools-extra/clang-doc/BitcodeWriter.cpp45
-rw-r--r--clang-tools-extra/clang-doc/BitcodeWriter.h8
-rw-r--r--clang-tools-extra/clang-doc/HTMLGenerator.cpp51
-rw-r--r--clang-tools-extra/clang-doc/MDGenerator.cpp39
-rw-r--r--clang-tools-extra/clang-doc/Mapper.cpp8
-rw-r--r--clang-tools-extra/clang-doc/Mapper.h2
-rw-r--r--clang-tools-extra/clang-doc/Representation.cpp42
-rw-r--r--clang-tools-extra/clang-doc/Representation.h58
-rw-r--r--clang-tools-extra/clang-doc/Serialize.cpp211
-rw-r--r--clang-tools-extra/clang-doc/Serialize.h12
-rw-r--r--clang-tools-extra/clang-doc/YAMLGenerator.cpp29
-rw-r--r--clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp36
-rw-r--r--clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp64
-rw-r--r--clang-tools-extra/unittests/clang-doc/ClangDocTest.h2
-rw-r--r--clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp30
-rw-r--r--clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp26
-rw-r--r--clang-tools-extra/unittests/clang-doc/MergeTest.cpp124
-rw-r--r--clang-tools-extra/unittests/clang-doc/SerializeTest.cpp85
-rw-r--r--clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp59
20 files changed, 672 insertions, 338 deletions
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 0272726..8e1db35 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -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();
}
@@ -234,6 +229,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) {
case ENUM_VALUE_NAME:
@@ -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)
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 194194a..7768f4b 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -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.Children.Enums)
emitBlock(C);
- for (const auto &C : I.ChildEnums)
+ 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;
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index d5aba22..5a2514a 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -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);
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index 45f10e0..6f1d4555 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -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,29 +803,37 @@ 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.Children.Functions, "Functions"));
+ if (!I.Children.Enums.empty())
InfoIndex.Children.emplace_back(
- genInfoIndexItem(I.ChildFunctions, "Functions"));
- if (!I.ChildEnums.empty())
- InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums"));
+ genInfoIndexItem(I.Children.Enums, "Enums"));
return Out;
}
+static std::vector<std::unique_ptr<TagNode>>
+genHTML(const TypedefInfo &I, const ClangDocContext &CDCtx,
+ std::string &InfoTitle) {
+ // TODO support typedefs in HTML.
+ return {};
+}
+
/// Generator for HTML documentation.
class HTMLGenerator : public Generator {
public:
@@ -858,6 +867,10 @@ llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
MainContentNodes =
genHTML(*static_cast<clang::doc::FunctionInfo *>(I), CDCtx, "");
break;
+ case InfoType::IT_typedef:
+ MainContentNodes =
+ genHTML(*static_cast<clang::doc::TypedefInfo *>(I), CDCtx, InfoTitle);
+ break;
case InfoType::IT_default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"unexpected info type");
@@ -882,6 +895,8 @@ static std::string getRefType(InfoType IT) {
return "function";
case InfoType::IT_enum:
return "enum";
+ case InfoType::IT_typedef:
+ return "typedef";
}
llvm_unreachable("Unknown InfoType");
}
diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 8957a47..0c5f163 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -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,26 +259,31 @@ 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);
}
}
+static void genMarkdown(const ClangDocContext &CDCtx, const TypedefInfo &I,
+ llvm::raw_ostream &OS) {
+ // TODO support typedefs in markdown.
+}
+
static void serializeReference(llvm::raw_fd_ostream &OS, Index &I, int Level) {
// Write out the heading level starting at ##
OS << "##" << std::string(Level, '#') << " ";
@@ -337,6 +342,9 @@ static llvm::Error genIndex(ClangDocContext &CDCtx) {
case InfoType::IT_function:
Type = "Function";
break;
+ case InfoType::IT_typedef:
+ Type = "Typedef";
+ break;
case InfoType::IT_default:
Type = "Other";
}
@@ -375,6 +383,9 @@ llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
case InfoType::IT_function:
genMarkdown(CDCtx, *static_cast<clang::doc::FunctionInfo *>(I), OS);
break;
+ case InfoType::IT_typedef:
+ genMarkdown(CDCtx, *static_cast<clang::doc::TypedefInfo *>(I), OS);
+ break;
case InfoType::IT_default:
return createStringError(llvm::inconvertibleErrorCode(),
"unexpected InfoType");
diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp
index 16a52e8..5264417 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -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);
diff --git a/clang-tools-extra/clang-doc/Mapper.h b/clang-tools-extra/clang-doc/Mapper.h
index b8cdb19..cedde93 100644
--- a/clang-tools-extra/clang-doc/Mapper.h
+++ b/clang-tools-extra/clang-doc/Mapper.h
@@ -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);
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index 1e76543..27b83d6 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -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)));
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 39e8723..d416306 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -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;
@@ -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 {
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index aeade31..66a938d 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -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
diff --git a/clang-tools-extra/clang-doc/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h
index da1361e..4e203ca 100644
--- a/clang-tools-extra/clang-doc/Serialize.h
+++ b/clang-tools-extra/clang-doc/Serialize.h
@@ -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
diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
index 6406b81..fcca0a6 100644
--- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
@@ -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");
diff --git a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
index e6933a7..db656941 100644
--- a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
@@ -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";
diff --git a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
index 01816fb..6e28aff 100644
--- a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
@@ -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) {
diff --git a/clang-tools-extra/unittests/clang-doc/ClangDocTest.h b/clang-tools-extra/unittests/clang-doc/ClangDocTest.h
index 08f4534..2e189ca 100644
--- a/clang-tools-extra/unittests/clang-doc/ClangDocTest.h
+++ b/clang-tools-extra/unittests/clang-doc/ClangDocTest.h
@@ -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);
diff --git a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
index 25ecb55..d912253 100644
--- a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -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);
diff --git a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
index f2a7858..be9ccee 100644
--- a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
@@ -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);
diff --git a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp
index 2305bd2..51fddb9 100644
--- a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp
@@ -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);
diff --git a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
index bc45264..05b6245 100644
--- a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
@@ -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
diff --git a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
index bf59c2c..d6410f2 100644
--- a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
@@ -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";