diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp --- a/clang-tools-extra/clang-doc/BitcodeReader.cpp +++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp @@ -350,6 +350,11 @@ return &I->Description.back(); } +template <> llvm::Expected getCommentInfo(MemberTypeInfo *I) { + I->Description.emplace_back(); + return &I->Description.back(); +} + template <> llvm::Expected getCommentInfo(EnumInfo *I) { I->Description.emplace_back(); return &I->Description.back(); diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp --- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp +++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp @@ -426,6 +426,8 @@ emitBlock(T.Type, FieldId::F_type); emitRecord(T.Name, MEMBER_TYPE_NAME); emitRecord(T.Access, MEMBER_TYPE_ACCESS); + for (const auto &CI : T.Description) + emitBlock(CI); } void ClangDocBitcodeWriter::emitBlock(const CommentInfo &I) { diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -201,8 +201,8 @@ : FieldTypeInfo(RefName, Path, Name), Access(Access) {} bool operator==(const MemberTypeInfo &Other) const { - return std::tie(Type, Name, Access) == - std::tie(Other.Type, Other.Name, Other.Access); + return std::tie(Type, Name, Access, Description) == + std::tie(Other.Type, Other.Name, Other.Access, Other.Description); } // Access level associated with this info (public, protected, private, none). @@ -210,6 +210,8 @@ // with value 0 to be used as the default. // (AS_public = 0, AS_protected = 1, AS_private = 2, AS_none = 3) AccessSpecifier Access = AccessSpecifier::AS_public; + + std::vector Description; // Comment description of this field. }; struct Location { diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -29,6 +29,8 @@ populateParentNamespaces(llvm::SmallVector &Namespaces, const T *D, bool &IsAnonymousNamespace); +static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D); + // A function to extract the appropriate relative path for a given info's // documentation. The path returned is a composite of the parent namespaces. // @@ -293,9 +295,11 @@ continue; } } - I.Members.emplace_back( + + auto& member = I.Members.emplace_back( F->getTypeSourceInfo()->getType().getAsString(), F->getNameAsString(), getFinalAccessSpecifier(Access, F->getAccessUnsafe())); + populateMemberTypeInfo(member, F); } } @@ -431,6 +435,23 @@ parseParameters(I, D); } +static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) { + assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo"); + + ASTContext& Context = D->getASTContext(); + // TODO investigate whether we can use ASTContext::getCommentForDecl instead + // of this logic. See also similar code in Mapper.cpp. + RawComment *Comment = Context.getRawCommentForDeclNoCache(D); + if (!Comment) + return; + + Comment->setAttached(); + if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) { + I.Description.emplace_back(); + parseFullComment(fc, I.Description.back()); + } +} + static void parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, bool PublicOnly, bool IsParent, diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp --- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp @@ -192,6 +192,7 @@ // the AS that shouldn't be part of the output. Even though AS_public is the // default in the struct, it should be displayed in the YAML output. IO.mapOptional("Access", I.Access, clang::AccessSpecifier::AS_none); + IO.mapOptional("Description", I.Description); } }; diff --git a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp --- a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp @@ -88,6 +88,18 @@ I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record); I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); + // Documentation for the data member. + CommentInfo TopComment; + TopComment.Kind = "FullComment"; + TopComment.Children.emplace_back(std::make_unique()); + CommentInfo *Brief = TopComment.Children.back().get(); + Brief->Kind = "ParagraphComment"; + Brief->Children.emplace_back(std::make_unique()); + Brief->Children.back()->Kind = "TextComment"; + Brief->Children.back()->Name = "ParagraphComment"; + 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(); diff --git a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp --- a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp @@ -34,7 +34,12 @@ return static_cast(I); } -void CheckCommentInfo(CommentInfo &Expected, CommentInfo &Actual) { +void CheckCommentInfo(const std::vector &Expected, + const std::vector &Actual); +void CheckCommentInfo(const std::vector> &Expected, + const std::vector> &Actual); + +void CheckCommentInfo(const CommentInfo &Expected, const CommentInfo &Actual) { EXPECT_EQ(Expected.Kind, Actual.Kind); EXPECT_EQ(Expected.Text, Actual.Text); EXPECT_EQ(Expected.Name, Actual.Name); @@ -56,9 +61,21 @@ for (size_t Idx = 0; Idx < Actual.Args.size(); ++Idx) EXPECT_EQ(Expected.Args[Idx], Actual.Args[Idx]); - ASSERT_EQ(Expected.Children.size(), Actual.Children.size()); - for (size_t Idx = 0; Idx < Actual.Children.size(); ++Idx) - CheckCommentInfo(*Expected.Children[Idx], *Actual.Children[Idx]); + CheckCommentInfo(Expected.Children, Actual.Children); +} + +void CheckCommentInfo(const std::vector &Expected, + const std::vector &Actual) { + ASSERT_EQ(Expected.size(), Actual.size()); + for (size_t Idx = 0; Idx < Actual.size(); ++Idx) + CheckCommentInfo(Expected[Idx], Actual[Idx]); +} + +void CheckCommentInfo(const std::vector> &Expected, + const std::vector> &Actual) { + ASSERT_EQ(Expected.size(), Actual.size()); + for (size_t Idx = 0; Idx < Actual.size(); ++Idx) + CheckCommentInfo(*Expected[Idx], *Actual[Idx]); } void CheckReference(Reference &Expected, Reference &Actual) { @@ -79,6 +96,7 @@ void CheckMemberTypeInfo(MemberTypeInfo *Expected, MemberTypeInfo *Actual) { CheckFieldTypeInfo(Expected, Actual); EXPECT_EQ(Expected->Access, Actual->Access); + CheckCommentInfo(Expected->Description, Actual->Description); } void CheckBaseInfo(Info *Expected, Info *Actual) { @@ -88,9 +106,7 @@ ASSERT_EQ(Expected->Namespace.size(), Actual->Namespace.size()); for (size_t Idx = 0; Idx < Actual->Namespace.size(); ++Idx) CheckReference(Expected->Namespace[Idx], Actual->Namespace[Idx]); - ASSERT_EQ(Expected->Description.size(), Actual->Description.size()); - for (size_t Idx = 0; Idx < Actual->Description.size(); ++Idx) - CheckCommentInfo(Expected->Description[Idx], Actual->Description[Idx]); + CheckCommentInfo(Expected->Description, Actual->Description); } void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual) { diff --git a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp --- a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp @@ -80,6 +80,26 @@ ASSERT_EQ(NumExpectedInfos, EmittedInfos.size()); } +// Constructs a comment definition as the parser would for one comment line. +/* TODO uncomment this when the missing comment is fixed in emitRecordInfo and + the code that calls this is re-enabled. +CommentInfo MakeOneLineCommentInfo(const std::string &Text) { + CommentInfo TopComment; + TopComment.Kind = "FullComment"; + TopComment.Children.emplace_back(std::make_unique()); + + CommentInfo *Brief = TopComment.Children.back().get(); + Brief->Kind = "ParagraphComment"; + + Brief->Children.emplace_back(std::make_unique()); + Brief->Children.back()->Kind = "TextComment"; + Brief->Children.back()->Name = "ParagraphComment"; + Brief->Children.back()->Text = Text; + + return TopComment; +} +*/ + // Test serialization of namespace declarations. TEST(SerializeTest, emitNamespaceInfo) { EmittedInfoList Infos; @@ -124,6 +144,9 @@ ExtractInfosFromCode(R"raw(class E { public: E() {} + + // Some docs. + int value; protected: void ProtectedMethod(); }; @@ -142,6 +165,9 @@ InfoType::IT_namespace); ExpectedE.TagType = TagTypeKind::TTK_Class; ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedE.Members.emplace_back("int", "value", AccessSpecifier::AS_public); + // TODO the data member should have the docstring on it: + //ExpectedE.Members.back().Description.push_back(MakeOneLineCommentInfo(" Some docs")); CheckRecordInfo(&ExpectedE, E); RecordInfo *RecordWithEConstructor = InfoAsRecord(Infos[2].get()); diff --git a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp --- a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp @@ -83,6 +83,19 @@ I.Members.emplace_back("int", "path/to/int", "X", AccessSpecifier::AS_private); + + // Member documentation. + CommentInfo TopComment; + TopComment.Kind = "FullComment"; + TopComment.Children.emplace_back(std::make_unique()); + CommentInfo *Brief = TopComment.Children.back().get(); + Brief->Kind = "ParagraphComment"; + Brief->Children.emplace_back(std::make_unique()); + Brief->Children.back()->Kind = "TextComment"; + Brief->Children.back()->Name = "ParagraphComment"; + Brief->Children.back()->Text = "Value of the thing."; + I.Members.back().Description.push_back(std::move(TopComment)); + I.TagType = TagTypeKind::TTK_Class; I.Bases.emplace_back(EmptySID, "F", "path/to/F", true, AccessSpecifier::AS_public, true); @@ -129,6 +142,14 @@ Path: 'path/to/int' Name: 'X' Access: Private + Description: + - Kind: 'FullComment' + Children: + - Kind: 'ParagraphComment' + Children: + - Kind: 'TextComment' + Text: 'Value of the thing.' + Name: 'ParagraphComment' Bases: - USR: '0000000000000000000000000000000000000000' Name: 'F'