Skip to content

[clang-doc] document global variables #145070

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2025
Merged

Conversation

evelez7
Copy link
Member

@evelez7 evelez7 commented Jun 20, 2025

Visit and map VarDecls to document variables declared in namespace scope.

Copy link
Member Author

evelez7 commented Jun 20, 2025

@evelez7 evelez7 force-pushed the users/evelez7/clang-doc-globals branch from 7c0658c to 87a27c9 Compare June 20, 2025 21:20
@evelez7 evelez7 force-pushed the users/evelez7/clang-doc-precommit-globals branch from f572cfc to f056f70 Compare June 20, 2025 21:20
@evelez7 evelez7 requested review from ilovepi and petrhosek June 20, 2025 21:23
@evelez7 evelez7 marked this pull request as ready for review June 20, 2025 21:23
@llvmbot
Copy link
Member

llvmbot commented Jun 20, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Erick Velez (evelez7)

Changes

Visit and map VarDecls to document variables declared in namespace scope.


Patch is 23.42 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/145070.diff

16 Files Affected:

  • (modified) clang-tools-extra/clang-doc/BitcodeReader.cpp (+40)
  • (modified) clang-tools-extra/clang-doc/BitcodeWriter.cpp (+29-3)
  • (modified) clang-tools-extra/clang-doc/BitcodeWriter.h (+6)
  • (modified) clang-tools-extra/clang-doc/HTMLGenerator.cpp (+3)
  • (modified) clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp (+2)
  • (modified) clang-tools-extra/clang-doc/JSONGenerator.cpp (+14)
  • (modified) clang-tools-extra/clang-doc/MDGenerator.cpp (+4)
  • (modified) clang-tools-extra/clang-doc/Mapper.cpp (+6)
  • (modified) clang-tools-extra/clang-doc/Mapper.h (+1)
  • (modified) clang-tools-extra/clang-doc/Representation.cpp (+16)
  • (modified) clang-tools-extra/clang-doc/Representation.h (+13-1)
  • (modified) clang-tools-extra/clang-doc/Serialize.cpp (+27)
  • (modified) clang-tools-extra/clang-doc/Serialize.h (+4)
  • (modified) clang-tools-extra/clang-doc/YAMLGenerator.cpp (+1)
  • (modified) clang-tools-extra/test/clang-doc/json/namespace.cpp (+20-21)
  • (modified) clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp (+2)
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 66852931226bf..cbdd5d245b8de 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -93,6 +93,7 @@ static llvm::Error decodeRecord(const Record &R, InfoType &Field,
   case InfoType::IT_enum:
   case InfoType::IT_typedef:
   case InfoType::IT_concept:
+  case InfoType::IT_variable:
     Field = IT;
     return llvm::Error::success();
   }
@@ -416,6 +417,23 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
                                  "invalid field for ConstraintInfo");
 }
 
+static llvm::Error parseRecord(const Record &R, unsigned ID,
+                               llvm::StringRef Blob, VarInfo *I) {
+  switch (ID) {
+  case VAR_USR:
+    return decodeRecord(R, I->USR, Blob);
+  case VAR_NAME:
+    return decodeRecord(R, I->Name, Blob);
+  case VAR_DEFLOCATION:
+    return decodeRecord(R, I->DefLoc, Blob);
+  case VAR_IS_STATIC:
+    return decodeRecord(R, I->IsStatic, Blob);
+  default:
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "invalid field for VarInfo");
+  }
+}
+
 template <typename T> static llvm::Expected<CommentInfo *> getCommentInfo(T I) {
   return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                  "invalid type cannot contain CommentInfo");
@@ -458,6 +476,10 @@ template <> llvm::Expected<CommentInfo *> getCommentInfo(ConceptInfo *I) {
   return &I->Description.emplace_back();
 }
 
+template <> Expected<CommentInfo *> getCommentInfo(VarInfo *I) {
+  return &I->Description.emplace_back();
+}
+
 // When readSubBlock encounters a TypeInfo sub-block, it calls addTypeInfo on
 // the parent block to set it. The template specializations define what to do
 // for each supported parent block.
@@ -497,6 +519,11 @@ template <> llvm::Error addTypeInfo(TypedefInfo *I, TypeInfo &&T) {
   return llvm::Error::success();
 }
 
+template <> llvm::Error addTypeInfo(VarInfo *I, TypeInfo &&T) {
+  I->Type = std::move(T);
+  return llvm::Error::success();
+}
+
 template <typename T>
 static llvm::Error addReference(T I, Reference &&R, FieldId F) {
   return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -643,6 +670,9 @@ template <> void addChild(NamespaceInfo *I, TypedefInfo &&R) {
 template <> void addChild(NamespaceInfo *I, ConceptInfo &&R) {
   I->Children.Concepts.emplace_back(std::move(R));
 }
+template <> void addChild(NamespaceInfo *I, VarInfo &&R) {
+  I->Children.Variables.emplace_back(std::move(R));
+}
 
 // Record children:
 template <> void addChild(RecordInfo *I, FunctionInfo &&R) {
@@ -887,6 +917,13 @@ llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) {
     addChild(I, std::move(CI));
     return llvm::Error::success();
   }
+  case BI_VAR_BLOCK_ID: {
+    VarInfo VI;
+    if (auto Err = readBlock(ID, &VI))
+      return Err;
+    addChild(I, std::move(VI));
+    return llvm::Error::success();
+  }
   default:
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "invalid subblock type");
@@ -996,6 +1033,8 @@ ClangDocBitcodeReader::readBlockToInfo(unsigned ID) {
     return createInfo<ConceptInfo>(ID);
   case BI_FUNCTION_BLOCK_ID:
     return createInfo<FunctionInfo>(ID);
+  case BI_VAR_BLOCK_ID:
+    return createInfo<VarInfo>(ID);
   default:
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "cannot create info");
@@ -1035,6 +1074,7 @@ ClangDocBitcodeReader::readBitcode() {
     case BI_ENUM_BLOCK_ID:
     case BI_TYPEDEF_BLOCK_ID:
     case BI_CONCEPT_BLOCK_ID:
+    case BI_VAR_BLOCK_ID:
     case BI_FUNCTION_BLOCK_ID: {
       auto InfoOrErr = readBlockToInfo(ID);
       if (!InfoOrErr)
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index b7308c012786f..c3351d1decbf5 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -130,7 +130,8 @@ static const llvm::IndexedMap<llvm::StringRef, BlockIdToIndexFunctor>
           {BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, "TemplateSpecializationBlock"},
           {BI_TEMPLATE_PARAM_BLOCK_ID, "TemplateParamBlock"},
           {BI_CONSTRAINT_BLOCK_ID, "ConstraintBlock"},
-          {BI_CONCEPT_BLOCK_ID, "ConceptBlock"}};
+          {BI_CONCEPT_BLOCK_ID, "ConceptBlock"},
+          {BI_VAR_BLOCK_ID, "VarBlock"}};
       assert(Inits.size() == BlockIdCount);
       for (const auto &Init : Inits)
         BlockIdNameMap[Init.first] = Init.second;
@@ -213,7 +214,12 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
           {CONCEPT_IS_TYPE, {"IsType", &genBoolAbbrev}},
           {CONCEPT_CONSTRAINT_EXPRESSION,
            {"ConstraintExpression", &genStringAbbrev}},
-          {CONSTRAINT_EXPRESSION, {"Expression", &genStringAbbrev}}};
+          {CONSTRAINT_EXPRESSION, {"Expression", &genStringAbbrev}},
+          {VAR_USR, {"USR", &genSymbolIdAbbrev}},
+          {VAR_NAME, {"Name", &genStringAbbrev}},
+          {VAR_DEFLOCATION, {"DefLocation", &genLocationAbbrev}},
+          {VAR_IS_STATIC, {"IsStatic", &genBoolAbbrev}}};
+
       assert(Inits.size() == RecordIdCount);
       for (const auto &Init : Inits) {
         RecordIdNameMap[Init.first] = Init.second;
@@ -277,7 +283,8 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
          {CONCEPT_USR, CONCEPT_NAME, CONCEPT_IS_TYPE,
           CONCEPT_CONSTRAINT_EXPRESSION}},
         // Constraint Block
-        {BI_CONSTRAINT_BLOCK_ID, {CONSTRAINT_EXPRESSION}}};
+        {BI_CONSTRAINT_BLOCK_ID, {CONSTRAINT_EXPRESSION}},
+        {BI_VAR_BLOCK_ID, {VAR_NAME, VAR_USR, VAR_DEFLOCATION, VAR_IS_STATIC}}};
 
 // AbbreviationMap
 
@@ -540,6 +547,8 @@ void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) {
     emitBlock(C);
   for (const auto &C : I.Children.Concepts)
     emitBlock(C);
+  for (const auto &C : I.Children.Variables)
+    emitBlock(C);
 }
 
 void ClangDocBitcodeWriter::emitBlock(const EnumInfo &I) {
@@ -682,6 +691,20 @@ void ClangDocBitcodeWriter::emitBlock(const ConstraintInfo &C) {
   emitBlock(C.ConceptRef, FieldId::F_concept);
 }
 
+void ClangDocBitcodeWriter::emitBlock(const VarInfo &I) {
+  StreamSubBlockGuard Block(Stream, BI_VAR_BLOCK_ID);
+  emitRecord(I.USR, VAR_USR);
+  emitRecord(I.Name, VAR_NAME);
+  for (const auto &N : I.Namespace)
+    emitBlock(N, FieldId::F_namespace);
+  for (const auto &CI : I.Description)
+    emitBlock(CI);
+  if (I.DefLoc)
+    emitRecord(*I.DefLoc, VAR_DEFLOCATION);
+  emitRecord(I.IsStatic, VAR_IS_STATIC);
+  emitBlock(I.Type);
+}
+
 bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) {
   switch (I->IT) {
   case InfoType::IT_namespace:
@@ -702,6 +725,9 @@ bool ClangDocBitcodeWriter::dispatchInfoForWrite(Info *I) {
   case InfoType::IT_concept:
     emitBlock(*static_cast<clang::doc::ConceptInfo *>(I));
     break;
+  case InfoType::IT_variable:
+    emitBlock(*static_cast<VarInfo *>(I));
+    break;
   case InfoType::IT_default:
     llvm::errs() << "Unexpected info, unable to write.\n";
     return true;
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index 4d0c0c07805e7..a70e50b53a61a 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -69,6 +69,7 @@ enum BlockId {
   BI_CONSTRAINT_BLOCK_ID,
   BI_TYPEDEF_BLOCK_ID,
   BI_CONCEPT_BLOCK_ID,
+  BI_VAR_BLOCK_ID,
   BI_LAST,
   BI_FIRST = BI_VERSION_BLOCK_ID
 };
@@ -142,6 +143,10 @@ enum RecordId {
   CONCEPT_IS_TYPE,
   CONCEPT_CONSTRAINT_EXPRESSION,
   CONSTRAINT_EXPRESSION,
+  VAR_USR,
+  VAR_NAME,
+  VAR_DEFLOCATION,
+  VAR_IS_STATIC,
   RI_LAST,
   RI_FIRST = VERSION
 };
@@ -190,6 +195,7 @@ class ClangDocBitcodeWriter {
   void emitBlock(const ConceptInfo &T);
   void emitBlock(const ConstraintInfo &T);
   void emitBlock(const Reference &B, FieldId F);
+  void emitBlock(const VarInfo &B);
 
 private:
   class AbbreviationMap {
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index 935bbfee7a9b1..c4303d287da9e 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -986,6 +986,7 @@ llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
         genHTML(*static_cast<clang::doc::TypedefInfo *>(I), CDCtx, InfoTitle);
     break;
   case InfoType::IT_concept:
+  case InfoType::IT_variable:
     break;
   case InfoType::IT_default:
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -1015,6 +1016,8 @@ static std::string getRefType(InfoType IT) {
     return "typedef";
   case InfoType::IT_concept:
     return "concept";
+  case InfoType::IT_variable:
+    return "variable";
   }
   llvm_unreachable("Unknown InfoType");
 }
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 81ba99c21e374..c611c946b3937 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -587,6 +587,8 @@ Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
     break;
   case InfoType::IT_concept:
     break;
+  case InfoType::IT_variable:
+    break;
   case InfoType::IT_default:
     return createStringError(inconvertibleErrorCode(), "unexpected InfoType");
   }
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 8a37621597c6a..91f5ba4080619 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -482,6 +482,15 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj,
   serializeCommonChildren(I.Children, Obj, RepositoryUrl);
 }
 
+static void serializeInfo(const VarInfo &I, json::Object &Obj,
+                          std::optional<StringRef> RepositoryUrl) {
+  serializeCommonAttributes(I, Obj, RepositoryUrl);
+  Obj["IsStatic"] = I.IsStatic;
+  auto TypeObj = Object();
+  serializeInfo(I.Type, TypeObj);
+  Obj["Type"] = std::move(TypeObj);
+}
+
 static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,
                           std::optional<StringRef> RepositoryUrl) {
   serializeCommonAttributes(I, Obj, RepositoryUrl);
@@ -519,6 +528,10 @@ static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,
   if (!I.Children.Concepts.empty())
     serializeArray(I.Children.Concepts, Obj, "Concepts", SerializeInfo);
 
+  if (!I.Children.Variables.empty()) {
+    serializeArray(I.Children.Variables, Obj, "Variables", SerializeInfo);
+  }
+
   serializeCommonChildren(I.Children, Obj, RepositoryUrl);
 }
 
@@ -573,6 +586,7 @@ Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
   case InfoType::IT_enum:
   case InfoType::IT_function:
   case InfoType::IT_typedef:
+  case InfoType::IT_variable:
     break;
   case InfoType::IT_default:
     return createStringError(inconvertibleErrorCode(), "unexpected info type");
diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 6e68e09cfa2a6..608a7f6d4a9d3 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -375,6 +375,9 @@ static llvm::Error genIndex(ClangDocContext &CDCtx) {
       case InfoType::IT_concept:
         Type = "Concept";
         break;
+      case InfoType::IT_variable:
+        Type = "Variable";
+        break;
       case InfoType::IT_default:
         Type = "Other";
       }
@@ -468,6 +471,7 @@ llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
     genMarkdown(CDCtx, *static_cast<clang::doc::TypedefInfo *>(I), OS);
     break;
   case InfoType::IT_concept:
+  case InfoType::IT_variable:
     break;
   case InfoType::IT_default:
     return createStringError(llvm::inconvertibleErrorCode(),
diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp
index 6021e17b4696d..497b80cf97463 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -138,6 +138,12 @@ bool MapASTVisitor::VisitConceptDecl(const ConceptDecl *D) {
   return mapDecl(D, true);
 }
 
+bool MapASTVisitor::VisitVarDecl(const VarDecl *D) {
+  if (D->isCXXClassMember())
+    return true;
+  return mapDecl(D, D->isThisDeclarationADefinition());
+}
+
 comments::FullComment *
 MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const {
   RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
diff --git a/clang-tools-extra/clang-doc/Mapper.h b/clang-tools-extra/clang-doc/Mapper.h
index 04dc5450c8ba3..322df6d594b3d 100644
--- a/clang-tools-extra/clang-doc/Mapper.h
+++ b/clang-tools-extra/clang-doc/Mapper.h
@@ -42,6 +42,7 @@ class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
   bool VisitTypedefDecl(const TypedefDecl *D);
   bool VisitTypeAliasDecl(const TypeAliasDecl *D);
   bool VisitConceptDecl(const ConceptDecl *D);
+  bool VisitVarDecl(const VarDecl *D);
 
 private:
   template <typename T> bool mapDecl(const T *D, bool IsDefinition);
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index 6a4fce70320ed..c1f98e64112fa 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -145,6 +145,8 @@ mergeInfos(std::vector<std::unique_ptr<Info>> &Values) {
     return reduce<TypedefInfo>(Values);
   case InfoType::IT_concept:
     return reduce<ConceptInfo>(Values);
+  case InfoType::IT_variable:
+    return reduce<VarInfo>(Values);
   case InfoType::IT_default:
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "unexpected info type");
@@ -290,6 +292,7 @@ void NamespaceInfo::merge(NamespaceInfo &&Other) {
   reduceChildren(Children.Enums, std::move(Other.Children.Enums));
   reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
   reduceChildren(Children.Concepts, std::move(Other.Children.Concepts));
+  reduceChildren(Children.Variables, std::move(Other.Children.Variables));
   mergeBase(std::move(Other));
 }
 
@@ -367,6 +370,15 @@ void ConceptInfo::merge(ConceptInfo &&Other) {
   SymbolInfo::merge(std::move(Other));
 }
 
+void VarInfo::merge(VarInfo &&Other) {
+  assert(mergeable(Other));
+  if (!IsStatic)
+    IsStatic = Other.IsStatic;
+  if (Type.Type.USR == EmptySID && Type.Type.Name == "")
+    Type = std::move(Other.Type);
+  SymbolInfo::merge(std::move(Other));
+}
+
 BaseRecordInfo::BaseRecordInfo() : RecordInfo() {}
 
 BaseRecordInfo::BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path,
@@ -406,6 +418,9 @@ llvm::SmallString<16> Info::extractName() const {
   case InfoType::IT_concept:
     return llvm::SmallString<16>("@nonymous_concept_" +
                                  toHex(llvm::toStringRef(USR)));
+  case InfoType::IT_variable:
+    return llvm::SmallString<16>("@nonymous_variable_" +
+                                 toHex(llvm::toStringRef(USR)));
   case InfoType::IT_default:
     return llvm::SmallString<16>("@nonymous_" + toHex(llvm::toStringRef(USR)));
   }
@@ -472,6 +487,7 @@ void ScopeChildren::sort() {
   llvm::sort(Enums.begin(), Enums.end());
   llvm::sort(Typedefs.begin(), Typedefs.end());
   llvm::sort(Concepts.begin(), Concepts.end());
+  llvm::sort(Variables.begin(), Variables.end());
 }
 } // namespace doc
 } // namespace clang
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index b23069f2bd324..59874f0cfcedf 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -36,6 +36,7 @@ struct FunctionInfo;
 struct Info;
 struct TypedefInfo;
 struct ConceptInfo;
+struct VarInfo;
 
 enum class InfoType {
   IT_default,
@@ -44,7 +45,8 @@ enum class InfoType {
   IT_function,
   IT_enum,
   IT_typedef,
-  IT_concept
+  IT_concept,
+  IT_variable
 };
 
 enum class CommentKind {
@@ -169,6 +171,7 @@ struct ScopeChildren {
   std::vector<EnumInfo> Enums;
   std::vector<TypedefInfo> Typedefs;
   std::vector<ConceptInfo> Concepts;
+  std::vector<VarInfo> Variables;
 
   void sort();
 };
@@ -376,6 +379,15 @@ struct SymbolInfo : public Info {
   bool IsStatic = false;
 };
 
+struct VarInfo : SymbolInfo {
+  VarInfo() : SymbolInfo(InfoType::IT_variable) {}
+  explicit VarInfo(SymbolID USR) : SymbolInfo(InfoType::IT_variable, USR) {}
+
+  void merge(VarInfo &&I);
+
+  TypeInfo Type;
+};
+
 // TODO: Expand to allow for documenting templating and default args.
 // Info for functions.
 struct FunctionInfo : public SymbolInfo {
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 5cc7889a02c58..662aec341c8d9 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -401,6 +401,8 @@ std::string serialize(std::unique_ptr<Info> &I) {
     return serialize(*static_cast<FunctionInfo *>(I.get()));
   case InfoType::IT_concept:
     return serialize(*static_cast<ConceptInfo *>(I.get()));
+  case InfoType::IT_variable:
+    return serialize(*static_cast<VarInfo *>(I.get()));
   case InfoType::IT_typedef:
   case InfoType::IT_default:
     return "";
@@ -507,6 +509,10 @@ static void InsertChild(ScopeChildren &Scope, ConceptInfo Info) {
   Scope.Concepts.push_back(std::move(Info));
 }
 
+static void InsertChild(ScopeChildren &Scope, VarInfo Info) {
+  Scope.Variables.push_back(std::move(Info));
+}
+
 // Creates a parent of the correct type for the given child and inserts it into
 // that parent.
 //
@@ -548,6 +554,7 @@ static std::unique_ptr<Info> makeAndInsertIntoParent(ChildType Child) {
   case InfoType::IT_function:
   case InfoType::IT_typedef:
   case InfoType::IT_concept:
+  case InfoType::IT_variable:
     break;
   }
   llvm_unreachable("Invalid reference type for parent namespace");
@@ -1163,6 +1170,26 @@ emitInfo(const ConceptDecl *D, const FullComment *FC, const Location &Loc,
   return {nullptr, makeAndInsertIntoParent<ConceptInfo &&>(std::move(Concept))};
 }
 
+std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
+emitInfo(const VarDecl *D, const FullComment *FC, const Location &Loc,
+         bool PublicOnly) {
+  VarInfo Var;
+  bool IsInAnonymousNamespace = false;
+  populateSymbolInfo(Var, D, FC, Loc, IsInAnonymousNamespace);
+  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
+    return {};
+
+  if (D->getStorageClass() == StorageClass::SC_Static)
+    Var.IsStatic = true;
+  Var.Type =
+      getTypeInfoForType(D->getType(), D->getASTContext().getPrintingPolicy());
+
+  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
+    return {};
+
+  return {nullptr, makeAndInsertIntoParent<VarInfo &&>(std::move(Var))};
+}
+
 } // namespace serialize
 } // namespace doc
 } // namespace clang
diff --git a/clang-tools-extra/clang-doc/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h
index 497b09bb339f8..06c4d64c51494 100644
--- a/clang-tools-extra/clang-doc/Serialize.h
+++ b/clang-tools-extra/clang-doc/Serialize.h
@@ -72,6 +72,10 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
 emitInfo(const ConceptDecl *D, const FullComment *FC, const Location &Loc,
          bool PublicOnly);
 
+std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
+emitInfo(const VarDecl *D, const FullComment *FC, const Location &Loc,
+         bool PublicOnly);
+
 // Function to hash a given USR value for storage.
 // As USRs (Unified Symbol Resolution) could be large, especially for functions
 // with long type arguments, we use 160-bits SHA1(USR) values to
diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
index f958871046981..3ca4d4947fa97 100644
--- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
@@ -409,6 +409,7 @@ llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
     InfoYAML << *static_cast<clang::doc::TypedefInfo *>(I);
     break;
   case InfoType::IT_concept:
+  case InfoType::IT_variable:
     break;
...
[truncated]

Copy link
Contributor

@ilovepi ilovepi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, w/ one Q about the test expr. I could go either way on that, so its up to you.

@@ -25,7 +24,7 @@ typedef int MyTypedef;
// CHECK-NEXT: {
// CHECK-NEXT: "Location": {
// CHECK-NEXT: "Filename": "{{.*}}namespace.cpp",
// CHECK-NEXT: "LineNumber": 15
// CHECK-NEXT: "LineNumber": 14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use LINE expressions? not sure it will help too much, but it may be a good idea to move all these bits closer to the code ... I'm not sure one way or the other. We're not very consistent about it, in any case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After our offline discussion, I think having one test to correctly check line numbers would be a pretty good compromise.

@evelez7 evelez7 force-pushed the users/evelez7/clang-doc-precommit-globals branch from f056f70 to 62269c9 Compare June 20, 2025 23:40
@evelez7 evelez7 force-pushed the users/evelez7/clang-doc-globals branch from 87a27c9 to 76a4ef1 Compare June 20, 2025 23:41
@evelez7 evelez7 force-pushed the users/evelez7/clang-doc-precommit-globals branch from 62269c9 to 890d6f9 Compare June 21, 2025 00:41
@evelez7 evelez7 force-pushed the users/evelez7/clang-doc-globals branch from 76a4ef1 to fa5f1cb Compare June 21, 2025 00:42
Base automatically changed from users/evelez7/clang-doc-precommit-globals to main June 21, 2025 18:56
@evelez7 evelez7 force-pushed the users/evelez7/clang-doc-globals branch from fa5f1cb to f3e1d5e Compare June 21, 2025 18:58
Copy link
Member Author

evelez7 commented Jun 24, 2025

Merge activity

  • Jun 24, 6:34 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jun 24, 6:36 PM UTC: @evelez7 merged this pull request with Graphite.

@evelez7 evelez7 merged commit b8ea650 into main Jun 24, 2025
7 checks passed
@evelez7 evelez7 deleted the users/evelez7/clang-doc-globals branch June 24, 2025 18:36
DrSergei pushed a commit to DrSergei/llvm-project that referenced this pull request Jun 24, 2025
Visit and map VarDecls to document variables declared in namespace scope.
anthonyhatran pushed a commit to anthonyhatran/llvm-project that referenced this pull request Jun 26, 2025
Visit and map VarDecls to document variables declared in namespace scope.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants