[clang-tools-extra] 21fb70c - [clang-doc] Add typedef/using information.

Brett Wilson via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 14 14:59:43 PDT 2022


Author: Brett Wilson
Date: 2022-10-14T14:59:29-07:00
New Revision: 21fb70c6ab3b25fe8f5f8384714a9a359b4b5a54

URL: https://github.com/llvm/llvm-project/commit/21fb70c6ab3b25fe8f5f8384714a9a359b4b5a54
DIFF: https://github.com/llvm/llvm-project/commit/21fb70c6ab3b25fe8f5f8384714a9a359b4b5a54.diff

LOG: [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).

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

Added: 
    

Modified: 
    clang-tools-extra/clang-doc/BitcodeReader.cpp
    clang-tools-extra/clang-doc/BitcodeWriter.cpp
    clang-tools-extra/clang-doc/BitcodeWriter.h
    clang-tools-extra/clang-doc/HTMLGenerator.cpp
    clang-tools-extra/clang-doc/MDGenerator.cpp
    clang-tools-extra/clang-doc/Mapper.cpp
    clang-tools-extra/clang-doc/Mapper.h
    clang-tools-extra/clang-doc/Representation.cpp
    clang-tools-extra/clang-doc/Representation.h
    clang-tools-extra/clang-doc/Serialize.cpp
    clang-tools-extra/clang-doc/Serialize.h
    clang-tools-extra/clang-doc/YAMLGenerator.cpp
    clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
    clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
    clang-tools-extra/unittests/clang-doc/ClangDocTest.h
    clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
    clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
    clang-tools-extra/unittests/clang-doc/MergeTest.cpp
    clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
    clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 027272691d03..8e1db35532a0 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();
   }
@@ -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)

diff  --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 194194aef3e2..7768f4b06519 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 d5aba22db9cf..5a2514a19b30 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 @@ class ClangDocBitcodeWriter {
   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 45f10e0f20dd..6f1d45551bc7 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 8957a4795a8f..0c5f16304598 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 16a52e843fcc..5264417748a1 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 b8cdb19ed971..cedde935ab74 100644
--- a/clang-tools-extra/clang-doc/Mapper.h
+++ b/clang-tools-extra/clang-doc/Mapper.h
@@ -39,6 +39,8 @@ class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
   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 1e76543525c4..27b83d67513d 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 39e872358c72..d41630670461 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 aeade31782ba..66a938d488e1 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 da1361e66fa4..4e203ca7891a 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 6406b817f1d4..fcca0a60a944 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 e6933a7237cb..db6569419d57 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 01816fb28ee6..6e28aff09f9d 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 08f4534fd92b..2e189caa4865 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 25ecb55627dc..d91225352c85 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 f2a7858f5cb7..be9ccee3da0a 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 2305bd2bdce3..51fddb97decf 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 bc45264a4b82..05b62451adbc 100644
--- a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
@@ -59,6 +59,10 @@ class ClangDocSerializeTestVisitor
   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 bf59c2cb7939..d6410f28282e 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 @@ Scoped:          true
   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";


        


More information about the cfe-commits mailing list