[clang-tools-extra] [clang-doc] Handle static members and functions (PR #135457)
Paul Kirth via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 12 15:48:20 PDT 2025
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 <paulkirth at google.com>
Date: Mon, 7 Apr 2025 08:37:40 -0700
Subject: [PATCH 01/11] [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 <paulkirth at google.com>
Date: Fri, 11 Apr 2025 22:05:42 -0700
Subject: [PATCH 02/11] 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 <paulkirth at google.com>
Date: Sat, 12 Apr 2025 09:42:04 -0700
Subject: [PATCH 03/11] 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 <paulkirth at google.com>
Date: Sat, 12 Apr 2025 10:06:31 -0700
Subject: [PATCH 04/11] 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 <paulkirth at google.com>
Date: Sat, 12 Apr 2025 10:20:35 -0700
Subject: [PATCH 05/11] 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 <paulkirth at google.com>
Date: Sat, 12 Apr 2025 12:54:52 -0700
Subject: [PATCH 06/11] 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 <paulkirth at google.com>
Date: Sat, 12 Apr 2025 13:03:20 -0700
Subject: [PATCH 07/11] 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 <paulkirth at google.com>
Date: Sat, 12 Apr 2025 13:59:19 -0700
Subject: [PATCH 08/11] 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 <paulkirth at google.com>
Date: Sat, 12 Apr 2025 15:29:08 -0700
Subject: [PATCH 09/11] 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.
>From c56abae6b22a47ec7582e6fc316fb7c504202268 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Sat, 12 Apr 2025 15:41:48 -0700
Subject: [PATCH 10/11] Put comment back in original place
---
clang-tools-extra/clang-doc/Serialize.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 4ee5416cd0ef5..837ecdd9f1278 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -374,6 +374,8 @@ 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) {
for (const FieldDecl *F : D->fields()) {
@@ -590,8 +592,6 @@ 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 DeclaratorDecl *D, bool IsStatic) {
// Use getAccessUnsafe so that we just get the default AS_none if it's not
>From 8586b7739e2e7d03b9afa11fd20822f34fcc05d0 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Sat, 12 Apr 2025 15:47:16 -0700
Subject: [PATCH 11/11] Move fields to end of struct definition
---
clang-tools-extra/clang-doc/Representation.h | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index d87a13f8ff119..d7f2453fe11b2 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -317,17 +317,12 @@ 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) {}
void merge(SymbolInfo &&I);
- std::optional<Location> DefLoc; // Location where this decl is defined.
- llvm::SmallVector<Location, 2> Loc; // Locations where this decl is declared.
-
bool operator<(const SymbolInfo &Other) const {
// Sort by declaration location since we want the doc to be
// generated in the order of the source code.
@@ -341,6 +336,10 @@ struct SymbolInfo : public Info {
return extractName() < Other.extractName();
}
+
+ std::optional<Location> DefLoc; // Location where this decl is defined.
+ llvm::SmallVector<Location, 2> Loc; // Locations where this decl is declared.
+ bool IsStatic = false;
};
// TODO: Expand to allow for documenting templating and default args.
More information about the cfe-commits
mailing list