[clang-doc] Add typedef/using information.
authorBrett Wilson <brettw@gmail.com>
Fri, 14 Oct 2022 21:28:43 +0000 (14:28 -0700)
committerBrett Wilson <brettw@gmail.com>
Fri, 14 Oct 2022 21:59:29 +0000 (14:59 -0700)
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

20 files changed:
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

index 0272726..8e1db35 100644 (file)
@@ -24,12 +24,6 @@ llvm::Error decodeRecord(const Record &R, llvm::SmallVectorImpl<char> &Field,
   return llvm::Error::success();
 }
 
-llvm::Error decodeRecord(const Record &R, std::string &Field,
-                         llvm::StringRef Blob) {
-  Field.assign(Blob.begin(), Blob.end());
-  return llvm::Error::success();
-}
-
 llvm::Error decodeRecord(const Record &R, SymbolID &Field,
                          llvm::StringRef Blob) {
   if (R[0] != BitCodeConstants::USRHashSize)
@@ -104,6 +98,7 @@ llvm::Error decodeRecord(const Record &R, InfoType &Field,
   case InfoType::IT_function:
   case InfoType::IT_default:
   case InfoType::IT_enum:
+  case InfoType::IT_typedef:
     Field = IT;
     return llvm::Error::success();
   }
@@ -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)
index 194194a..7768f4b 100644 (file)
@@ -113,6 +113,7 @@ static const llvm::IndexedMap<llvm::StringRef, BlockIdToIndexFunctor>
           {BI_NAMESPACE_BLOCK_ID, "NamespaceBlock"},
           {BI_ENUM_BLOCK_ID, "EnumBlock"},
           {BI_ENUM_VALUE_BLOCK_ID, "EnumValueBlock"},
+          {BI_TYPEDEF_BLOCK_ID, "TypedefBlock"},
           {BI_TYPE_BLOCK_ID, "TypeBlock"},
           {BI_FIELD_TYPE_BLOCK_ID, "FieldTypeBlock"},
           {BI_MEMBER_TYPE_BLOCK_ID, "MemberTypeBlock"},
@@ -187,7 +188,11 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
           {REFERENCE_NAME, {"Name", &StringAbbrev}},
           {REFERENCE_TYPE, {"RefType", &IntAbbrev}},
           {REFERENCE_PATH, {"Path", &StringAbbrev}},
-          {REFERENCE_FIELD, {"Field", &IntAbbrev}}};
+          {REFERENCE_FIELD, {"Field", &IntAbbrev}},
+          {TYPEDEF_USR, {"USR", &SymbolIDAbbrev}},
+          {TYPEDEF_NAME, {"Name", &StringAbbrev}},
+          {TYPEDEF_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
+          {TYPEDEF_IS_USING, {"IsUsing", &BoolAbbrev}}};
       assert(Inits.size() == RecordIdCount);
       for (const auto &Init : Inits) {
         RecordIdNameMap[Init.first] = Init.second;
@@ -218,6 +223,9 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
         // Enum Value Block
         {BI_ENUM_VALUE_BLOCK_ID,
          {ENUM_VALUE_NAME, ENUM_VALUE_VALUE, ENUM_VALUE_EXPR}},
+        // Typedef Block
+        {BI_TYPEDEF_BLOCK_ID,
+         {TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DEFLOCATION, TYPEDEF_IS_USING}},
         // Namespace Block
         {BI_NAMESPACE_BLOCK_ID,
          {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
@@ -418,6 +426,18 @@ void ClangDocBitcodeWriter::emitBlock(const TypeInfo &T) {
   emitBlock(T.Type, FieldId::F_type);
 }
 
+void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) {
+  StreamSubBlockGuard Block(Stream, BI_TYPEDEF_BLOCK_ID);
+  emitRecord(T.USR, TYPEDEF_USR);
+  emitRecord(T.Name, TYPEDEF_NAME);
+  for (const auto &N : T.Namespace)
+    emitBlock(N, FieldId::F_namespace);
+  if (T.DefLoc)
+    emitRecord(*T.DefLoc, TYPEDEF_DEFLOCATION);
+  emitRecord(T.IsUsing, TYPEDEF_IS_USING);
+  emitBlock(T.Underlying);
+}
+
 void ClangDocBitcodeWriter::emitBlock(const FieldTypeInfo &T) {
   StreamSubBlockGuard Block(Stream, BI_FIELD_TYPE_BLOCK_ID);
   emitBlock(T.Type, FieldId::F_type);
@@ -465,13 +485,15 @@ void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) {
     emitBlock(N, FieldId::F_namespace);
   for (const auto &CI : I.Description)
     emitBlock(CI);
-  for (const auto &C : I.ChildNamespaces)
+  for (const auto &C : I.Children.Namespaces)
     emitBlock(C, FieldId::F_child_namespace);
-  for (const auto &C : I.ChildRecords)
+  for (const auto &C : I.Children.Records)
     emitBlock(C, FieldId::F_child_record);
-  for (const auto &C : I.ChildFunctions)
+  for (const auto &C : I.Children.Functions)
     emitBlock(C);
-  for (const auto &C : I.ChildEnums)
+  for (const auto &C : I.Children.Enums)
+    emitBlock(C);
+  for (const auto &C : I.Children.Typedefs)
     emitBlock(C);
 }
 
@@ -524,11 +546,13 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
     emitBlock(P, FieldId::F_vparent);
   for (const auto &PB : I.Bases)
     emitBlock(PB);
-  for (const auto &C : I.ChildRecords)
+  for (const auto &C : I.Children.Records)
     emitBlock(C, FieldId::F_child_record);
-  for (const auto &C : I.ChildFunctions)
+  for (const auto &C : I.Children.Functions)
+    emitBlock(C);
+  for (const auto &C : I.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;
index d5aba22..5a2514a 100644 (file)
@@ -64,6 +64,7 @@ enum BlockId {
   BI_FUNCTION_BLOCK_ID,
   BI_COMMENT_BLOCK_ID,
   BI_REFERENCE_BLOCK_ID,
+  BI_TYPEDEF_BLOCK_ID,
   BI_LAST,
   BI_FIRST = BI_VERSION_BLOCK_ID
 };
@@ -123,6 +124,10 @@ enum RecordId {
   REFERENCE_TYPE,
   REFERENCE_PATH,
   REFERENCE_FIELD,
+  TYPEDEF_USR,
+  TYPEDEF_NAME,
+  TYPEDEF_DEFLOCATION,
+  TYPEDEF_IS_USING,
   RI_LAST,
   RI_FIRST = VERSION
 };
@@ -160,8 +165,9 @@ public:
   void emitBlock(const EnumInfo &I);
   void emitBlock(const EnumValueInfo &I);
   void emitBlock(const TypeInfo &B);
+  void emitBlock(const TypedefInfo &B);
   void emitBlock(const FieldTypeInfo &B);
-  void emitBlock(const MemberTypeInfo &B);
+  void emitBlock(const MemberTypeInfo &T);
   void emitBlock(const CommentInfo &B);
   void emitBlock(const Reference &B, FieldId F);
 
index 45f10e0..6f1d455 100644 (file)
@@ -734,28 +734,29 @@ genHTML(const NamespaceInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
   llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
 
   std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
-      genReferencesBlock(I.ChildNamespaces, "Namespaces", BasePath);
+      genReferencesBlock(I.Children.Namespaces, "Namespaces", BasePath);
   AppendVector(std::move(ChildNamespaces), Out);
   std::vector<std::unique_ptr<TagNode>> ChildRecords =
-      genReferencesBlock(I.ChildRecords, "Records", BasePath);
+      genReferencesBlock(I.Children.Records, "Records", BasePath);
   AppendVector(std::move(ChildRecords), Out);
 
   std::vector<std::unique_ptr<TagNode>> ChildFunctions =
-      genFunctionsBlock(I.ChildFunctions, CDCtx, BasePath);
+      genFunctionsBlock(I.Children.Functions, CDCtx, BasePath);
   AppendVector(std::move(ChildFunctions), Out);
   std::vector<std::unique_ptr<TagNode>> ChildEnums =
-      genEnumsBlock(I.ChildEnums, CDCtx);
+      genEnumsBlock(I.Children.Enums, CDCtx);
   AppendVector(std::move(ChildEnums), Out);
 
-  if (!I.ChildNamespaces.empty())
+  if (!I.Children.Namespaces.empty())
     InfoIndex.Children.emplace_back("Namespaces", "Namespaces");
-  if (!I.ChildRecords.empty())
+  if (!I.Children.Records.empty())
     InfoIndex.Children.emplace_back("Records", "Records");
-  if (!I.ChildFunctions.empty())
+  if (!I.Children.Functions.empty())
     InfoIndex.Children.emplace_back(
-        genInfoIndexItem(I.ChildFunctions, "Functions"));
-  if (!I.ChildEnums.empty())
-    InfoIndex.Children.emplace_back(genInfoIndexItem(I.ChildEnums, "Enums"));
+        genInfoIndexItem(I.Children.Functions, "Functions"));
+  if (!I.Children.Enums.empty())
+    InfoIndex.Children.emplace_back(
+        genInfoIndexItem(I.Children.Enums, "Enums"));
 
   return Out;
 }
@@ -802,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");
 }
index 8957a47..0c5f163 100644 (file)
@@ -189,9 +189,9 @@ static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
 
   llvm::SmallString<64> BasePath = I.getRelativeFilePath("");
 
-  if (!I.ChildNamespaces.empty()) {
+  if (!I.Children.Namespaces.empty()) {
     writeHeader("Namespaces", 2, OS);
-    for (const auto &R : I.ChildNamespaces) {
+    for (const auto &R : I.Children.Namespaces) {
       OS << "* ";
       writeNameLink(BasePath, R, OS);
       OS << "\n";
@@ -199,9 +199,9 @@ static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
     writeNewLine(OS);
   }
 
-  if (!I.ChildRecords.empty()) {
+  if (!I.Children.Records.empty()) {
     writeHeader("Records", 2, OS);
-    for (const auto &R : I.ChildRecords) {
+    for (const auto &R : I.Children.Records) {
       OS << "* ";
       writeNameLink(BasePath, R, OS);
       OS << "\n";
@@ -209,15 +209,15 @@ static void genMarkdown(const ClangDocContext &CDCtx, const NamespaceInfo &I,
     writeNewLine(OS);
   }
 
-  if (!I.ChildFunctions.empty()) {
+  if (!I.Children.Functions.empty()) {
     writeHeader("Functions", 2, OS);
-    for (const auto &F : I.ChildFunctions)
+    for (const auto &F : I.Children.Functions)
       genMarkdown(CDCtx, F, OS);
     writeNewLine(OS);
   }
-  if (!I.ChildEnums.empty()) {
+  if (!I.Children.Enums.empty()) {
     writeHeader("Enums", 2, OS);
-    for (const auto &E : I.ChildEnums)
+    for (const auto &E : I.Children.Enums)
       genMarkdown(CDCtx, E, OS);
     writeNewLine(OS);
   }
@@ -259,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");
index 16a52e8..5264417 100644 (file)
@@ -71,6 +71,14 @@ bool MapASTVisitor::VisitFunctionDecl(const FunctionDecl *D) {
   return mapDecl(D);
 }
 
+bool MapASTVisitor::VisitTypedefDecl(const TypedefDecl *D) {
+  return mapDecl(D);
+}
+
+bool MapASTVisitor::VisitTypeAliasDecl(const TypeAliasDecl *D) {
+  return mapDecl(D);
+}
+
 comments::FullComment *
 MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const {
   RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
index b8cdb19..cedde93 100644 (file)
@@ -39,6 +39,8 @@ public:
   bool VisitEnumDecl(const EnumDecl *D);
   bool VisitCXXMethodDecl(const CXXMethodDecl *D);
   bool VisitFunctionDecl(const FunctionDecl *D);
+  bool VisitTypedefDecl(const TypedefDecl *D);
+  bool VisitTypeAliasDecl(const TypeAliasDecl *D);
 
 private:
   template <typename T> bool mapDecl(const T *D);
index 1e76543..27b83d6 100644 (file)
@@ -90,6 +90,18 @@ void reduceChildren(std::vector<EnumInfo> &Children,
   }
 }
 
+void reduceChildren(std::vector<TypedefInfo> &Children,
+                    std::vector<TypedefInfo> &&ChildrenToMerge) {
+  for (auto &ChildToMerge : ChildrenToMerge) {
+    int mergeIdx = getChildIndexIfExists(Children, ChildToMerge);
+    if (mergeIdx == -1) {
+      Children.push_back(std::move(ChildToMerge));
+      continue;
+    }
+    Children[mergeIdx].merge(std::move(ChildToMerge));
+  }
+}
+
 } // namespace
 
 // Dispatch function.
@@ -108,6 +120,8 @@ mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
     return reduce<EnumInfo>(Values);
   case InfoType::IT_function:
     return reduce<FunctionInfo>(Values);
+  case InfoType::IT_typedef:
+    return reduce<TypedefInfo>(Values);
   default:
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "unexpected info type");
@@ -209,10 +223,11 @@ void SymbolInfo::merge(SymbolInfo &&Other) {
 void NamespaceInfo::merge(NamespaceInfo &&Other) {
   assert(mergeable(Other));
   // Reduce children if necessary.
-  reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces));
-  reduceChildren(ChildRecords, std::move(Other.ChildRecords));
-  reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
-  reduceChildren(ChildEnums, std::move(Other.ChildEnums));
+  reduceChildren(Children.Namespaces, std::move(Other.Children.Namespaces));
+  reduceChildren(Children.Records, std::move(Other.Children.Records));
+  reduceChildren(Children.Functions, std::move(Other.Children.Functions));
+  reduceChildren(Children.Enums, std::move(Other.Children.Enums));
+  reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
   mergeBase(std::move(Other));
 }
 
@@ -230,9 +245,10 @@ void RecordInfo::merge(RecordInfo &&Other) {
   if (VirtualParents.empty())
     VirtualParents = std::move(Other.VirtualParents);
   // Reduce children if necessary.
-  reduceChildren(ChildRecords, std::move(Other.ChildRecords));
-  reduceChildren(ChildFunctions, std::move(Other.ChildFunctions));
-  reduceChildren(ChildEnums, std::move(Other.ChildEnums));
+  reduceChildren(Children.Records, std::move(Other.Children.Records));
+  reduceChildren(Children.Functions, std::move(Other.Children.Functions));
+  reduceChildren(Children.Enums, std::move(Other.Children.Enums));
+  reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
   SymbolInfo::merge(std::move(Other));
 }
 
@@ -260,6 +276,15 @@ void FunctionInfo::merge(FunctionInfo &&Other) {
   SymbolInfo::merge(std::move(Other));
 }
 
+void TypedefInfo::merge(TypedefInfo &&Other) {
+  assert(mergeable(Other));
+  if (!IsUsing)
+    IsUsing = Other.IsUsing;
+  if (Underlying.Type.Name == "")
+    Underlying = Other.Underlying;
+  SymbolInfo::merge(std::move(Other));
+}
+
 llvm::SmallString<16> Info::extractName() const {
   if (!Name.empty())
     return Name;
@@ -282,6 +307,9 @@ llvm::SmallString<16> Info::extractName() const {
   case InfoType::IT_enum:
     return llvm::SmallString<16>("@nonymous_enum_" +
                                  toHex(llvm::toStringRef(USR)));
+  case InfoType::IT_typedef:
+    return llvm::SmallString<16>("@nonymous_typedef_" +
+                                 toHex(llvm::toStringRef(USR)));
   case InfoType::IT_function:
     return llvm::SmallString<16>("@nonymous_function_" +
                                  toHex(llvm::toStringRef(USR)));
index 39e8723..d416306 100644 (file)
@@ -30,17 +30,19 @@ namespace doc {
 // SHA1'd hash of a USR.
 using SymbolID = std::array<uint8_t, 20>;
 
-struct Info;
-struct FunctionInfo;
-struct EnumInfo;
 struct BaseRecordInfo;
+struct EnumInfo;
+struct FunctionInfo;
+struct Info;
+struct TypedefInfo;
 
 enum class InfoType {
   IT_default,
   IT_namespace,
   IT_record,
   IT_function,
-  IT_enum
+  IT_enum,
+  IT_typedef
 };
 
 // A representation of a parsed comment.
@@ -142,6 +144,22 @@ struct Reference {
   llvm::SmallString<128> Path;
 };
 
+// Holds the children of a record or namespace.
+struct ScopeChildren {
+  // Namespaces and Records are references because they will be properly
+  // documented in their own info, while the entirety of Functions and Enums are
+  // included here because they should not have separate documentation from
+  // their scope.
+  //
+  // Namespaces are not syntactically valid as children of records, but making
+  // this general for all possible container types reduces code complexity.
+  std::vector<Reference> Namespaces;
+  std::vector<Reference> Records;
+  std::vector<FunctionInfo> Functions;
+  std::vector<EnumInfo> Enums;
+  std::vector<TypedefInfo> Typedefs;
+};
+
 // A base struct for TypeInfos
 struct TypeInfo {
   TypeInfo() = default;
@@ -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 {
index aeade31..66a938d 100644 (file)
@@ -273,6 +273,75 @@ static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
           isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
 }
 
+// The InsertChild functions insert the given info into the given scope using
+// the method appropriate for that type. Some types are moved into the
+// appropriate vector, while other types have Reference objects generated to
+// refer to them.
+//
+// See MakeAndInsertIntoParent().
+static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
+  Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
+                                getInfoRelativePath(Info.Namespace));
+}
+
+static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
+  Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
+                             getInfoRelativePath(Info.Namespace));
+}
+
+static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
+  Scope.Enums.push_back(std::move(Info));
+}
+
+static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
+  Scope.Functions.push_back(std::move(Info));
+}
+
+static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
+  Scope.Typedefs.push_back(std::move(Info));
+}
+
+// Creates a parent of the correct type for the given child and inserts it into
+// that parent.
+//
+// This is complicated by the fact that namespaces and records are inserted by
+// reference (constructing a "Reference" object with that namespace/record's
+// info), while everything else is inserted by moving it directly into the child
+// vectors.
+//
+// For namespaces and records, explicitly specify a const& template parameter
+// when invoking this function:
+//   MakeAndInsertIntoParent<const Record&>(...);
+// Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
+// parameter. Since each variant is used once, it's not worth having a more
+// elaborate system to automatically deduce this information.
+template <typename ChildType>
+std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {
+  if (Child.Namespace.empty()) {
+    // Insert into unnamed parent namespace.
+    auto ParentNS = std::make_unique<NamespaceInfo>();
+    InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
+    return ParentNS;
+  }
+
+  switch (Child.Namespace[0].RefType) {
+  case InfoType::IT_namespace: {
+    auto ParentNS = std::make_unique<NamespaceInfo>();
+    ParentNS->USR = Child.Namespace[0].USR;
+    InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
+    return ParentNS;
+  }
+  case InfoType::IT_record: {
+    auto ParentRec = std::make_unique<RecordInfo>();
+    ParentRec->USR = Child.Namespace[0].USR;
+    InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
+    return ParentRec;
+  }
+  default:
+    llvm_unreachable("Invalid reference type for parent namespace");
+  }
+}
+
 // There are two uses for this function.
 // 1) Getting the resulting mode of inheritance of a record.
 //    Example: class A {}; class B : private A {}; class C : public B {};
@@ -376,8 +445,8 @@ template <typename T>
 static void
 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
                          const T *D, bool &IsInAnonymousNamespace) {
-  const auto *DC = cast<DeclContext>(D);
-  while ((DC = DC->getParent())) {
+  const DeclContext *DC = D->getDeclContext();
+  do {
     if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
       std::string Namespace;
       if (N->isAnonymousNamespace()) {
@@ -396,7 +465,7 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
     else if (const auto *N = dyn_cast<EnumDecl>(DC))
       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
                               InfoType::IT_enum);
-  }
+  } while ((DC = DC->getParent()));
   // The global namespace should be added to the list of namespaces if the decl
   // corresponds to a Record and if it doesn't have any namespace (because this
   // means it's in the global namespace). Also if its outermost namespace is a
@@ -501,7 +570,7 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
                                  IsInAnonymousNamespace);
             FI.Access =
                 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
-            BI.ChildFunctions.emplace_back(std::move(FI));
+            BI.Children.Functions.emplace_back(std::move(FI));
           }
         I.Bases.emplace_back(std::move(BI));
         // Call this function recursively to get the inherited classes of
@@ -530,14 +599,9 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
   if (I->Namespace.empty() && I->USR == SymbolID())
     return {std::unique_ptr<Info>{std::move(I)}, nullptr};
 
-  auto ParentI = std::make_unique<NamespaceInfo>();
-  ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR;
-  ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace,
-                                        getInfoRelativePath(I->Namespace));
-  if (I->Namespace.empty())
-    ParentI->Path = getInfoRelativePath(ParentI->Namespace);
-  return {std::unique_ptr<Info>{std::move(I)},
-          std::unique_ptr<Info>{std::move(ParentI)}};
+  // Namespaces are inserted into the parent by reference, so we need to return
+  // both the parent and the record itself.
+  return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
@@ -563,26 +627,10 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
   }
   I->Path = getInfoRelativePath(I->Namespace);
 
-  switch (I->Namespace[0].RefType) {
-  case InfoType::IT_namespace: {
-    auto ParentI = std::make_unique<NamespaceInfo>();
-    ParentI->USR = I->Namespace[0].USR;
-    ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
-                                       getInfoRelativePath(I->Namespace));
-    return {std::unique_ptr<Info>{std::move(I)},
-            std::unique_ptr<Info>{std::move(ParentI)}};
-  }
-  case InfoType::IT_record: {
-    auto ParentI = std::make_unique<RecordInfo>();
-    ParentI->USR = I->Namespace[0].USR;
-    ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
-                                       getInfoRelativePath(I->Namespace));
-    return {std::unique_ptr<Info>{std::move(I)},
-            std::unique_ptr<Info>{std::move(ParentI)}};
-  }
-  default:
-    llvm_unreachable("Invalid reference type for parent namespace");
-  }
+  // Records are inserted into the parent by reference, so we need to return
+  // both the parent and the record itself.
+  auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
+  return {std::move(I), std::move(Parent)};
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
@@ -596,17 +644,8 @@ emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
-  // Wrap in enclosing scope
-  auto ParentI = std::make_unique<NamespaceInfo>();
-  if (!Func.Namespace.empty())
-    ParentI->USR = Func.Namespace[0].USR;
-  else
-    ParentI->USR = SymbolID();
-  if (Func.Namespace.empty())
-    ParentI->Path = getInfoRelativePath(ParentI->Namespace);
-  ParentI->ChildFunctions.emplace_back(std::move(Func));
-  // Info is wrapped in its parent scope so it's returned in the second position
-  return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
+  // Info is wrapped in its parent scope so is returned in the second position.
+  return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
@@ -633,12 +672,52 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
       Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record};
   Func.Access = D->getAccess();
 
-  // Wrap in enclosing scope
-  auto ParentI = std::make_unique<RecordInfo>();
-  ParentI->USR = ParentUSR;
-  ParentI->ChildFunctions.emplace_back(std::move(Func));
-  // Info is wrapped in its parent scope so it's returned in the second position
-  return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
+  // Info is wrapped in its parent scope so is returned in the second position.
+  return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
+}
+
+std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
+emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
+         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+  TypedefInfo Info;
+
+  bool IsInAnonymousNamespace = false;
+  populateInfo(Info, D, FC, IsInAnonymousNamespace);
+  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
+    return {};
+
+  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
+  Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
+  if (Info.Underlying.Type.Name.empty()) {
+    // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
+    // The record serializer explicitly checks for this syntax and constructs
+    // a record with that name, so we don't want to emit a duplicate here.
+    return {};
+  }
+  Info.IsUsing = false;
+
+  // Info is wrapped in its parent scope so is returned in the second position.
+  return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
+}
+
+// A type alias is a C++ "using" declaration for a type. It gets mapped to a
+// TypedefInfo with the IsUsing flag set.
+std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
+emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
+         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+  TypedefInfo Info;
+
+  bool IsInAnonymousNamespace = false;
+  populateInfo(Info, D, FC, IsInAnonymousNamespace);
+  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
+    return {};
+
+  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
+  Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
+  Info.IsUsing = true;
+
+  // Info is wrapped in its parent scope so is returned in the second position.
+  return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
@@ -656,38 +735,8 @@ emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
     Enum.BaseType = TypeInfo(D->getIntegerType().getAsString());
   parseEnumerators(Enum, D);
 
-  // Put in global namespace
-  if (Enum.Namespace.empty()) {
-    auto ParentI = std::make_unique<NamespaceInfo>();
-    ParentI->USR = SymbolID();
-    ParentI->ChildEnums.emplace_back(std::move(Enum));
-    ParentI->Path = getInfoRelativePath(ParentI->Namespace);
-    // Info is wrapped in its parent scope so it's returned in the second
-    // position
-    return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
-  }
-
-  // Wrap in enclosing scope
-  switch (Enum.Namespace[0].RefType) {
-  case InfoType::IT_namespace: {
-    auto ParentI = std::make_unique<NamespaceInfo>();
-    ParentI->USR = Enum.Namespace[0].USR;
-    ParentI->ChildEnums.emplace_back(std::move(Enum));
-    // Info is wrapped in its parent scope so it's returned in the second
-    // position
-    return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
-  }
-  case InfoType::IT_record: {
-    auto ParentI = std::make_unique<RecordInfo>();
-    ParentI->USR = Enum.Namespace[0].USR;
-    ParentI->ChildEnums.emplace_back(std::move(Enum));
-    // Info is wrapped in its parent scope so it's returned in the second
-    // position
-    return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
-  }
-  default:
-    llvm_unreachable("Invalid reference type for parent namespace");
-  }
+  // Info is wrapped in its parent scope so is returned in the second position.
+  return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
 }
 
 } // namespace serialize
index da1361e..4e203ca 100644 (file)
@@ -39,19 +39,31 @@ namespace serialize {
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
 emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
          StringRef File, bool IsFileInRootDir, bool PublicOnly);
+
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
 emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
          StringRef File, bool IsFileInRootDir, bool PublicOnly);
+
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
 emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
          StringRef File, bool IsFileInRootDir, bool PublicOnly);
+
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
 emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
          StringRef File, bool IsFileInRootDir, bool PublicOnly);
+
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
 emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
          StringRef File, bool IsFileInRootDir, bool PublicOnly);
 
+std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
+emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
+         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+
+std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
+emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
+         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+
 // Function to hash a given USR value for storage.
 // As USRs (Unified Symbol Resolution) could be large, especially for functions
 // with long type arguments, we use 160-bits SHA1(USR) values to
index 6406b81..fcca0a6 100644 (file)
@@ -23,6 +23,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(EnumValueInfo)
+LLVM_YAML_IS_SEQUENCE_VECTOR(TypedefInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(BaseRecordInfo)
 LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr<CommentInfo>)
 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>)
@@ -137,9 +138,10 @@ static void RecordInfoMapping(IO &IO, RecordInfo &I) {
   IO.mapOptional("Parents", I.Parents, llvm::SmallVector<Reference, 4>());
   IO.mapOptional("VirtualParents", I.VirtualParents,
                  llvm::SmallVector<Reference, 4>());
-  IO.mapOptional("ChildRecords", I.ChildRecords, std::vector<Reference>());
-  IO.mapOptional("ChildFunctions", I.ChildFunctions);
-  IO.mapOptional("ChildEnums", I.ChildEnums);
+  IO.mapOptional("ChildRecords", I.Children.Records, std::vector<Reference>());
+  IO.mapOptional("ChildFunctions", I.Children.Functions);
+  IO.mapOptional("ChildEnums", I.Children.Enums);
+  IO.mapOptional("ChildTypedefs", I.Children.Typedefs);
 }
 
 static void CommentInfoMapping(IO &IO, CommentInfo &I) {
@@ -203,11 +205,13 @@ template <> struct MappingTraits<MemberTypeInfo> {
 template <> struct MappingTraits<NamespaceInfo> {
   static void mapping(IO &IO, NamespaceInfo &I) {
     InfoMapping(IO, I);
-    IO.mapOptional("ChildNamespaces", I.ChildNamespaces,
+    IO.mapOptional("ChildNamespaces", I.Children.Namespaces,
                    std::vector<Reference>());
-    IO.mapOptional("ChildRecords", I.ChildRecords, std::vector<Reference>());
-    IO.mapOptional("ChildFunctions", I.ChildFunctions);
-    IO.mapOptional("ChildEnums", I.ChildEnums);
+    IO.mapOptional("ChildRecords", I.Children.Records,
+                   std::vector<Reference>());
+    IO.mapOptional("ChildFunctions", I.Children.Functions);
+    IO.mapOptional("ChildEnums", I.Children.Enums);
+    IO.mapOptional("ChildTypedefs", I.Children.Typedefs);
   }
 };
 
@@ -244,6 +248,14 @@ template <> struct MappingTraits<EnumInfo> {
   }
 };
 
+template <> struct MappingTraits<TypedefInfo> {
+  static void mapping(IO &IO, TypedefInfo &I) {
+    SymbolInfoMapping(IO, I);
+    IO.mapOptional("Underlying", I.Underlying.Type);
+    IO.mapOptional("IsUsing", I.IsUsing, false);
+  }
+};
+
 template <> struct MappingTraits<FunctionInfo> {
   static void mapping(IO &IO, FunctionInfo &I) {
     SymbolInfoMapping(IO, I);
@@ -302,6 +314,9 @@ llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   case InfoType::IT_function:
     InfoYAML << *static_cast<clang::doc::FunctionInfo *>(I);
     break;
+  case InfoType::IT_typedef:
+    InfoYAML << *static_cast<clang::doc::TypedefInfo *>(I);
+    break;
   case InfoType::IT_default:
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "unexpected InfoType");
index e6933a7..db65694 100644 (file)
@@ -35,6 +35,8 @@ std::string writeInfo(Info *I) {
     return writeInfo(*static_cast<EnumInfo *>(I));
   case InfoType::IT_function:
     return writeInfo(*static_cast<FunctionInfo *>(I));
+  case InfoType::IT_typedef:
+    return writeInfo(*static_cast<TypedefInfo *>(I));
   default:
     return "";
   }
@@ -57,11 +59,11 @@ TEST(BitcodeTest, emitNamespaceInfoBitcode) {
   I.Name = "r";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
-                                 InfoType::IT_namespace);
-  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
-  I.ChildFunctions.emplace_back();
-  I.ChildEnums.emplace_back();
+  I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
+                                     InfoType::IT_namespace);
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+  I.Children.Functions.emplace_back();
+  I.Children.Enums.emplace_back();
 
   std::string WriteResult = writeInfo(&I);
   EXPECT_TRUE(WriteResult.size() > 0);
@@ -83,7 +85,7 @@ TEST(BitcodeTest, emitRecordInfoBitcode) {
   I.IsTypeDef = true;
   I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
                        AccessSpecifier::AS_public, true);
-  I.Bases.back().ChildFunctions.emplace_back();
+  I.Bases.back().Children.Functions.emplace_back();
   I.Bases.back().Members.emplace_back(TypeInfo("int"), "X",
                                       AccessSpecifier::AS_private);
   I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
@@ -101,9 +103,9 @@ TEST(BitcodeTest, emitRecordInfoBitcode) {
   Brief->Children.back()->Text = "Value of the thing.";
   I.Bases.back().Members.back().Description.emplace_back(std::move(TopComment));
 
-  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
-  I.ChildFunctions.emplace_back();
-  I.ChildEnums.emplace_back();
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+  I.Children.Functions.emplace_back();
+  I.Children.Enums.emplace_back();
 
   std::string WriteResult = writeInfo(&I);
   EXPECT_TRUE(WriteResult.size() > 0);
@@ -172,6 +174,22 @@ TEST(BitcodeTest, emitEnumInfoBitcode) {
   CheckEnumInfo(&I, InfoAsEnum(ReadResults[0].get()));
 }
 
+TEST(BitcodeTest, emitTypedefInfoBitcode) {
+  TypedefInfo I;
+  I.Name = "MyInt";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+  I.Underlying = TypeInfo("unsigned");
+  I.IsUsing = true;
+
+  std::string WriteResult = writeInfo(&I);
+  EXPECT_TRUE(WriteResult.size() > 0);
+  std::vector<std::unique_ptr<Info>> ReadResults = readInfo(WriteResult, 1);
+
+  CheckTypedefInfo(&I, InfoAsTypedef(ReadResults[0].get()));
+}
+
 TEST(SerializeTest, emitInfoWithCommentBitcode) {
   FunctionInfo F;
   F.Name = "F";
index 01816fb..6e28aff 100644 (file)
@@ -34,6 +34,11 @@ EnumInfo *InfoAsEnum(Info *I) {
   return static_cast<EnumInfo *>(I);
 }
 
+TypedefInfo *InfoAsTypedef(Info *I) {
+  assert(I->IT == InfoType::IT_typedef);
+  return static_cast<TypedefInfo *>(I);
+}
+
 void CheckCommentInfo(const std::vector<CommentInfo> &Expected,
                       const std::vector<CommentInfo> &Actual);
 void CheckCommentInfo(const std::vector<std::unique_ptr<CommentInfo>> &Expected,
@@ -144,26 +149,35 @@ void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual) {
     EXPECT_EQ(Expected->Members[Idx], Actual->Members[Idx]);
 }
 
+void CheckTypedefInfo(TypedefInfo *Expected, TypedefInfo *Actual) {
+  CheckSymbolInfo(Expected, Actual);
+  EXPECT_EQ(Expected->IsUsing, Actual->IsUsing);
+  CheckTypeInfo(&Expected->Underlying, &Actual->Underlying);
+}
+
 void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual) {
   CheckBaseInfo(Expected, Actual);
 
-  ASSERT_EQ(Expected->ChildNamespaces.size(), Actual->ChildNamespaces.size());
-  for (size_t Idx = 0; Idx < Actual->ChildNamespaces.size(); ++Idx)
-    CheckReference(Expected->ChildNamespaces[Idx],
-                   Actual->ChildNamespaces[Idx]);
+  ASSERT_EQ(Expected->Children.Namespaces.size(),
+            Actual->Children.Namespaces.size());
+  for (size_t Idx = 0; Idx < Actual->Children.Namespaces.size(); ++Idx)
+    CheckReference(Expected->Children.Namespaces[Idx],
+                   Actual->Children.Namespaces[Idx]);
 
-  ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size());
-  for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx)
-    CheckReference(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]);
+  ASSERT_EQ(Expected->Children.Records.size(), Actual->Children.Records.size());
+  for (size_t Idx = 0; Idx < Actual->Children.Records.size(); ++Idx)
+    CheckReference(Expected->Children.Records[Idx],
+                   Actual->Children.Records[Idx]);
 
-  ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size());
-  for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx)
-    CheckFunctionInfo(&Expected->ChildFunctions[Idx],
-                      &Actual->ChildFunctions[Idx]);
+  ASSERT_EQ(Expected->Children.Functions.size(),
+            Actual->Children.Functions.size());
+  for (size_t Idx = 0; Idx < Actual->Children.Functions.size(); ++Idx)
+    CheckFunctionInfo(&Expected->Children.Functions[Idx],
+                      &Actual->Children.Functions[Idx]);
 
-  ASSERT_EQ(Expected->ChildEnums.size(), Actual->ChildEnums.size());
-  for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx)
-    CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]);
+  ASSERT_EQ(Expected->Children.Enums.size(), Actual->Children.Enums.size());
+  for (size_t Idx = 0; Idx < Actual->Children.Enums.size(); ++Idx)
+    CheckEnumInfo(&Expected->Children.Enums[Idx], &Actual->Children.Enums[Idx]);
 }
 
 void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) {
@@ -189,18 +203,20 @@ void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) {
   for (size_t Idx = 0; Idx < Actual->Bases.size(); ++Idx)
     CheckBaseRecordInfo(&Expected->Bases[Idx], &Actual->Bases[Idx]);
 
-  ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size());
-  for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx)
-    CheckReference(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]);
+  ASSERT_EQ(Expected->Children.Records.size(), Actual->Children.Records.size());
+  for (size_t Idx = 0; Idx < Actual->Children.Records.size(); ++Idx)
+    CheckReference(Expected->Children.Records[Idx],
+                   Actual->Children.Records[Idx]);
 
-  ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size());
-  for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx)
-    CheckFunctionInfo(&Expected->ChildFunctions[Idx],
-                      &Actual->ChildFunctions[Idx]);
+  ASSERT_EQ(Expected->Children.Functions.size(),
+            Actual->Children.Functions.size());
+  for (size_t Idx = 0; Idx < Actual->Children.Functions.size(); ++Idx)
+    CheckFunctionInfo(&Expected->Children.Functions[Idx],
+                      &Actual->Children.Functions[Idx]);
 
-  ASSERT_EQ(Expected->ChildEnums.size(), Actual->ChildEnums.size());
-  for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx)
-    CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]);
+  ASSERT_EQ(Expected->Children.Enums.size(), Actual->Children.Enums.size());
+  for (size_t Idx = 0; Idx < Actual->Children.Enums.size(); ++Idx)
+    CheckEnumInfo(&Expected->Children.Enums[Idx], &Actual->Children.Enums[Idx]);
 }
 
 void CheckBaseRecordInfo(BaseRecordInfo *Expected, BaseRecordInfo *Actual) {
index 08f4534..2e189ca 100644 (file)
@@ -27,6 +27,7 @@ NamespaceInfo *InfoAsNamespace(Info *I);
 RecordInfo *InfoAsRecord(Info *I);
 FunctionInfo *InfoAsFunction(Info *I);
 EnumInfo *InfoAsEnum(Info *I);
+TypedefInfo *InfoAsTypedef(Info *I);
 
 // Unlike the operator==, these functions explicitly does not check USRs, as
 // that may change and it would be better to not rely on its implementation.
@@ -41,6 +42,7 @@ void CheckBaseInfo(Info *Expected, Info *Actual);
 void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual);
 void CheckFunctionInfo(FunctionInfo *Expected, FunctionInfo *Actual);
 void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual);
+void CheckTypedefInfo(TypedefInfo *Expected, TypedefInfo *Actual);
 void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual);
 void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual);
 void CheckBaseRecordInfo(BaseRecordInfo *Expected, BaseRecordInfo *Actual);
index 25ecb55..d912253 100644 (file)
@@ -43,15 +43,15 @@ TEST(HTMLGeneratorTest, emitNamespaceHTML) {
   I.Name = "Namespace";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
-                                 InfoType::IT_namespace, "Namespace");
-  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
-                              "Namespace");
-  I.ChildFunctions.emplace_back();
-  I.ChildFunctions.back().Access = AccessSpecifier::AS_none;
-  I.ChildFunctions.back().Name = "OneFunction";
-  I.ChildEnums.emplace_back();
-  I.ChildEnums.back().Name = "OneEnum";
+  I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
+                                     InfoType::IT_namespace, "Namespace");
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+                                  "Namespace");
+  I.Children.Functions.emplace_back();
+  I.Children.Functions.back().Access = AccessSpecifier::AS_none;
+  I.Children.Functions.back().Name = "OneFunction";
+  I.Children.Enums.emplace_back();
+  I.Children.Enums.back().Name = "OneEnum";
 
   auto G = getHTMLGenerator();
   assert(G);
@@ -158,12 +158,12 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
   I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, PathTo);
   I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
 
-  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
-                              "X/Y/Z/r");
-  I.ChildFunctions.emplace_back();
-  I.ChildFunctions.back().Name = "OneFunction";
-  I.ChildEnums.emplace_back();
-  I.ChildEnums.back().Name = "OneEnum";
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+                                  "X/Y/Z/r");
+  I.Children.Functions.emplace_back();
+  I.Children.Functions.back().Name = "OneFunction";
+  I.Children.Enums.emplace_back();
+  I.Children.Enums.back().Name = "OneEnum";
 
   auto G = getHTMLGenerator();
   assert(G);
index f2a7858..be9ccee 100644 (file)
@@ -26,14 +26,14 @@ TEST(MDGeneratorTest, emitNamespaceMD) {
   I.Name = "Namespace";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
-                                 InfoType::IT_namespace);
-  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
-  I.ChildFunctions.emplace_back();
-  I.ChildFunctions.back().Name = "OneFunction";
-  I.ChildFunctions.back().Access = AccessSpecifier::AS_none;
-  I.ChildEnums.emplace_back();
-  I.ChildEnums.back().Name = "OneEnum";
+  I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
+                                     InfoType::IT_namespace);
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+  I.Children.Functions.emplace_back();
+  I.Children.Functions.back().Name = "OneFunction";
+  I.Children.Functions.back().Access = AccessSpecifier::AS_none;
+  I.Children.Enums.emplace_back();
+  I.Children.Enums.back().Name = "OneEnum";
 
   auto G = getMDGenerator();
   assert(G);
@@ -90,11 +90,11 @@ TEST(MDGeneratorTest, emitRecordMD) {
   I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
   I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
 
-  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
-  I.ChildFunctions.emplace_back();
-  I.ChildFunctions.back().Name = "OneFunction";
-  I.ChildEnums.emplace_back();
-  I.ChildEnums.back().Name = "OneEnum";
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+  I.Children.Functions.emplace_back();
+  I.Children.Functions.back().Name = "OneFunction";
+  I.Children.Enums.emplace_back();
+  I.Children.Enums.back().Name = "OneEnum";
 
   auto G = getMDGenerator();
   assert(G);
index 2305bd2..51fddb9 100644 (file)
@@ -18,29 +18,29 @@ TEST(MergeTest, mergeNamespaceInfos) {
   One.Name = "Namespace";
   One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  One.ChildNamespaces.emplace_back(NonEmptySID, "ChildNamespace",
-                                   InfoType::IT_namespace);
-  One.ChildRecords.emplace_back(NonEmptySID, "ChildStruct",
-                                InfoType::IT_record);
-  One.ChildFunctions.emplace_back();
-  One.ChildFunctions.back().Name = "OneFunction";
-  One.ChildFunctions.back().USR = NonEmptySID;
-  One.ChildEnums.emplace_back();
-  One.ChildEnums.back().Name = "OneEnum";
-  One.ChildEnums.back().USR = NonEmptySID;
+  One.Children.Namespaces.emplace_back(NonEmptySID, "ChildNamespace",
+                                       InfoType::IT_namespace);
+  One.Children.Records.emplace_back(NonEmptySID, "ChildStruct",
+                                    InfoType::IT_record);
+  One.Children.Functions.emplace_back();
+  One.Children.Functions.back().Name = "OneFunction";
+  One.Children.Functions.back().USR = NonEmptySID;
+  One.Children.Enums.emplace_back();
+  One.Children.Enums.back().Name = "OneEnum";
+  One.Children.Enums.back().USR = NonEmptySID;
 
   NamespaceInfo Two;
   Two.Name = "Namespace";
   Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  Two.ChildNamespaces.emplace_back(EmptySID, "OtherChildNamespace",
-                                   InfoType::IT_namespace);
-  Two.ChildRecords.emplace_back(EmptySID, "OtherChildStruct",
-                                InfoType::IT_record);
-  Two.ChildFunctions.emplace_back();
-  Two.ChildFunctions.back().Name = "TwoFunction";
-  Two.ChildEnums.emplace_back();
-  Two.ChildEnums.back().Name = "TwoEnum";
+  Two.Children.Namespaces.emplace_back(EmptySID, "OtherChildNamespace",
+                                       InfoType::IT_namespace);
+  Two.Children.Records.emplace_back(EmptySID, "OtherChildStruct",
+                                    InfoType::IT_record);
+  Two.Children.Functions.emplace_back();
+  Two.Children.Functions.back().Name = "TwoFunction";
+  Two.Children.Enums.emplace_back();
+  Two.Children.Enums.back().Name = "TwoEnum";
 
   std::vector<std::unique_ptr<Info>> Infos;
   Infos.emplace_back(std::make_unique<NamespaceInfo>(std::move(One)));
@@ -50,24 +50,24 @@ TEST(MergeTest, mergeNamespaceInfos) {
   Expected->Name = "Namespace";
   Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  Expected->ChildNamespaces.emplace_back(NonEmptySID, "ChildNamespace",
-                                         InfoType::IT_namespace);
-  Expected->ChildRecords.emplace_back(NonEmptySID, "ChildStruct",
-                                      InfoType::IT_record);
-  Expected->ChildNamespaces.emplace_back(EmptySID, "OtherChildNamespace",
-                                         InfoType::IT_namespace);
-  Expected->ChildRecords.emplace_back(EmptySID, "OtherChildStruct",
-                                      InfoType::IT_record);
-  Expected->ChildFunctions.emplace_back();
-  Expected->ChildFunctions.back().Name = "OneFunction";
-  Expected->ChildFunctions.back().USR = NonEmptySID;
-  Expected->ChildFunctions.emplace_back();
-  Expected->ChildFunctions.back().Name = "TwoFunction";
-  Expected->ChildEnums.emplace_back();
-  Expected->ChildEnums.back().Name = "OneEnum";
-  Expected->ChildEnums.back().USR = NonEmptySID;
-  Expected->ChildEnums.emplace_back();
-  Expected->ChildEnums.back().Name = "TwoEnum";
+  Expected->Children.Namespaces.emplace_back(NonEmptySID, "ChildNamespace",
+                                             InfoType::IT_namespace);
+  Expected->Children.Records.emplace_back(NonEmptySID, "ChildStruct",
+                                          InfoType::IT_record);
+  Expected->Children.Namespaces.emplace_back(EmptySID, "OtherChildNamespace",
+                                             InfoType::IT_namespace);
+  Expected->Children.Records.emplace_back(EmptySID, "OtherChildStruct",
+                                          InfoType::IT_record);
+  Expected->Children.Functions.emplace_back();
+  Expected->Children.Functions.back().Name = "OneFunction";
+  Expected->Children.Functions.back().USR = NonEmptySID;
+  Expected->Children.Functions.emplace_back();
+  Expected->Children.Functions.back().Name = "TwoFunction";
+  Expected->Children.Enums.emplace_back();
+  Expected->Children.Enums.back().Name = "OneEnum";
+  Expected->Children.Enums.back().USR = NonEmptySID;
+  Expected->Children.Enums.emplace_back();
+  Expected->Children.Enums.back().Name = "TwoEnum";
 
   auto Actual = mergeInfos(Infos);
   assert(Actual);
@@ -90,14 +90,14 @@ TEST(MergeTest, mergeRecordInfos) {
 
   One.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
                          AccessSpecifier::AS_protected, true);
-  One.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
-                                InfoType::IT_record);
-  One.ChildFunctions.emplace_back();
-  One.ChildFunctions.back().Name = "OneFunction";
-  One.ChildFunctions.back().USR = NonEmptySID;
-  One.ChildEnums.emplace_back();
-  One.ChildEnums.back().Name = "OneEnum";
-  One.ChildEnums.back().USR = NonEmptySID;
+  One.Children.Records.emplace_back(NonEmptySID, "SharedChildStruct",
+                                    InfoType::IT_record);
+  One.Children.Functions.emplace_back();
+  One.Children.Functions.back().Name = "OneFunction";
+  One.Children.Functions.back().USR = NonEmptySID;
+  One.Children.Enums.emplace_back();
+  One.Children.Enums.back().Name = "OneEnum";
+  One.Children.Enums.back().USR = NonEmptySID;
 
   RecordInfo Two;
   Two.Name = "r";
@@ -107,12 +107,12 @@ TEST(MergeTest, mergeRecordInfos) {
 
   Two.TagType = TagTypeKind::TTK_Class;
 
-  Two.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
-                                InfoType::IT_record, "path");
-  Two.ChildFunctions.emplace_back();
-  Two.ChildFunctions.back().Name = "TwoFunction";
-  Two.ChildEnums.emplace_back();
-  Two.ChildEnums.back().Name = "TwoEnum";
+  Two.Children.Records.emplace_back(NonEmptySID, "SharedChildStruct",
+                                    InfoType::IT_record, "path");
+  Two.Children.Functions.emplace_back();
+  Two.Children.Functions.back().Name = "TwoFunction";
+  Two.Children.Enums.emplace_back();
+  Two.Children.Enums.back().Name = "TwoEnum";
 
   std::vector<std::unique_ptr<Info>> Infos;
   Infos.emplace_back(std::make_unique<RecordInfo>(std::move(One)));
@@ -134,18 +134,18 @@ TEST(MergeTest, mergeRecordInfos) {
   Expected->Bases.emplace_back(EmptySID, "F", "path/to/F", true,
                                AccessSpecifier::AS_protected, true);
 
-  Expected->ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
-                                      InfoType::IT_record, "path");
-  Expected->ChildFunctions.emplace_back();
-  Expected->ChildFunctions.back().Name = "OneFunction";
-  Expected->ChildFunctions.back().USR = NonEmptySID;
-  Expected->ChildFunctions.emplace_back();
-  Expected->ChildFunctions.back().Name = "TwoFunction";
-  Expected->ChildEnums.emplace_back();
-  Expected->ChildEnums.back().Name = "OneEnum";
-  Expected->ChildEnums.back().USR = NonEmptySID;
-  Expected->ChildEnums.emplace_back();
-  Expected->ChildEnums.back().Name = "TwoEnum";
+  Expected->Children.Records.emplace_back(NonEmptySID, "SharedChildStruct",
+                                          InfoType::IT_record, "path");
+  Expected->Children.Functions.emplace_back();
+  Expected->Children.Functions.back().Name = "OneFunction";
+  Expected->Children.Functions.back().USR = NonEmptySID;
+  Expected->Children.Functions.emplace_back();
+  Expected->Children.Functions.back().Name = "TwoFunction";
+  Expected->Children.Enums.emplace_back();
+  Expected->Children.Enums.back().Name = "OneEnum";
+  Expected->Children.Enums.back().USR = NonEmptySID;
+  Expected->Children.Enums.emplace_back();
+  Expected->Children.Enums.back().Name = "TwoEnum";
 
   auto Actual = mergeInfos(Infos);
   assert(Actual);
index bc45264..05b6245 100644 (file)
@@ -59,6 +59,10 @@ public:
   bool VisitRecordDecl(const RecordDecl *D) { return mapDecl(D); }
 
   bool VisitEnumDecl(const EnumDecl *D) { return mapDecl(D); }
+
+  bool VisitTypedefDecl(const TypedefDecl *D) { return mapDecl(D); }
+
+  bool VisitTypeAliasDecl(const TypeAliasDecl *D) { return mapDecl(D); }
 };
 
 void ExtractInfosFromCode(StringRef Code, size_t NumExpectedInfos, bool Public,
@@ -124,7 +128,7 @@ TEST(SerializeTest, emitNamespaceInfo) {
   F.Namespace.emplace_back(EmptySID, "B", InfoType::IT_namespace);
   F.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
   F.Access = AccessSpecifier::AS_none;
-  ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F));
+  ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
   CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
 }
 
@@ -183,7 +187,7 @@ typedef struct {} G;)raw",
                                       InfoType::IT_namespace);
   EConstructor.Access = AccessSpecifier::AS_public;
   EConstructor.IsMethod = true;
-  ExpectedRecordWithEConstructor.ChildFunctions.emplace_back(
+  ExpectedRecordWithEConstructor.Children.Functions.emplace_back(
       std::move(EConstructor));
   CheckRecordInfo(&ExpectedRecordWithEConstructor, RecordWithEConstructor);
 
@@ -199,7 +203,7 @@ typedef struct {} G;)raw",
                                 InfoType::IT_namespace);
   Method.Access = AccessSpecifier::AS_protected;
   Method.IsMethod = true;
-  ExpectedRecordWithMethod.ChildFunctions.emplace_back(std::move(Method));
+  ExpectedRecordWithMethod.Children.Functions.emplace_back(std::move(Method));
   CheckRecordInfo(&ExpectedRecordWithMethod, RecordWithMethod);
 
   RecordInfo *F = InfoAsRecord(Infos[4].get());
@@ -222,7 +226,7 @@ typedef struct {} G;)raw",
                                         InfoType::IT_namespace);
   TemplateMethod.Access = AccessSpecifier::AS_public;
   TemplateMethod.IsMethod = true;
-  ExpectedRecordWithTemplateMethod.ChildFunctions.emplace_back(
+  ExpectedRecordWithTemplateMethod.Children.Functions.emplace_back(
       std::move(TemplateMethod));
   CheckRecordInfo(&ExpectedRecordWithTemplateMethod, RecordWithTemplateMethod);
 
@@ -241,7 +245,7 @@ typedef struct {} G;)raw",
                                                    InfoType::IT_namespace);
   SpecializedTemplateMethod.Access = AccessSpecifier::AS_public;
   SpecializedTemplateMethod.IsMethod = true;
-  ExpectedTemplatedRecord.ChildFunctions.emplace_back(
+  ExpectedTemplatedRecord.Children.Functions.emplace_back(
       std::move(SpecializedTemplateMethod));
   CheckRecordInfo(&ExpectedTemplatedRecord, TemplatedRecord);
 
@@ -268,7 +272,7 @@ TEST(SerializeTest, emitEnumInfo) {
   E.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
   E.Members.emplace_back("X", "0");
   E.Members.emplace_back("Y", "1");
-  ExpectedNamespaceWithEnum.ChildEnums.emplace_back(std::move(E));
+  ExpectedNamespaceWithEnum.Children.Enums.emplace_back(std::move(E));
   CheckNamespaceInfo(&ExpectedNamespaceWithEnum, NamespaceWithEnum);
 
   NamespaceInfo *NamespaceWithScopedEnum = InfoAsNamespace(Infos[1].get());
@@ -279,7 +283,7 @@ TEST(SerializeTest, emitEnumInfo) {
   G.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
   G.Members.emplace_back("A", "0");
   G.Members.emplace_back("B", "1");
-  ExpectedNamespaceWithScopedEnum.ChildEnums.emplace_back(std::move(G));
+  ExpectedNamespaceWithScopedEnum.Children.Enums.emplace_back(std::move(G));
   CheckNamespaceInfo(&ExpectedNamespaceWithScopedEnum, NamespaceWithScopedEnum);
 }
 
@@ -352,7 +356,7 @@ TEST(SerializeTest, emitPublicFunctionInternalInfo) {
   F.ReturnType = TypeInfo("int");
   F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
   F.Access = AccessSpecifier::AS_none;
-  ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F));
+  ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
   CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
 }
 
@@ -368,7 +372,7 @@ TEST(SerializeTest, emitInlinedFunctionInfo) {
   F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
   F.Params.emplace_back(TypeInfo("int"), "I");
   F.Access = AccessSpecifier::AS_none;
-  ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F));
+  ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
   CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
 }
 
@@ -422,7 +426,8 @@ class J : public I<int> {} ;)raw",
                                      InfoType::IT_namespace);
   FunctionSet.Access = AccessSpecifier::AS_protected;
   FunctionSet.IsMethod = true;
-  ExpectedE.Bases.back().ChildFunctions.emplace_back(std::move(FunctionSet));
+  ExpectedE.Bases.back().Children.Functions.emplace_back(
+      std::move(FunctionSet));
   ExpectedE.Bases.emplace_back(EmptySID, /*Name=*/"G",
                                /*Path=*/"GlobalNamespace", true,
                                AccessSpecifier::AS_private, true);
@@ -435,7 +440,8 @@ class J : public I<int> {} ;)raw",
                                      InfoType::IT_namespace);
   FunctionGet.Access = AccessSpecifier::AS_private;
   FunctionGet.IsMethod = true;
-  ExpectedE.Bases.back().ChildFunctions.emplace_back(std::move(FunctionGet));
+  ExpectedE.Bases.back().Children.Functions.emplace_back(
+      std::move(FunctionGet));
   ExpectedE.Bases.back().Members.emplace_back(TypeInfo("int"), "I",
                                               AccessSpecifier::AS_private);
   ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
@@ -468,7 +474,8 @@ class J : public I<int> {} ;)raw",
                                         InfoType::IT_namespace);
   FunctionSetNew.Access = AccessSpecifier::AS_private;
   FunctionSetNew.IsMethod = true;
-  ExpectedH.Bases.back().ChildFunctions.emplace_back(std::move(FunctionSetNew));
+  ExpectedH.Bases.back().Children.Functions.emplace_back(
+      std::move(FunctionSetNew));
   ExpectedH.Bases.emplace_back(EmptySID, /*Name=*/"G",
                                /*Path=*/"GlobalNamespace", true,
                                AccessSpecifier::AS_private, false);
@@ -481,7 +488,8 @@ class J : public I<int> {} ;)raw",
                                         InfoType::IT_namespace);
   FunctionGetNew.Access = AccessSpecifier::AS_private;
   FunctionGetNew.IsMethod = true;
-  ExpectedH.Bases.back().ChildFunctions.emplace_back(std::move(FunctionGetNew));
+  ExpectedH.Bases.back().Children.Functions.emplace_back(
+      std::move(FunctionGetNew));
   ExpectedH.Bases.back().Members.emplace_back(TypeInfo("int"), "I",
                                               AccessSpecifier::AS_private);
   CheckRecordInfo(&ExpectedH, H);
@@ -528,7 +536,7 @@ export double exportedModuleFunction(double y);)raw",
   F.Params.emplace_back(TypeInfo("double"), "d");
   F.Params.back().DefaultValue = "3.2 - 1.0";
   F.Access = AccessSpecifier::AS_none;
-  ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F));
+  ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
   CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
 
   NamespaceInfo *BWithExportedFunction = InfoAsNamespace(Infos[1].get());
@@ -540,7 +548,7 @@ export double exportedModuleFunction(double y);)raw",
   ExportedF.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"});
   ExportedF.Params.emplace_back(TypeInfo("double"), "y");
   ExportedF.Access = AccessSpecifier::AS_none;
-  ExpectedBWithExportedFunction.ChildFunctions.emplace_back(
+  ExpectedBWithExportedFunction.Children.Functions.emplace_back(
       std::move(ExportedF));
   CheckNamespaceInfo(&ExpectedBWithExportedFunction, BWithExportedFunction);
 }
@@ -553,22 +561,22 @@ TEST(SerializeTest, emitChildRecords) {
 
   NamespaceInfo *ParentA = InfoAsNamespace(Infos[1].get());
   NamespaceInfo ExpectedParentA(EmptySID);
-  ExpectedParentA.ChildRecords.emplace_back(EmptySID, "A", InfoType::IT_record,
-                                            "GlobalNamespace");
+  ExpectedParentA.Children.Records.emplace_back(
+      EmptySID, "A", InfoType::IT_record, "GlobalNamespace");
   CheckNamespaceInfo(&ExpectedParentA, ParentA);
 
   RecordInfo *ParentB = InfoAsRecord(Infos[3].get());
   RecordInfo ExpectedParentB(EmptySID);
   llvm::SmallString<128> ExpectedParentBPath("GlobalNamespace/A");
   llvm::sys::path::native(ExpectedParentBPath);
-  ExpectedParentB.ChildRecords.emplace_back(EmptySID, "B", InfoType::IT_record,
-                                            ExpectedParentBPath);
+  ExpectedParentB.Children.Records.emplace_back(
+      EmptySID, "B", InfoType::IT_record, ExpectedParentBPath);
   CheckRecordInfo(&ExpectedParentB, ParentB);
 
   NamespaceInfo *ParentC = InfoAsNamespace(Infos[7].get());
   NamespaceInfo ExpectedParentC(EmptySID);
-  ExpectedParentC.ChildRecords.emplace_back(EmptySID, "C", InfoType::IT_record,
-                                            "@nonymous_namespace");
+  ExpectedParentC.Children.Records.emplace_back(
+      EmptySID, "C", InfoType::IT_record, "@nonymous_namespace");
   CheckNamespaceInfo(&ExpectedParentC, ParentC);
 }
 
@@ -580,16 +588,43 @@ TEST(SerializeTest, emitChildNamespaces) {
 
   NamespaceInfo *ParentA = InfoAsNamespace(Infos[1].get());
   NamespaceInfo ExpectedParentA(EmptySID);
-  ExpectedParentA.ChildNamespaces.emplace_back(EmptySID, "A",
-                                               InfoType::IT_namespace);
+  ExpectedParentA.Children.Namespaces.emplace_back(EmptySID, "A",
+                                                   InfoType::IT_namespace);
   CheckNamespaceInfo(&ExpectedParentA, ParentA);
 
   NamespaceInfo *ParentB = InfoAsNamespace(Infos[3].get());
   NamespaceInfo ExpectedParentB(EmptySID);
-  ExpectedParentB.ChildNamespaces.emplace_back(EmptySID, "B",
-                                               InfoType::IT_namespace, "A");
+  ExpectedParentB.Children.Namespaces.emplace_back(EmptySID, "B",
+                                                   InfoType::IT_namespace, "A");
   CheckNamespaceInfo(&ExpectedParentB, ParentB);
 }
 
+TEST(SerializeTests, emitTypedefs) {
+  EmittedInfoList Infos;
+  ExtractInfosFromCode("typedef int MyInt; using MyDouble = double;", 2,
+                       /*Public=*/false, Infos);
+
+  // First info will be the global namespace with the typedef in it.
+  NamespaceInfo *GlobalNS1 = InfoAsNamespace(Infos[0].get());
+  ASSERT_EQ(1u, GlobalNS1->Children.Typedefs.size());
+
+  const TypedefInfo &FirstTD = GlobalNS1->Children.Typedefs[0];
+  EXPECT_EQ("MyInt", FirstTD.Name);
+  EXPECT_FALSE(FirstTD.IsUsing);
+  EXPECT_EQ("int", FirstTD.Underlying.Type.Name);
+
+  // The second will be another global namespace with the using in it (the
+  // global namespace is duplicated because the items haven't been merged at the
+  // serialization phase of processing).
+  NamespaceInfo *GlobalNS2 = InfoAsNamespace(Infos[1].get());
+  ASSERT_EQ(1u, GlobalNS2->Children.Typedefs.size());
+
+  // Second is the "using" typedef.
+  const TypedefInfo &SecondTD = GlobalNS2->Children.Typedefs[0];
+  EXPECT_EQ("MyDouble", SecondTD.Name);
+  EXPECT_TRUE(SecondTD.IsUsing);
+  EXPECT_EQ("double", SecondTD.Underlying.Type.Name);
+}
+
 } // namespace doc
 } // end namespace clang
index bf59c2c..d6410f2 100644 (file)
@@ -28,15 +28,16 @@ TEST(YAMLGeneratorTest, emitNamespaceYAML) {
   I.Path = "path/to/A";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
-                                 InfoType::IT_namespace, "path/to/A/Namespace");
-  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
-                              "path/to/A/Namespace");
-  I.ChildFunctions.emplace_back();
-  I.ChildFunctions.back().Name = "OneFunction";
-  I.ChildFunctions.back().Access = AccessSpecifier::AS_none;
-  I.ChildEnums.emplace_back();
-  I.ChildEnums.back().Name = "OneEnum";
+  I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
+                                     InfoType::IT_namespace,
+                                     "path/to/A/Namespace");
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+                                  "path/to/A/Namespace");
+  I.Children.Functions.emplace_back();
+  I.Children.Functions.back().Name = "OneFunction";
+  I.Children.Functions.back().Access = AccessSpecifier::AS_none;
+  I.Children.Enums.emplace_back();
+  I.Children.Enums.back().Name = "OneEnum";
 
   auto G = getYAMLGenerator();
   assert(G);
@@ -100,8 +101,8 @@ TEST(YAMLGeneratorTest, emitRecordYAML) {
   I.TagType = TagTypeKind::TTK_Class;
   I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
                        AccessSpecifier::AS_public, true);
-  I.Bases.back().ChildFunctions.emplace_back();
-  I.Bases.back().ChildFunctions.back().Name = "InheritedFunctionOne";
+  I.Bases.back().Children.Functions.emplace_back();
+  I.Bases.back().Children.Functions.back().Name = "InheritedFunctionOne";
   I.Bases.back().Members.emplace_back(TypeInfo("int", "path/to/int"), "N",
                                       AccessSpecifier::AS_private);
   // F is in the global namespace
@@ -109,12 +110,12 @@ TEST(YAMLGeneratorTest, emitRecordYAML) {
   I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
                                 "path/to/G");
 
-  I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
-                              "path/to/A/r");
-  I.ChildFunctions.emplace_back();
-  I.ChildFunctions.back().Name = "OneFunction";
-  I.ChildEnums.emplace_back();
-  I.ChildEnums.back().Name = "OneEnum";
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+                                  "path/to/A/r");
+  I.Children.Functions.emplace_back();
+  I.Children.Functions.back().Name = "OneFunction";
+  I.Children.Enums.emplace_back();
+  I.Children.Enums.back().Name = "OneEnum";
 
   auto G = getYAMLGenerator();
   assert(G);
@@ -330,6 +331,30 @@ Members:
   EXPECT_EQ(Expected, Actual.str());
 }
 
+TEST(YAMLGeneratorTest, enumTypedefYAML) {
+  TypedefInfo I;
+  I.Name = "MyUsing";
+  I.Underlying = TypeInfo("int");
+  I.IsUsing = true;
+
+  auto G = getYAMLGenerator();
+  assert(G);
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+  auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
+  assert(!Err);
+  std::string Expected =
+      R"raw(---
+USR:             '0000000000000000000000000000000000000000'
+Name:            'MyUsing'
+Underlying:
+  Name:            'int'
+IsUsing:         true
+...
+)raw";
+  EXPECT_EQ(Expected, Actual.str());
+}
+
 TEST(YAMLGeneratorTest, emitCommentYAML) {
   FunctionInfo I;
   I.Name = "f";