[PATCH] D66238: [clang-doc] Serialize inherited attributes and methods

2019-08-15 Thread Diego Astiazarán via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL369075: [clang-doc] Serialize inherited attributes and 
methods (authored by DiegoAstiazaran, committed by ).
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

Changed prior to commit:
  https://reviews.llvm.org/D66238?vs=215506&id=215513#toc

Repository:
  rL LLVM

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D66238/new/

https://reviews.llvm.org/D66238

Files:
  clang-tools-extra/trunk/clang-doc/BitcodeReader.cpp
  clang-tools-extra/trunk/clang-doc/BitcodeWriter.cpp
  clang-tools-extra/trunk/clang-doc/BitcodeWriter.h
  clang-tools-extra/trunk/clang-doc/Representation.cpp
  clang-tools-extra/trunk/clang-doc/Representation.h
  clang-tools-extra/trunk/clang-doc/Serialize.cpp
  clang-tools-extra/trunk/clang-doc/YAMLGenerator.cpp
  clang-tools-extra/trunk/unittests/clang-doc/BitcodeTest.cpp
  clang-tools-extra/trunk/unittests/clang-doc/ClangDocTest.cpp
  clang-tools-extra/trunk/unittests/clang-doc/ClangDocTest.h
  clang-tools-extra/trunk/unittests/clang-doc/MergeTest.cpp
  clang-tools-extra/trunk/unittests/clang-doc/SerializeTest.cpp
  clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp

Index: clang-tools-extra/trunk/unittests/clang-doc/ClangDocTest.cpp
===
--- clang-tools-extra/trunk/unittests/clang-doc/ClangDocTest.cpp
+++ clang-tools-extra/trunk/unittests/clang-doc/ClangDocTest.cpp
@@ -6,6 +6,7 @@
 //
 //===--===//
 
+#include "ClangDocTest.h"
 #include "Representation.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "gtest/gtest.h"
@@ -168,6 +169,10 @@
   for (size_t Idx = 0; Idx < Actual->VirtualParents.size(); ++Idx)
 CheckReference(Expected->VirtualParents[Idx], Actual->VirtualParents[Idx]);
 
+  ASSERT_EQ(Expected->Bases.size(), Actual->Bases.size());
+  for (size_t Idx = 0; Idx < Actual->Bases.size(); ++Idx)
+CheckBaseRecordInfo(&Expected->Bases[Idx], &Actual->Bases[Idx]);
+
   ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size());
   for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx)
 CheckReference(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]);
@@ -182,6 +187,14 @@
 CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]);
 }
 
+void CheckBaseRecordInfo(BaseRecordInfo *Expected, BaseRecordInfo *Actual) {
+  CheckRecordInfo(Expected, Actual);
+
+  EXPECT_EQ(Expected->IsVirtual, Actual->IsVirtual);
+  EXPECT_EQ(Expected->Access, Actual->Access);
+  EXPECT_EQ(Expected->IsParent, Actual->IsParent);
+}
+
 void CheckIndex(Index &Expected, Index &Actual) {
   CheckReference(Expected, Actual);
   ASSERT_EQ(Expected.Children.size(), Actual.Children.size());
Index: clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp
===
--- clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp
+++ clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp
@@ -84,6 +84,12 @@
   I.Members.emplace_back("int", "path/to/int", "X",
  AccessSpecifier::AS_private);
   I.TagType = TagTypeKind::TTK_Class;
+  I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
+   AccessSpecifier::AS_public, true);
+  I.Bases.back().ChildFunctions.emplace_back();
+  I.Bases.back().ChildFunctions.back().Name = "InheritedFunctionOne";
+  I.Bases.back().Members.emplace_back("int", "path/to/int", "N",
+  AccessSpecifier::AS_private);
   // F is in the global namespace
   I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "");
   I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
@@ -123,6 +129,24 @@
   Path:'path/to/int'
 Name:'X'
 Access:  Private
+Bases:
+  - USR: ''
+Name:'F'
+Path:'path/to/F'
+Members:
+  - Type:
+  Name:'int'
+  Path:'path/to/int'
+Name:'N'
+Access:  Private
+ChildFunctions:
+  - USR: ''
+Name:'InheritedFunctionOne'
+ReturnType:  {}
+Access:  Public
+IsVirtual:   true
+Access:  Public
+IsParent:true
 Parents:
   - Type:Record
 Name:'F'
Index: clang-tools-extra/trunk/unittests/clang-doc/BitcodeTest.cpp
===
--- clang-tools-extra/trunk/unittests/clang-doc/BitcodeTest.cpp
+++ clang-tools-extra/trunk/unittests/clang-doc/BitcodeTest.cpp
@@ -81,6 +81,10 @@
   I.Members.emplace_back("int", "X", AccessSpecifier::AS_private);
   I.TagType = TagTypeKind::TTK_Class;
   I.IsTy

[PATCH] D66238: [clang-doc] Serialize inherited attributes and methods

2019-08-15 Thread Diego Astiazarán via Phabricator via cfe-commits
DiegoAstiazaran updated this revision to Diff 215506.
DiegoAstiazaran added a comment.

Rebase to master


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D66238/new/

https://reviews.llvm.org/D66238

Files:
  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/Representation.cpp
  clang-tools-extra/clang-doc/Representation.h
  clang-tools-extra/clang-doc/Serialize.cpp
  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/MergeTest.cpp
  clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
  clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp

Index: clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
===
--- clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
+++ clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
@@ -84,6 +84,12 @@
   I.Members.emplace_back("int", "path/to/int", "X",
  AccessSpecifier::AS_private);
   I.TagType = TagTypeKind::TTK_Class;
+  I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
+   AccessSpecifier::AS_public, true);
+  I.Bases.back().ChildFunctions.emplace_back();
+  I.Bases.back().ChildFunctions.back().Name = "InheritedFunctionOne";
+  I.Bases.back().Members.emplace_back("int", "path/to/int", "N",
+  AccessSpecifier::AS_private);
   // F is in the global namespace
   I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "");
   I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
@@ -123,6 +129,24 @@
   Path:'path/to/int'
 Name:'X'
 Access:  Private
+Bases:
+  - USR: ''
+Name:'F'
+Path:'path/to/F'
+Members:
+  - Type:
+  Name:'int'
+  Path:'path/to/int'
+Name:'N'
+Access:  Private
+ChildFunctions:
+  - USR: ''
+Name:'InheritedFunctionOne'
+ReturnType:  {}
+Access:  Public
+IsVirtual:   true
+Access:  Public
+IsParent:true
 Parents:
   - Type:Record
 Name:'F'
Index: clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
===
--- clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
+++ clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
@@ -321,15 +321,16 @@
   CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
 }
 
-TEST(SerializeTest, ) {
+TEST(SerializeTest, emitInheritedRecordInfo) {
   EmittedInfoList Infos;
-  ExtractInfosFromCode(R"raw(class F {};
-class G {} ;
+  ExtractInfosFromCode(R"raw(class F { protected: void set(int N); };
+class G { public: int get() { return 1; } protected: int I; };
 class E : public F, virtual private G {};
+class H : private E {};
 template 
-class H {} ;
-class I : public H {} ;)raw",
-   10, /*Public=*/false, Infos);
+class I {} ;
+class J : public I {} ;)raw",
+   14, /*Public=*/false, Infos);
 
   RecordInfo *F = InfoAsRecord(Infos[0].get());
   RecordInfo ExpectedF(EmptySID, "F");
@@ -337,32 +338,91 @@
   ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
   CheckRecordInfo(&ExpectedF, F);
 
-  RecordInfo *G = InfoAsRecord(Infos[2].get());
+  RecordInfo *G = InfoAsRecord(Infos[3].get());
   RecordInfo ExpectedG(EmptySID, "G");
   ExpectedG.TagType = TagTypeKind::TTK_Class;
   ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedG.Members.emplace_back("int", "I", AccessSpecifier::AS_protected);
   CheckRecordInfo(&ExpectedG, G);
 
-  RecordInfo *E = InfoAsRecord(Infos[4].get());
+  RecordInfo *E = InfoAsRecord(Infos[6].get());
   RecordInfo ExpectedE(EmptySID, "E");
   ExpectedE.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
   ExpectedE.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
+  ExpectedE.Bases.emplace_back(EmptySID, "F", "", false,
+   AccessSpecifier::AS_public, true);
+  FunctionInfo FunctionSet;
+  FunctionSet.Name = "set";
+  FunctionSet.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default);
+  FunctionSet.Loc.emplace_back();
+  FunctionSet.Params.emplace_back("int", "N");
+  FunctionSet.Namespace.emplace_back(EmptySID, "F", InfoType::IT_record);
+  FunctionSet.Access = AccessSpecifier::AS_protected;
+  FunctionSet.IsMethod = true;
+  ExpectedE.Bases.back().ChildFunctions.emplace_back(std::move(FunctionSet));
+  Ex

[PATCH] D66238: [clang-doc] Serialize inherited attributes and methods

2019-08-14 Thread Diego Astiazarán via Phabricator via cfe-commits
DiegoAstiazaran added inline comments.



Comment at: clang-tools-extra/clang-doc/Serialize.cpp:436
+  if (const CXXRecordDecl *Base =
+  cast_or_null(Ty->getDecl()->getDefinition())) {
+bool IsVirtual = false;

juliehockett wrote:
> Will `getDecl()` always return a non-null pointer? In the normal case I'd 
> assume so, but the `cast_or_null` will only catch a null coming out of 
> `getDefinition()` or the cast, not `getDecl`, so just want to check.
I got this logic from 
https://clang.llvm.org/doxygen/CXXInheritance_8cpp_source.html#l00150 so it 
should probably work as you said, `getDecl()` always returns a non-null pointer.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D66238/new/

https://reviews.llvm.org/D66238



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D66238: [clang-doc] Serialize inherited attributes and methods

2019-08-14 Thread Diego Astiazarán via Phabricator via cfe-commits
DiegoAstiazaran updated this revision to Diff 215304.
DiegoAstiazaran marked 11 inline comments as done.
DiegoAstiazaran added a comment.

Increase version number of clang-doc bitcode.
Rename `documentInfo` function to `shouldSerializeInfo`.
In new `parseBases` a BaseRecordInfo object is created and then added to Bases 
of main RecordInfo.
Fix comments.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D66238/new/

https://reviews.llvm.org/D66238

Files:
  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/Representation.cpp
  clang-tools-extra/clang-doc/Representation.h
  clang-tools-extra/clang-doc/Serialize.cpp
  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/MergeTest.cpp
  clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
  clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp

Index: clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
===
--- clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
+++ clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
@@ -84,6 +84,12 @@
   I.Members.emplace_back("int", "path/to/int", "X",
  AccessSpecifier::AS_private);
   I.TagType = TagTypeKind::TTK_Class;
+  I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
+   AccessSpecifier::AS_public, true);
+  I.Bases.back().ChildFunctions.emplace_back();
+  I.Bases.back().ChildFunctions.back().Name = "InheritedFunctionOne";
+  I.Bases.back().Members.emplace_back("int", "path/to/int", "N",
+  AccessSpecifier::AS_private);
   // F is in the global namespace
   I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "");
   I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
@@ -123,6 +129,24 @@
   Path:'path/to/int'
 Name:'X'
 Access:  Private
+Bases:
+  - USR: ''
+Name:'F'
+Path:'path/to/F'
+Members:
+  - Type:
+  Name:'int'
+  Path:'path/to/int'
+Name:'N'
+Access:  Private
+ChildFunctions:
+  - USR: ''
+Name:'InheritedFunctionOne'
+ReturnType:  {}
+Access:  Public
+IsVirtual:   true
+Access:  Public
+IsParent:true
 Parents:
   - Type:Record
 Name:'F'
Index: clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
===
--- clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
+++ clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
@@ -321,15 +321,16 @@
   CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
 }
 
-TEST(SerializeTest, ) {
+TEST(SerializeTest, emitInheritedRecordInfo) {
   EmittedInfoList Infos;
-  ExtractInfosFromCode(R"raw(class F {};
-class G {} ;
+  ExtractInfosFromCode(R"raw(class F { protected: void set(int N); };
+class G { public: int get() { return 1; } protected: int I; };
 class E : public F, virtual private G {};
+class H : private E {};
 template 
-class H {} ;
-class I : public H {} ;)raw",
-   10, /*Public=*/false, Infos);
+class I {} ;
+class J : public I {} ;)raw",
+   14, /*Public=*/false, Infos);
 
   RecordInfo *F = InfoAsRecord(Infos[0].get());
   RecordInfo ExpectedF(EmptySID, "F");
@@ -337,32 +338,91 @@
   ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
   CheckRecordInfo(&ExpectedF, F);
 
-  RecordInfo *G = InfoAsRecord(Infos[2].get());
+  RecordInfo *G = InfoAsRecord(Infos[3].get());
   RecordInfo ExpectedG(EmptySID, "G");
   ExpectedG.TagType = TagTypeKind::TTK_Class;
   ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedG.Members.emplace_back("int", "I", AccessSpecifier::AS_protected);
   CheckRecordInfo(&ExpectedG, G);
 
-  RecordInfo *E = InfoAsRecord(Infos[4].get());
+  RecordInfo *E = InfoAsRecord(Infos[6].get());
   RecordInfo ExpectedE(EmptySID, "E");
   ExpectedE.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
   ExpectedE.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
+  ExpectedE.Bases.emplace_back(EmptySID, "F", "", false,
+   AccessSpecifier::AS_public, true);
+  FunctionInfo FunctionSet;
+  FunctionSet.Name = "set";
+  FunctionSet.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default);
+  FunctionSet.Loc.emplace_back();
+  FunctionSet.Params.emplace_back("int", "

[PATCH] D66238: [clang-doc] Serialize inherited attributes and methods

2019-08-14 Thread Julie Hockett via Phabricator via cfe-commits
juliehockett added inline comments.



Comment at: clang-tools-extra/clang-doc/BitcodeWriter.h:33
 // BitCodeConstants, though they can be added without breaking it.
 static const unsigned VersionNumber = 2;
 

I definitely haven't been particularly good about bumping this, but can you 
bump it? This is a decently large change.



Comment at: clang-tools-extra/clang-doc/Serialize.cpp:233
 
-static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly) {
+static bool documentInfo(bool PublicOnly, bool IsInAnonymousNamespace,
+ const NamedDecl *D) {

s/documentInfo/shouldSerializeInfo



Comment at: clang-tools-extra/clang-doc/Serialize.cpp:277
   for (const FieldDecl *F : D->fields()) {
-if (PublicOnly && !isPublic(F->getAccessUnsafe(), F->getLinkageInternal()))
+if (!documentInfo(PublicOnly, false, F))
   continue;

s/false/`/*IsInAnonymousNamespace=*/false` 



Comment at: clang-tools-extra/clang-doc/Serialize.cpp:436
+  if (const CXXRecordDecl *Base =
+  cast_or_null(Ty->getDecl()->getDefinition())) {
+bool IsVirtual = false;

Will `getDecl()` always return a non-null pointer? In the normal case I'd 
assume so, but the `cast_or_null` will only catch a null coming out of 
`getDefinition()` or the cast, not `getDecl`, so just want to check.



Comment at: clang-tools-extra/clang-doc/Serialize.cpp:437-439
+bool IsVirtual = false;
+if (B.isVirtual())
+  IsVirtual = true;

Is there a reason this isn't `bool IsVirtual = B.IsVirtual()`?



Comment at: clang-tools-extra/clang-doc/Serialize.cpp:440-446
+SymbolID USR = getUSRForDecl(Base);
+std::string BaseName = Base->getNameAsString();
+if (const auto *Ty = B.getType()->getAs()) 
{
+  const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
+  USR = getUSRForDecl(D);
+  BaseName = B.getType().getAsString();
+}

Do this in an if/else -- `getNameAsString()` is a decently expensive operation, 
so only do it when you need it.



Comment at: clang-tools-extra/clang-doc/Serialize.cpp:447-450
+I.Bases.emplace_back(
+USR, BaseName, getInfoRelativePath(Base), IsVirtual,
+getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
+IsParent);

It would be better to create the BaseRecordInfo and then move it into the 
vector, since the constructor will copy all those strings.



Comment at: clang-tools-extra/clang-doc/Serialize.cpp:454
+  if (const auto *MD = dyn_cast(Decl)) {
+// Don't inherit private methods
+if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||

s/inherit/serialize



Comment at: clang-tools-extra/clang-doc/Serialize.cpp:460
+FI.IsMethod = true;
+// The seventh arg in populateParentNamespaces is a boolean
+// passed by reference, its value is not relevant in here so

s/populateParentNamespaces/populateFunctionInfo ?



Comment at: clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp:91
+  I.Bases.back().ChildFunctions.back().Name = "InheritedFunctionOne";
+  I.Bases.back().Members.emplace_back("int", "path/to/int", "X",
+  AccessSpecifier::AS_private);

For clarity, call this something else? Since it already has an `X` private 
member.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D66238/new/

https://reviews.llvm.org/D66238



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D66238: [clang-doc] Serialize inherited attributes and methods

2019-08-14 Thread Diego Astiazarán via Phabricator via cfe-commits
DiegoAstiazaran created this revision.
DiegoAstiazaran added reviewers: juliehockett, jakehehrlich.
DiegoAstiazaran added a project: clang-tools-extra.
Herald added subscribers: kadircet, arphaman.

clang-doc now serializes the inherited attributes and methods, not only the 
name of the base class.
All inherited are tracked, if B:A and C:B, info of A is included in C.
This data is stored in attribute Bases in a RecordInfo.
Previously tracked inheritance data, stored in Parents and VParents, hasn't 
been removed to reduce review load.


https://reviews.llvm.org/D66238

Files:
  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/Representation.cpp
  clang-tools-extra/clang-doc/Representation.h
  clang-tools-extra/clang-doc/Serialize.cpp
  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/MergeTest.cpp
  clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
  clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp

Index: clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
===
--- clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
+++ clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
@@ -84,6 +84,12 @@
   I.Members.emplace_back("int", "path/to/int", "X",
  AccessSpecifier::AS_private);
   I.TagType = TagTypeKind::TTK_Class;
+  I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
+   AccessSpecifier::AS_public, true);
+  I.Bases.back().ChildFunctions.emplace_back();
+  I.Bases.back().ChildFunctions.back().Name = "InheritedFunctionOne";
+  I.Bases.back().Members.emplace_back("int", "path/to/int", "X",
+  AccessSpecifier::AS_private);
   // F is in the global namespace
   I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "");
   I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
@@ -123,6 +129,24 @@
   Path:'path/to/int'
 Name:'X'
 Access:  Private
+Bases:
+  - USR: ''
+Name:'F'
+Path:'path/to/F'
+Members:
+  - Type:
+  Name:'int'
+  Path:'path/to/int'
+Name:'X'
+Access:  Private
+ChildFunctions:
+  - USR: ''
+Name:'InheritedFunctionOne'
+ReturnType:  {}
+Access:  Public
+IsVirtual:   true
+Access:  Public
+IsParent:true
 Parents:
   - Type:Record
 Name:'F'
Index: clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
===
--- clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
+++ clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
@@ -321,15 +321,16 @@
   CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
 }
 
-TEST(SerializeTest, ) {
+TEST(SerializeTest, emitInheritedRecordInfo) {
   EmittedInfoList Infos;
-  ExtractInfosFromCode(R"raw(class F {};
-class G {} ;
+  ExtractInfosFromCode(R"raw(class F { protected: void set(int N); };
+class G { public: int get() { return 1; } protected: int I; };
 class E : public F, virtual private G {};
+class H : private E {};
 template 
-class H {} ;
-class I : public H {} ;)raw",
-   10, /*Public=*/false, Infos);
+class I {} ;
+class J : public I {} ;)raw",
+   14, /*Public=*/false, Infos);
 
   RecordInfo *F = InfoAsRecord(Infos[0].get());
   RecordInfo ExpectedF(EmptySID, "F");
@@ -337,32 +338,91 @@
   ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
   CheckRecordInfo(&ExpectedF, F);
 
-  RecordInfo *G = InfoAsRecord(Infos[2].get());
+  RecordInfo *G = InfoAsRecord(Infos[3].get());
   RecordInfo ExpectedG(EmptySID, "G");
   ExpectedG.TagType = TagTypeKind::TTK_Class;
   ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedG.Members.emplace_back("int", "I", AccessSpecifier::AS_protected);
   CheckRecordInfo(&ExpectedG, G);
 
-  RecordInfo *E = InfoAsRecord(Infos[4].get());
+  RecordInfo *E = InfoAsRecord(Infos[6].get());
   RecordInfo ExpectedE(EmptySID, "E");
   ExpectedE.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
   ExpectedE.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
+  ExpectedE.Bases.emplace_back(EmptySID, "F", "", false,
+   AccessSpecifier::AS_public, true);
+  FunctionInfo FunctionSet;
+  FunctionSet.Name = "set";
+  FunctionSet.ReturnType = TypeInfo(E