https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/135457
>From ca38d210bd3058575752ff9d21232e87a550a943 Mon Sep 17 00:00:00 2001 From: Paul Kirth <paulki...@google.com> Date: Mon, 7 Apr 2025 08:37:40 -0700 Subject: [PATCH 1/9] [clang-doc] Handle static members and functions clang-doc didn't visit VarDecl, and hence never collected info from class statics members and functions. Fixes #59813. --- clang-tools-extra/clang-doc/Mapper.cpp | 4 ++ clang-tools-extra/clang-doc/Mapper.h | 1 + clang-tools-extra/clang-doc/Serialize.cpp | 56 +++++++++++++++++++ clang-tools-extra/clang-doc/Serialize.h | 4 ++ .../test/clang-doc/basic-project.test | 13 ++++- 5 files changed, 77 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp index 6c90db03424c6..98698f9151280 100644 --- a/clang-tools-extra/clang-doc/Mapper.cpp +++ b/clang-tools-extra/clang-doc/Mapper.cpp @@ -82,6 +82,10 @@ bool MapASTVisitor::VisitRecordDecl(const RecordDecl *D) { return mapDecl(D, D->isThisDeclarationADefinition()); } +bool MapASTVisitor::VisitVarDecl(const VarDecl *D) { + return mapDecl(D, D->isThisDeclarationADefinition()); +} + bool MapASTVisitor::VisitEnumDecl(const EnumDecl *D) { return mapDecl(D, D->isThisDeclarationADefinition()); } diff --git a/clang-tools-extra/clang-doc/Mapper.h b/clang-tools-extra/clang-doc/Mapper.h index 75c8e947c8f90..62fc5fbbdf3da 100644 --- a/clang-tools-extra/clang-doc/Mapper.h +++ b/clang-tools-extra/clang-doc/Mapper.h @@ -36,6 +36,7 @@ class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>, void HandleTranslationUnit(ASTContext &Context) override; bool VisitNamespaceDecl(const NamespaceDecl *D); bool VisitRecordDecl(const RecordDecl *D); + bool VisitVarDecl(const VarDecl *D); bool VisitEnumDecl(const EnumDecl *D); bool VisitCXXMethodDecl(const CXXMethodDecl *D); bool VisitFunctionDecl(const FunctionDecl *D); diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index f737fc75135a1..d34451cd10484 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -729,6 +729,62 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, return {std::move(I), std::move(Parent)}; } +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> +emitInfo(const VarDecl *D, const FullComment *FC, int LineNumber, + llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { + auto I = std::make_unique<RecordInfo>(); + bool IsInAnonymousNamespace = false; + populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir, + IsInAnonymousNamespace); + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) + return {}; + + I->Path = getInfoRelativePath(I->Namespace); + + PopulateTemplateParameters(I->Template, D); + + // Full and partial specializations. + if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + if (!I->Template) + I->Template.emplace(); + I->Template->Specialization.emplace(); + auto &Specialization = *I->Template->Specialization; + + // What this is a specialization of. + auto SpecOf = CTSD->getSpecializedTemplateOrPartial(); + if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf)) + Specialization.SpecializationOf = getUSRForDecl(CTD); + else if (auto *CTPSD = + dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf)) + Specialization.SpecializationOf = getUSRForDecl(CTPSD); + + // Parameters to the specilization. For partial specializations, get the + // parameters "as written" from the ClassTemplatePartialSpecializationDecl + // because the non-explicit template parameters will have generated internal + // placeholder names rather than the names the user typed that match the + // template parameters. + if (const ClassTemplatePartialSpecializationDecl *CTPSD = + dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) { + if (const ASTTemplateArgumentListInfo *AsWritten = + CTPSD->getTemplateArgsAsWritten()) { + for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) { + Specialization.Params.emplace_back( + getSourceCode(D, (*AsWritten)[i].getSourceRange())); + } + } + } else { + for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) { + Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg)); + } + } + } + + // 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>> emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { diff --git a/clang-tools-extra/clang-doc/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h index 4e203ca7891ac..41946796f39f6 100644 --- a/clang-tools-extra/clang-doc/Serialize.h +++ b/clang-tools-extra/clang-doc/Serialize.h @@ -52,6 +52,10 @@ 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 VarDecl *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); diff --git a/clang-tools-extra/test/clang-doc/basic-project.test b/clang-tools-extra/test/clang-doc/basic-project.test index 94484b393df59..fb389c62e18a6 100644 --- a/clang-tools-extra/test/clang-doc/basic-project.test +++ b/clang-tools-extra/test/clang-doc/basic-project.test @@ -52,7 +52,15 @@ // JSON-INDEX-NEXT: "Name": "Calculator", // JSON-INDEX-NEXT: "RefType": "record", // JSON-INDEX-NEXT: "Path": "GlobalNamespace", -// JSON-INDEX-NEXT: "Children": [] +// JSON-INDEX-NEXT: "Children": [ +// JSON-INDEX-NEXT: { +// JSON-INDEX-NEXT: "USR": "{{([0-9A-F]{40})}}", +// JSON-INDEX-NEXT: "Name": "static_val", +// JSON-INDEX-NEXT: "RefType": "record", +// JSON-INDEX-NEXT: "Path": "GlobalNamespace/Calculator", +// JSON-INDEX-NEXT: "Children": [] +// JSON-INDEX-NEXT: } +// JSON-INDEX-NEXT: ] // JSON-INDEX-NEXT: }, // JSON-INDEX-NEXT: { // JSON-INDEX-NEXT: "USR": "{{([0-9A-F]{40})}}", @@ -135,6 +143,9 @@ // HTML-CALC: <p> Holds a public value.</p> // HTML-CALC: <div>public int public_val</div> +// HTML-CALC: <h2 id="Records">Records</h2> +// HTML-CALC: <a href="../GlobalNamespace/Calculator/static_val.html">static_val</a> + // HTML-CALC: <h2 id="Functions">Functions</h2> // HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">add</h3> // HTML-CALC: <p>public int add(int a, int b)</p> >From 7fb1bd717de19c29478302239e013a524dfa6d88 Mon Sep 17 00:00:00 2001 From: Paul Kirth <paulki...@google.com> Date: Fri, 11 Apr 2025 22:05:42 -0700 Subject: [PATCH 2/9] Fix typo --- clang-tools-extra/clang-doc/Serialize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index d34451cd10484..34aaa60c4db85 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -702,7 +702,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf)) Specialization.SpecializationOf = getUSRForDecl(CTPSD); - // Parameters to the specilization. For partial specializations, get the + // Parameters to the specialization. For partial specializations, get the // parameters "as written" from the ClassTemplatePartialSpecializationDecl // because the non-explicit template parameters will have generated internal // placeholder names rather than the names the user typed that match the >From 3c37677d51213753d63f191acfc8ddef52b9b1ea Mon Sep 17 00:00:00 2001 From: Paul Kirth <paulki...@google.com> Date: Sat, 12 Apr 2025 09:42:04 -0700 Subject: [PATCH 3/9] Fix file separator in test for Windows --- clang-tools-extra/test/clang-doc/basic-project.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/test/clang-doc/basic-project.test b/clang-tools-extra/test/clang-doc/basic-project.test index fb389c62e18a6..0bd9f0227bd44 100644 --- a/clang-tools-extra/test/clang-doc/basic-project.test +++ b/clang-tools-extra/test/clang-doc/basic-project.test @@ -57,7 +57,7 @@ // JSON-INDEX-NEXT: "USR": "{{([0-9A-F]{40})}}", // JSON-INDEX-NEXT: "Name": "static_val", // JSON-INDEX-NEXT: "RefType": "record", -// JSON-INDEX-NEXT: "Path": "GlobalNamespace/Calculator", +// JSON-INDEX-NEXT: "Path": "GlobalNamespace{{[\/]}}Calculator", // JSON-INDEX-NEXT: "Children": [] // JSON-INDEX-NEXT: } // JSON-INDEX-NEXT: ] >From 9333d967052ae7d8c69368f1b600416748a59b7a Mon Sep 17 00:00:00 2001 From: Paul Kirth <paulki...@google.com> Date: Sat, 12 Apr 2025 10:06:31 -0700 Subject: [PATCH 4/9] Allow for escaped forward slash in Windows paths --- clang-tools-extra/test/clang-doc/basic-project.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/test/clang-doc/basic-project.test b/clang-tools-extra/test/clang-doc/basic-project.test index 0bd9f0227bd44..0262f6d2a01d6 100644 --- a/clang-tools-extra/test/clang-doc/basic-project.test +++ b/clang-tools-extra/test/clang-doc/basic-project.test @@ -57,7 +57,7 @@ // JSON-INDEX-NEXT: "USR": "{{([0-9A-F]{40})}}", // JSON-INDEX-NEXT: "Name": "static_val", // JSON-INDEX-NEXT: "RefType": "record", -// JSON-INDEX-NEXT: "Path": "GlobalNamespace{{[\/]}}Calculator", +// JSON-INDEX-NEXT: "Path": "GlobalNamespace{{[\/]+}}Calculator", // JSON-INDEX-NEXT: "Children": [] // JSON-INDEX-NEXT: } // JSON-INDEX-NEXT: ] >From 487782f63b66d10cb79c73d516f2bcf354f1ab56 Mon Sep 17 00:00:00 2001 From: Paul Kirth <paulki...@google.com> Date: Sat, 12 Apr 2025 10:20:35 -0700 Subject: [PATCH 5/9] Fix path check in MD-CALC --- clang-tools-extra/test/clang-doc/basic-project.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang-tools-extra/test/clang-doc/basic-project.test b/clang-tools-extra/test/clang-doc/basic-project.test index 0262f6d2a01d6..b14624a2b49f7 100644 --- a/clang-tools-extra/test/clang-doc/basic-project.test +++ b/clang-tools-extra/test/clang-doc/basic-project.test @@ -337,6 +337,8 @@ // MD-CALC: Provides basic arithmetic operations. // MD-CALC: ## Members // MD-CALC: public int public_val +// MD-CALC: ## Records +// MD-CALC: static_val // MD-CALC: ## Functions // MD-CALC: ### add // MD-CALC: *public int add(int a, int b)* >From b7daaedac99186dac54560fb401ad159932d5a3e Mon Sep 17 00:00:00 2001 From: Paul Kirth <paulki...@google.com> Date: Sat, 12 Apr 2025 12:54:52 -0700 Subject: [PATCH 6/9] Mark static functions and members in normal processing Visiting VarDecls looses context. Instead, track additional information about statics within the Info* classes, and update serialization to handle it. We can visit VarDecl's as a normal part of visiting Records, and keep the necessary context. --- clang-tools-extra/clang-doc/BitcodeReader.cpp | 4 + clang-tools-extra/clang-doc/BitcodeWriter.cpp | 8 +- clang-tools-extra/clang-doc/BitcodeWriter.h | 2 + clang-tools-extra/clang-doc/HTMLGenerator.cpp | 8 ++ clang-tools-extra/clang-doc/Mapper.cpp | 4 - clang-tools-extra/clang-doc/Mapper.h | 1 - clang-tools-extra/clang-doc/Representation.h | 8 +- clang-tools-extra/clang-doc/Serialize.cpp | 76 +++++-------------- .../test/clang-doc/basic-project.test | 20 ++--- 9 files changed, 49 insertions(+), 82 deletions(-) diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp index 1f2fb0a8b2b85..d6c9da81a05c7 100644 --- a/clang-tools-extra/clang-doc/BitcodeReader.cpp +++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp @@ -274,6 +274,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob, return decodeRecord(R, I->Access, Blob); case FUNCTION_IS_METHOD: return decodeRecord(R, I->IsMethod, Blob); + case FUNCTION_IS_STATIC: + return decodeRecord(R, I->IsStatic, Blob); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for FunctionInfo"); @@ -305,6 +307,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob, return decodeRecord(R, I->Name, Blob); case MEMBER_TYPE_ACCESS: return decodeRecord(R, I->Access, Blob); + case MEMBER_TYPE_IS_STATIC: + return decodeRecord(R, I->IsStatic, Blob); default: return llvm::createStringError(llvm::inconvertibleErrorCode(), "invalid field for MemberTypeInfo"); diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp index 06f30f76e33d8..fbc3f9969dbed 100644 --- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp +++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp @@ -156,6 +156,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor> {FIELD_DEFAULT_VALUE, {"DefaultValue", &StringAbbrev}}, {MEMBER_TYPE_NAME, {"Name", &StringAbbrev}}, {MEMBER_TYPE_ACCESS, {"Access", &IntAbbrev}}, + {MEMBER_TYPE_IS_STATIC, {"IsStatic", &BoolAbbrev}}, {NAMESPACE_USR, {"USR", &SymbolIDAbbrev}}, {NAMESPACE_NAME, {"Name", &StringAbbrev}}, {NAMESPACE_PATH, {"Path", &StringAbbrev}}, @@ -187,6 +188,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor> {FUNCTION_LOCATION, {"Location", &LocationAbbrev}}, {FUNCTION_ACCESS, {"Access", &IntAbbrev}}, {FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}}, + {FUNCTION_IS_STATIC, {"IsStatic", &BoolAbbrev}}, {REFERENCE_USR, {"USR", &SymbolIDAbbrev}}, {REFERENCE_NAME, {"Name", &StringAbbrev}}, {REFERENCE_QUAL_NAME, {"QualName", &StringAbbrev}}, @@ -222,7 +224,7 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>> // FieldType Block {BI_FIELD_TYPE_BLOCK_ID, {FIELD_TYPE_NAME, FIELD_DEFAULT_VALUE}}, // MemberType Block - {BI_MEMBER_TYPE_BLOCK_ID, {MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS}}, + {BI_MEMBER_TYPE_BLOCK_ID, {MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS, MEMBER_TYPE_IS_STATIC}}, // Enum Block {BI_ENUM_BLOCK_ID, {ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_SCOPED}}, @@ -247,7 +249,7 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>> // Function Block {BI_FUNCTION_BLOCK_ID, {FUNCTION_USR, FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION, - FUNCTION_ACCESS, FUNCTION_IS_METHOD}}, + FUNCTION_ACCESS, FUNCTION_IS_METHOD, FUNCTION_IS_STATIC}}, // Reference Block {BI_REFERENCE_BLOCK_ID, {REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE, @@ -465,6 +467,7 @@ void ClangDocBitcodeWriter::emitBlock(const MemberTypeInfo &T) { emitBlock(T.Type, FieldId::F_type); emitRecord(T.Name, MEMBER_TYPE_NAME); emitRecord(T.Access, MEMBER_TYPE_ACCESS); + emitRecord(T.IsStatic, MEMBER_TYPE_IS_STATIC); for (const auto &CI : T.Description) emitBlock(CI); } @@ -600,6 +603,7 @@ void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) { emitBlock(CI); emitRecord(I.Access, FUNCTION_ACCESS); emitRecord(I.IsMethod, FUNCTION_IS_METHOD); + emitRecord(I.IsStatic, FUNCTION_IS_STATIC); if (I.DefLoc) emitRecord(*I.DefLoc, FUNCTION_DEFLOCATION); for (const auto &L : I.Loc) diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h index 9a572e40e352f..e33a1aece883c 100644 --- a/clang-tools-extra/clang-doc/BitcodeWriter.h +++ b/clang-tools-extra/clang-doc/BitcodeWriter.h @@ -81,6 +81,7 @@ enum RecordId { FUNCTION_LOCATION, FUNCTION_ACCESS, FUNCTION_IS_METHOD, + FUNCTION_IS_STATIC, COMMENT_KIND, COMMENT_TEXT, COMMENT_NAME, @@ -96,6 +97,7 @@ enum RecordId { FIELD_DEFAULT_VALUE, MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS, + MEMBER_TYPE_IS_STATIC, NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH, diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp index cb10f16804024..1bc3265c1fe89 100644 --- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -419,6 +419,8 @@ genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members, std::string Access = getAccessSpelling(M.Access).str(); if (Access != "") Access = Access + " "; + if(M.IsStatic) + Access += "static "; auto LIBody = std::make_unique<TagNode>(HTMLTag::TAG_LI); auto MemberDecl = std::make_unique<TagNode>(HTMLTag::TAG_DIV); MemberDecl->Children.emplace_back(std::make_unique<TextNode>(Access)); @@ -740,6 +742,12 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx, if (Access != "") FunctionHeader->Children.emplace_back( std::make_unique<TextNode>(Access + " ")); + if(I.IsStatic) + FunctionHeader->Children.emplace_back( + std::make_unique<TextNode>("static ")); + else{ + llvm::errs() << I.Name << " is not static\n"; + } if (I.ReturnType.Type.Name != "") { FunctionHeader->Children.emplace_back( genReference(I.ReturnType.Type, ParentInfoDir)); diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp index 98698f9151280..6c90db03424c6 100644 --- a/clang-tools-extra/clang-doc/Mapper.cpp +++ b/clang-tools-extra/clang-doc/Mapper.cpp @@ -82,10 +82,6 @@ bool MapASTVisitor::VisitRecordDecl(const RecordDecl *D) { return mapDecl(D, D->isThisDeclarationADefinition()); } -bool MapASTVisitor::VisitVarDecl(const VarDecl *D) { - return mapDecl(D, D->isThisDeclarationADefinition()); -} - bool MapASTVisitor::VisitEnumDecl(const EnumDecl *D) { return mapDecl(D, D->isThisDeclarationADefinition()); } diff --git a/clang-tools-extra/clang-doc/Mapper.h b/clang-tools-extra/clang-doc/Mapper.h index 62fc5fbbdf3da..75c8e947c8f90 100644 --- a/clang-tools-extra/clang-doc/Mapper.h +++ b/clang-tools-extra/clang-doc/Mapper.h @@ -36,7 +36,6 @@ class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>, void HandleTranslationUnit(ASTContext &Context) override; bool VisitNamespaceDecl(const NamespaceDecl *D); bool VisitRecordDecl(const RecordDecl *D); - bool VisitVarDecl(const VarDecl *D); bool VisitEnumDecl(const EnumDecl *D); bool VisitCXXMethodDecl(const CXXMethodDecl *D); bool VisitFunctionDecl(const FunctionDecl *D); diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index 2153b62864ee3..dc95c6295f900 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -224,8 +224,9 @@ struct MemberTypeInfo : public FieldTypeInfo { : FieldTypeInfo(TI, Name), Access(Access) {} bool operator==(const MemberTypeInfo &Other) const { - return std::tie(Type, Name, Access, Description) == - std::tie(Other.Type, Other.Name, Other.Access, Other.Description); + return std::tie(Type, Name, Access, IsStatic, Description) == + std::tie(Other.Type, Other.Name, Other.Access, Other.IsStatic, + Other.Description); } // Access level associated with this info (public, protected, private, none). @@ -235,6 +236,7 @@ struct MemberTypeInfo : public FieldTypeInfo { AccessSpecifier Access = AccessSpecifier::AS_public; std::vector<CommentInfo> Description; // Comment description of this field. + bool IsStatic = false; }; struct Location { @@ -314,6 +316,8 @@ struct NamespaceInfo : public Info { // Info for symbols. struct SymbolInfo : public Info { + bool IsStatic = false; + SymbolInfo(InfoType IT, SymbolID USR = SymbolID(), StringRef Name = StringRef(), StringRef Path = StringRef()) : Info(IT, USR, Name, Path) {} diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 34aaa60c4db85..429f7de5a7202 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -30,7 +30,7 @@ static void populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, const T *D, bool &IsAnonymousNamespace); -static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D); +static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *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. @@ -375,11 +375,11 @@ static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS, // record, the access specification of the field depends on the inheritance mode static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, AccessSpecifier Access = AccessSpecifier::AS_public) { + auto &LO = D->getLangOpts(); for (const FieldDecl *F : D->fields()) { if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F)) continue; - auto &LO = F->getLangOpts(); // Use getAccessUnsafe so that we just get the default AS_none if it's not // valid, as opposed to an assert. MemberTypeInfo &NewMember = I.Members.emplace_back( @@ -388,6 +388,19 @@ static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, getFinalAccessSpecifier(Access, F->getAccessUnsafe())); populateMemberTypeInfo(NewMember, F); } + const CXXRecordDecl *CPP = dyn_cast<CXXRecordDecl>(D); + for (Decl *F : CPP->decls()) { + if (auto *VD = dyn_cast<VarDecl>(F)) { + if (VD->isStaticDataMember()) { + MemberTypeInfo &NewMember = I.Members.emplace_back( + getTypeInfoForType(VD->getTypeSourceInfo()->getType(), LO), + VD->getNameAsString(), + getFinalAccessSpecifier(Access, F->getAccessUnsafe())); + NewMember.IsStatic = true; + populateMemberTypeInfo(NewMember, VD); + } + } + } } static void parseEnumerators(EnumInfo &I, const EnumDecl *D) { @@ -568,7 +581,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, } } -static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) { +static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) { assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo"); ASTContext& Context = D->getASTContext(); @@ -619,6 +632,7 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, continue; FunctionInfo FI; FI.IsMethod = true; + FI.IsStatic = MD->isStatic(); // The seventh arg in populateFunctionInfo is a boolean passed by // reference, its value is not relevant in here so it's not used // anywhere besides the function call. @@ -729,61 +743,6 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, return {std::move(I), std::move(Parent)}; } -std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> -emitInfo(const VarDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { - auto I = std::make_unique<RecordInfo>(); - bool IsInAnonymousNamespace = false; - populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir, - IsInAnonymousNamespace); - if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) - return {}; - - I->Path = getInfoRelativePath(I->Namespace); - - PopulateTemplateParameters(I->Template, D); - - // Full and partial specializations. - if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) { - if (!I->Template) - I->Template.emplace(); - I->Template->Specialization.emplace(); - auto &Specialization = *I->Template->Specialization; - - // What this is a specialization of. - auto SpecOf = CTSD->getSpecializedTemplateOrPartial(); - if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf)) - Specialization.SpecializationOf = getUSRForDecl(CTD); - else if (auto *CTPSD = - dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf)) - Specialization.SpecializationOf = getUSRForDecl(CTPSD); - - // Parameters to the specilization. For partial specializations, get the - // parameters "as written" from the ClassTemplatePartialSpecializationDecl - // because the non-explicit template parameters will have generated internal - // placeholder names rather than the names the user typed that match the - // template parameters. - if (const ClassTemplatePartialSpecializationDecl *CTPSD = - dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) { - if (const ASTTemplateArgumentListInfo *AsWritten = - CTPSD->getTemplateArgsAsWritten()) { - for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) { - Specialization.Params.emplace_back( - getSourceCode(D, (*AsWritten)[i].getSourceRange())); - } - } - } else { - for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) { - Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg)); - } - } - } - - // 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>> emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, @@ -811,6 +770,7 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, return {}; Func.IsMethod = true; + Func.IsStatic = D->isStatic(); const NamedDecl *Parent = nullptr; if (const auto *SD = diff --git a/clang-tools-extra/test/clang-doc/basic-project.test b/clang-tools-extra/test/clang-doc/basic-project.test index b14624a2b49f7..e14fa81a58b88 100644 --- a/clang-tools-extra/test/clang-doc/basic-project.test +++ b/clang-tools-extra/test/clang-doc/basic-project.test @@ -52,15 +52,7 @@ // JSON-INDEX-NEXT: "Name": "Calculator", // JSON-INDEX-NEXT: "RefType": "record", // JSON-INDEX-NEXT: "Path": "GlobalNamespace", -// JSON-INDEX-NEXT: "Children": [ -// JSON-INDEX-NEXT: { -// JSON-INDEX-NEXT: "USR": "{{([0-9A-F]{40})}}", -// JSON-INDEX-NEXT: "Name": "static_val", -// JSON-INDEX-NEXT: "RefType": "record", -// JSON-INDEX-NEXT: "Path": "GlobalNamespace{{[\/]+}}Calculator", -// JSON-INDEX-NEXT: "Children": [] -// JSON-INDEX-NEXT: } -// JSON-INDEX-NEXT: ] +// JSON-INDEX-NEXT: "Children": [] // JSON-INDEX-NEXT: }, // JSON-INDEX-NEXT: { // JSON-INDEX-NEXT: "USR": "{{([0-9A-F]{40})}}", @@ -142,9 +134,9 @@ // HTML-CALC: <div>brief</div> // HTML-CALC: <p> Holds a public value.</p> // HTML-CALC: <div>public int public_val</div> - -// HTML-CALC: <h2 id="Records">Records</h2> -// HTML-CALC: <a href="../GlobalNamespace/Calculator/static_val.html">static_val</a> +// HTML-CALC: <div>brief</div> +// HTML-CALC: <p> A static value.</p> +// HTML-CALC: <div>public static const int static_val</div> // HTML-CALC: <h2 id="Functions">Functions</h2> // HTML-CALC: <h3 id="{{([0-9A-F]{40})}}">add</h3> @@ -202,7 +194,7 @@ // HTML-CALC: <div>throw</div> // HTML-CALC: <p>if b is zero.</p> -// HTML-CALC: <p>public int mod(int a, int b)</p> +// HTML-CALC: <p>public static int mod(int a, int b)</p> // CALC-NO-REPOSITORY: Defined at line 54 of file .{{.}}include{{.}}Calculator.h // CALC-REPOSITORY: Defined at line // CALC-REPOSITORY-NEXT: <a href="https://repository.com/./include/Calculator.h#54">54</a> @@ -337,8 +329,6 @@ // MD-CALC: Provides basic arithmetic operations. // MD-CALC: ## Members // MD-CALC: public int public_val -// MD-CALC: ## Records -// MD-CALC: static_val // MD-CALC: ## Functions // MD-CALC: ### add // MD-CALC: *public int add(int a, int b)* >From fc820045ae33e2a86eebb2f0e513dd11803b5d02 Mon Sep 17 00:00:00 2001 From: Paul Kirth <paulki...@google.com> Date: Sat, 12 Apr 2025 13:03:20 -0700 Subject: [PATCH 7/9] fix formatting --- clang-tools-extra/clang-doc/BitcodeWriter.cpp | 3 ++- clang-tools-extra/clang-doc/HTMLGenerator.cpp | 6 +++--- clang-tools-extra/clang-doc/Serialize.cpp | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp index fbc3f9969dbed..621af4e51e443 100644 --- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp +++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp @@ -224,7 +224,8 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>> // FieldType Block {BI_FIELD_TYPE_BLOCK_ID, {FIELD_TYPE_NAME, FIELD_DEFAULT_VALUE}}, // MemberType Block - {BI_MEMBER_TYPE_BLOCK_ID, {MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS, MEMBER_TYPE_IS_STATIC}}, + {BI_MEMBER_TYPE_BLOCK_ID, + {MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS, MEMBER_TYPE_IS_STATIC}}, // Enum Block {BI_ENUM_BLOCK_ID, {ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_SCOPED}}, diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp index 1bc3265c1fe89..e54c3f8a4ed42 100644 --- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -419,7 +419,7 @@ genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members, std::string Access = getAccessSpelling(M.Access).str(); if (Access != "") Access = Access + " "; - if(M.IsStatic) + if (M.IsStatic) Access += "static "; auto LIBody = std::make_unique<TagNode>(HTMLTag::TAG_LI); auto MemberDecl = std::make_unique<TagNode>(HTMLTag::TAG_DIV); @@ -742,10 +742,10 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx, if (Access != "") FunctionHeader->Children.emplace_back( std::make_unique<TextNode>(Access + " ")); - if(I.IsStatic) + if (I.IsStatic) FunctionHeader->Children.emplace_back( std::make_unique<TextNode>("static ")); - else{ + else { llvm::errs() << I.Name << " is not static\n"; } if (I.ReturnType.Type.Name != "") { diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 429f7de5a7202..727e2ed6e93d9 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -743,7 +743,6 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, return {std::move(I), std::move(Parent)}; } - std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { >From e97e6f2c6bae585f642749e23cf581f14758bd18 Mon Sep 17 00:00:00 2001 From: Paul Kirth <paulki...@google.com> Date: Sat, 12 Apr 2025 13:59:19 -0700 Subject: [PATCH 8/9] Introduce helper function and remove debugging code --- clang-tools-extra/clang-doc/HTMLGenerator.cpp | 3 -- clang-tools-extra/clang-doc/Representation.h | 5 +- clang-tools-extra/clang-doc/Serialize.cpp | 53 +++++++++++-------- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp index e54c3f8a4ed42..0fc1d74d806d5 100644 --- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -745,9 +745,6 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx, if (I.IsStatic) FunctionHeader->Children.emplace_back( std::make_unique<TextNode>("static ")); - else { - llvm::errs() << I.Name << " is not static\n"; - } if (I.ReturnType.Type.Name != "") { FunctionHeader->Children.emplace_back( genReference(I.ReturnType.Type, ParentInfoDir)); diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index dc95c6295f900..d87a13f8ff119 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -220,8 +220,9 @@ struct FieldTypeInfo : public TypeInfo { // Info for member types. struct MemberTypeInfo : public FieldTypeInfo { MemberTypeInfo() = default; - MemberTypeInfo(const TypeInfo &TI, StringRef Name, AccessSpecifier Access) - : FieldTypeInfo(TI, Name), Access(Access) {} + MemberTypeInfo(const TypeInfo &TI, StringRef Name, AccessSpecifier Access, + bool IsStatic = false) + : FieldTypeInfo(TI, Name), Access(Access), IsStatic(IsStatic) {} bool operator==(const MemberTypeInfo &Other) const { return std::tie(Type, Name, Access, IsStatic, Description) == diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 727e2ed6e93d9..496c8674c1ae2 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -31,6 +31,10 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, const T *D, bool &IsAnonymousNamespace); static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D); +static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access, + const LangOptions &LO, + const DeclaratorDecl *D, + bool IsStatic = false); // A function to extract the appropriate relative path for a given info's // documentation. The path returned is a composite of the parent namespaces. @@ -371,35 +375,25 @@ static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS, return AccessSpecifier::AS_public; } -// The Access parameter is only provided when parsing the field of an inherited -// record, the access specification of the field depends on the inheritance mode static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, AccessSpecifier Access = AccessSpecifier::AS_public) { auto &LO = D->getLangOpts(); for (const FieldDecl *F : D->fields()) { if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F)) continue; - - // Use getAccessUnsafe so that we just get the default AS_none if it's not - // valid, as opposed to an assert. - MemberTypeInfo &NewMember = I.Members.emplace_back( - getTypeInfoForType(F->getTypeSourceInfo()->getType(), LO), - F->getNameAsString(), - getFinalAccessSpecifier(Access, F->getAccessUnsafe())); - populateMemberTypeInfo(NewMember, F); + populateMemberTypeInfo(I, Access, LO, F); } - const CXXRecordDecl *CPP = dyn_cast<CXXRecordDecl>(D); - for (Decl *F : CPP->decls()) { - if (auto *VD = dyn_cast<VarDecl>(F)) { - if (VD->isStaticDataMember()) { - MemberTypeInfo &NewMember = I.Members.emplace_back( - getTypeInfoForType(VD->getTypeSourceInfo()->getType(), LO), - VD->getNameAsString(), - getFinalAccessSpecifier(Access, F->getAccessUnsafe())); - NewMember.IsStatic = true; - populateMemberTypeInfo(NewMember, VD); - } - } + const auto *CxxRD = dyn_cast<CXXRecordDecl>(D); + if (!CxxRD) + return; + for (Decl *CxxDecl : CxxRD->decls()) { + auto *VD = dyn_cast<VarDecl>(CxxDecl); + if (!VD || + !shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, VD)) + continue; + + if (VD->isStaticDataMember()) + populateMemberTypeInfo(I, Access, LO, VD, /*IsStatic=*/true); } } @@ -598,6 +592,21 @@ static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) { } } +// The Access parameter is only provided when parsing the field of an inherited +// record, the access specification of the field depends on the inheritance mode +static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access, + const LangOptions &LO, + const DeclaratorDecl *D, + bool IsStatic) { + // Use getAccessUnsafe so that we just get the default AS_none if it's not + // valid, as opposed to an assert. + MemberTypeInfo &NewMember = I.Members.emplace_back( + getTypeInfoForType(D->getTypeSourceInfo()->getType(), LO), + D->getNameAsString(), + getFinalAccessSpecifier(Access, D->getAccessUnsafe()), IsStatic); + populateMemberTypeInfo(NewMember, D); +} + static void parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, bool PublicOnly, bool IsParent, >From 0a2e3fa4da9c7c38a894657afeec454060c629d8 Mon Sep 17 00:00:00 2001 From: Paul Kirth <paulki...@google.com> Date: Sat, 12 Apr 2025 15:29:08 -0700 Subject: [PATCH 9/9] Handle missing MD case --- clang-tools-extra/clang-doc/MDGenerator.cpp | 24 +++++++++---------- clang-tools-extra/clang-doc/Serialize.cpp | 12 ++++------ .../test/clang-doc/basic-project.test | 3 ++- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp index 5c782fcc10da5..641119ba1d2bd 100644 --- a/clang-tools-extra/clang-doc/MDGenerator.cpp +++ b/clang-tools-extra/clang-doc/MDGenerator.cpp @@ -170,14 +170,13 @@ static void genMarkdown(const ClangDocContext &CDCtx, const FunctionInfo &I, } writeHeader(I.Name, 3, OS); std::string Access = getAccessSpelling(I.Access).str(); - if (Access != "") - writeLine(genItalic(Access + " " + I.ReturnType.Type.QualName + " " + - I.Name + "(" + Stream.str() + ")"), - OS); - else - writeLine(genItalic(I.ReturnType.Type.QualName + " " + I.Name + "(" + - Stream.str() + ")"), - OS); + if (!Access.empty()) + Access += " "; + if (I.IsStatic) + Access += "static "; + writeLine(genItalic(Access + I.ReturnType.Type.QualName + " " + I.Name + "(" + + Stream.str() + ")"), + OS); maybeWriteSourceFileRef(OS, CDCtx, I.DefLoc); @@ -263,10 +262,11 @@ static void genMarkdown(const ClangDocContext &CDCtx, const RecordInfo &I, writeHeader("Members", 2, OS); for (const auto &Member : I.Members) { std::string Access = getAccessSpelling(Member.Access).str(); - if (Access != "") - writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS); - else - writeLine(Member.Type.Name + " " + Member.Name, OS); + if (!Access.empty()) + Access += " "; + if (Member.IsStatic) + Access += "static "; + writeLine(Access + Member.Type.Name + " " + Member.Name, OS); } writeNewLine(OS); } diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 496c8674c1ae2..4ee5416cd0ef5 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -32,7 +32,6 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D); static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access, - const LangOptions &LO, const DeclaratorDecl *D, bool IsStatic = false); @@ -377,11 +376,10 @@ static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS, static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, AccessSpecifier Access = AccessSpecifier::AS_public) { - auto &LO = D->getLangOpts(); for (const FieldDecl *F : D->fields()) { if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F)) continue; - populateMemberTypeInfo(I, Access, LO, F); + populateMemberTypeInfo(I, Access, F); } const auto *CxxRD = dyn_cast<CXXRecordDecl>(D); if (!CxxRD) @@ -393,7 +391,7 @@ static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, continue; if (VD->isStaticDataMember()) - populateMemberTypeInfo(I, Access, LO, VD, /*IsStatic=*/true); + populateMemberTypeInfo(I, Access, VD, /*IsStatic=*/true); } } @@ -595,13 +593,11 @@ static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) { // The Access parameter is only provided when parsing the field of an inherited // record, the access specification of the field depends on the inheritance mode static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access, - const LangOptions &LO, - const DeclaratorDecl *D, - bool IsStatic) { + const DeclaratorDecl *D, bool IsStatic) { // Use getAccessUnsafe so that we just get the default AS_none if it's not // valid, as opposed to an assert. MemberTypeInfo &NewMember = I.Members.emplace_back( - getTypeInfoForType(D->getTypeSourceInfo()->getType(), LO), + getTypeInfoForType(D->getTypeSourceInfo()->getType(), D->getLangOpts()), D->getNameAsString(), getFinalAccessSpecifier(Access, D->getAccessUnsafe()), IsStatic); populateMemberTypeInfo(NewMember, D); diff --git a/clang-tools-extra/test/clang-doc/basic-project.test b/clang-tools-extra/test/clang-doc/basic-project.test index e14fa81a58b88..9c1ed29973d79 100644 --- a/clang-tools-extra/test/clang-doc/basic-project.test +++ b/clang-tools-extra/test/clang-doc/basic-project.test @@ -329,6 +329,7 @@ // MD-CALC: Provides basic arithmetic operations. // MD-CALC: ## Members // MD-CALC: public int public_val +// MD-CALC: public static const int static_val // MD-CALC: ## Functions // MD-CALC: ### add // MD-CALC: *public int add(int a, int b)* @@ -360,7 +361,7 @@ // MD-CALC: **return** double The result of a / b. // MD-CALC: **throw**if b is zero. // MD-CALC: ### mod -// MD-CALC: *public int mod(int a, int b)* +// MD-CALC: *public static int mod(int a, int b)* // MD-CALC: *Defined at ./include{{[\/]}}Calculator.h#54* // MD-CALC: **brief** Performs the mod operation on integers. // MD-CALC: **a** First integer. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits