[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 12:59:22 PDT 2025


https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/135457

>From 32f60cd27c9234fd945443e801f1788930b1cd57 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 1/6] [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 617ddb71ef2987f762ce42e50e6611b51512d919 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 2/6] 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 df44b4c08fdc50b76496761b155453e3ccfaddfa 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 3/6] 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 7b9a54534f48fe0213c02053940c002dedffd420 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 4/6] 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 d0e1eaf13d33a1add70902d5ed35178d52abf263 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 5/6] 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 cc1c017ea7c48dbdbda9871335b0d297be2c7828 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 6/6] 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)*



More information about the cfe-commits mailing list