[llvm-branch-commits] [clang-tools-extra] [clang-doc] Introduce Serializer class (PR #184873)

Paul Kirth via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Mar 5 12:01:17 PST 2026


https://github.com/ilovepi created https://github.com/llvm/llvm-project/pull/184873

Serialization has mostly been done with static functions, but soon we
will need to share state, like alocator references. To avoid blowing up
our parameter lists, we can just wrap the local functions within a
class.

>From 753b81c5a8ac5117e1776a0e1e84a8f620e78c08 Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 5 Mar 2026 02:09:14 +0000
Subject: [PATCH] [clang-doc] Introduce Serializer class

Serialization has mostly been done with static functions, but soon we
will need to share state, like alocator references. To avoid blowing up
our parameter lists, we can just wrap the local functions within a
class.
---
 clang-tools-extra/clang-doc/Mapper.cpp        |   3 +-
 clang-tools-extra/clang-doc/Serialize.cpp     | 216 +++++++++---------
 clang-tools-extra/clang-doc/Serialize.h       | 198 ++++++++++++----
 .../benchmarks/ClangDocBenchmark.cpp          |   3 +-
 .../unittests/clang-doc/SerializeTest.cpp     |   3 +-
 5 files changed, 262 insertions(+), 161 deletions(-)

diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp
index d567fd28acfc4..01997800505eb 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -83,7 +83,8 @@ bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
     bool IsFileInRootDir;
     llvm::SmallString<128> File =
         getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
-    CP = serialize::emitInfo(D, getComment(D, D->getASTContext()),
+    serialize::ClangDocSerializer Serializer;
+    CP = Serializer.emitInfo(D, getComment(D, D->getASTContext()),
                              getDeclLocation(D), CDCtx.PublicOnly);
   }
 
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index b6e963bd5fd74..de26b204e4f7f 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -41,18 +41,8 @@ SymbolID hashUSR(llvm::StringRef USR) {
   return llvm::SHA1::hash(arrayRefFromStringRef(USR));
 }
 
-template <typename T>
-static void
-populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
-                         const T *D, bool &IsAnonymousNamespace);
-
-template <typename T> static void populateMemberTypeInfo(T &I, const Decl *D);
-static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
-                                   const DeclaratorDecl *D,
-                                   bool IsStatic = false);
-
-static void getTemplateParameters(const TemplateParameterList *TemplateParams,
-                                  llvm::raw_ostream &Stream) {
+void ClangDocSerializer::getTemplateParameters(
+    const TemplateParameterList *TemplateParams, llvm::raw_ostream &Stream) {
   Stream << "template <";
 
   for (unsigned i = 0; i < TemplateParams->size(); ++i) {
@@ -96,8 +86,8 @@ static void getTemplateParameters(const TemplateParameterList *TemplateParams,
 
 // Extract the full function prototype from a FunctionDecl including
 // Full Decl
-static llvm::SmallString<256>
-getFunctionPrototype(const FunctionDecl *FuncDecl) {
+llvm::SmallString<256>
+ClangDocSerializer::getFunctionPrototype(const FunctionDecl *FuncDecl) {
   llvm::SmallString<256> Result;
   llvm::raw_svector_ostream Stream(Result);
   const ASTContext &Ctx = FuncDecl->getASTContext();
@@ -167,7 +157,8 @@ getFunctionPrototype(const FunctionDecl *FuncDecl) {
   return Result; // Convert SmallString to std::string for return
 }
 
-static llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias) {
+llvm::SmallString<16>
+ClangDocSerializer::getTypeAlias(const TypeAliasDecl *Alias) {
   llvm::SmallString<16> Result;
   llvm::raw_svector_ostream Stream(Result);
   const ASTContext &Ctx = Alias->getASTContext();
@@ -193,15 +184,15 @@ static llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias) {
 //
 // }
 // }
-static llvm::SmallString<128>
-getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
+llvm::SmallString<128> ClangDocSerializer::getInfoRelativePath(
+    const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
   llvm::SmallString<128> Path;
   for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
     llvm::sys::path::append(Path, R->Name);
   return Path;
 }
 
-static llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
+llvm::SmallString<128> ClangDocSerializer::getInfoRelativePath(const Decl *D) {
   llvm::SmallVector<Reference, 4> Namespaces;
   // The third arg in populateParentNamespaces is a boolean passed by reference,
   // its value is not relevant in here so it's not used anywhere besides the
@@ -330,7 +321,8 @@ std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
 
 // Serializing functions.
 
-static std::string getSourceCode(const Decl *D, const SourceRange &R) {
+std::string ClangDocSerializer::getSourceCode(const Decl *D,
+                                              const SourceRange &R) {
   return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
                               D->getASTContext().getSourceManager(),
                               D->getASTContext().getLangOpts())
@@ -368,32 +360,33 @@ std::string serialize(OwnedPtr<Info> &I, DiagnosticsEngine &Diags) {
   llvm_unreachable("unhandled enumerator");
 }
 
-static void parseFullComment(const FullComment *C, CommentInfo &CI) {
+void ClangDocSerializer::parseFullComment(const FullComment *C,
+                                          CommentInfo &CI) {
   ClangDocCommentVisitor Visitor(CI);
   Visitor.parseComment(C);
 }
 
-static SymbolID getUSRForDecl(const Decl *D) {
+SymbolID ClangDocSerializer::getUSRForDecl(const Decl *D) {
   llvm::SmallString<128> USR;
   if (index::generateUSRForDecl(D, USR))
     return SymbolID();
   return hashUSR(USR);
 }
 
-static TagDecl *getTagDeclForType(const QualType &T) {
+TagDecl *ClangDocSerializer::getTagDeclForType(const QualType &T) {
   if (const TagDecl *D = T->getAsTagDecl())
     return D->getDefinition();
   return nullptr;
 }
 
-static RecordDecl *getRecordDeclForType(const QualType &T) {
+RecordDecl *ClangDocSerializer::getRecordDeclForType(const QualType &T) {
   if (const RecordDecl *D = T->getAsRecordDecl())
     return D->getDefinition();
   return nullptr;
 }
 
-static TypeInfo getTypeInfoForType(const QualType &T,
-                                   const PrintingPolicy &Policy) {
+TypeInfo ClangDocSerializer::getTypeInfoForType(const QualType &T,
+                                                const PrintingPolicy &Policy) {
   const TagDecl *TD = getTagDeclForType(T);
   if (!TD) {
     TypeInfo TI = TypeInfo(Reference(SymbolID(), T.getAsString(Policy)));
@@ -417,8 +410,8 @@ static TypeInfo getTypeInfoForType(const QualType &T,
   return TI;
 }
 
-static bool isPublic(const clang::AccessSpecifier AS,
-                     const clang::Linkage Link) {
+bool ClangDocSerializer::isPublic(const clang::AccessSpecifier AS,
+                                  const clang::Linkage Link) {
   if (AS == clang::AccessSpecifier::AS_private)
     return false;
   if ((Link == clang::Linkage::Module) || (Link == clang::Linkage::External))
@@ -426,8 +419,9 @@ static bool isPublic(const clang::AccessSpecifier AS,
   return false; // otherwise, linkage is some form of internal linkage
 }
 
-static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
-                                const NamedDecl *D) {
+bool ClangDocSerializer::shouldSerializeInfo(bool PublicOnly,
+                                             bool IsInAnonymousNamespace,
+                                             const NamedDecl *D) {
   bool IsAnonymousNamespace = false;
   if (const auto *N = dyn_cast<NamespaceDecl>(D))
     IsAnonymousNamespace = N->isAnonymousNamespace();
@@ -442,34 +436,36 @@ static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
 // refer to them.
 //
 // See MakeAndInsertIntoParent().
-static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
+void ClangDocSerializer::InsertChild(ScopeChildren &Scope,
+                                     const NamespaceInfo &Info) {
   Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
                                 Info.Name, getInfoRelativePath(Info.Namespace));
 }
 
-static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
+void ClangDocSerializer::InsertChild(ScopeChildren &Scope,
+                                     const RecordInfo &Info) {
   Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
                              Info.Name, getInfoRelativePath(Info.Namespace),
                              Info.MangledName);
 }
 
-static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
+void ClangDocSerializer::InsertChild(ScopeChildren &Scope, EnumInfo Info) {
   Scope.Enums.push_back(std::move(Info));
 }
 
-static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
+void ClangDocSerializer::InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
   Scope.Functions.push_back(std::move(Info));
 }
 
-static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
+void ClangDocSerializer::InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
   Scope.Typedefs.push_back(std::move(Info));
 }
 
-static void InsertChild(ScopeChildren &Scope, ConceptInfo Info) {
+void ClangDocSerializer::InsertChild(ScopeChildren &Scope, ConceptInfo Info) {
   Scope.Concepts.push_back(std::move(Info));
 }
 
-static void InsertChild(ScopeChildren &Scope, VarInfo Info) {
+void ClangDocSerializer::InsertChild(ScopeChildren &Scope, VarInfo Info) {
   Scope.Variables.push_back(std::move(Info));
 }
 
@@ -488,7 +484,7 @@ static void InsertChild(ScopeChildren &Scope, VarInfo Info) {
 // parameter. Since each variant is used once, it's not worth having a more
 // elaborate system to automatically deduce this information.
 template <typename ChildType>
-static OwnedPtr<Info> makeAndInsertIntoParent(ChildType Child) {
+OwnedPtr<Info> ClangDocSerializer::makeAndInsertIntoParent(ChildType Child) {
   if (Child.Namespace.empty()) {
     // Insert into unnamed parent namespace.
     auto ParentNS = allocatePtr<NamespaceInfo>();
@@ -536,8 +532,9 @@ static OwnedPtr<Info> makeAndInsertIntoParent(ChildType Child) {
 //    will be private because the inheritance is private. This is the AS that
 //    this function calculates. FirstAS is the inheritance mode and SecondAS is
 //    the AS of the attribute / method.
-static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
-                                               AccessSpecifier SecondAS) {
+AccessSpecifier
+ClangDocSerializer::getFinalAccessSpecifier(AccessSpecifier FirstAS,
+                                            AccessSpecifier SecondAS) {
   if (FirstAS == AccessSpecifier::AS_none ||
       SecondAS == AccessSpecifier::AS_none)
     return AccessSpecifier::AS_none;
@@ -552,8 +549,8 @@ static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
 
 // 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) {
+void ClangDocSerializer::parseFields(RecordInfo &I, const RecordDecl *D,
+                                     bool PublicOnly, AccessSpecifier Access) {
   for (const FieldDecl *F : D->fields()) {
     if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
       continue;
@@ -573,7 +570,7 @@ static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
   }
 }
 
-static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
+void ClangDocSerializer::parseEnumerators(EnumInfo &I, const EnumDecl *D) {
   for (const EnumConstantDecl *E : D->enumerators()) {
     std::string ValueExpr;
     if (const Expr *InitExpr = E->getInitExpr())
@@ -594,7 +591,8 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
   }
 }
 
-static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
+void ClangDocSerializer::parseParameters(FunctionInfo &I,
+                                         const FunctionDecl *D) {
   auto &LO = D->getLangOpts();
   for (const ParmVarDecl *P : D->parameters()) {
     FieldTypeInfo &FieldInfo = I.Params.emplace_back(
@@ -605,7 +603,7 @@ static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
 
 // TODO: Remove the serialization of Parents and VirtualParents, this
 // information is also extracted in the other definition of parseBases.
-static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
+void ClangDocSerializer::parseBases(RecordInfo &I, const CXXRecordDecl *D) {
   // Don't parse bases if this isn't a definition.
   if (!D->isThisDeclarationADefinition())
     return;
@@ -635,9 +633,9 @@ static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
 }
 
 template <typename T>
-static void
-populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
-                         const T *D, bool &IsInAnonymousNamespace) {
+void ClangDocSerializer::populateParentNamespaces(
+    llvm::SmallVector<Reference, 4> &Namespaces, const T *D,
+    bool &IsInAnonymousNamespace) {
   const DeclContext *DC = D->getDeclContext();
   do {
     if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
@@ -672,9 +670,8 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
                             InfoType::IT_namespace);
 }
 
-static void
-populateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
-                           const clang::Decl *D) {
+void ClangDocSerializer::populateTemplateParameters(
+    std::optional<TemplateInfo> &TemplateInfo, const clang::Decl *D) {
   if (const TemplateParameterList *ParamList =
           D->getDescribedTemplateParams()) {
     if (!TemplateInfo) {
@@ -687,8 +684,9 @@ populateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
   }
 }
 
-static TemplateParamInfo convertTemplateArgToInfo(const clang::Decl *D,
-                                                  const TemplateArgument &Arg) {
+TemplateParamInfo
+ClangDocSerializer::convertTemplateArgToInfo(const clang::Decl *D,
+                                             const TemplateArgument &Arg) {
   // The TemplateArgument's pretty printing handles all the normal cases
   // well enough for our requirements.
   std::string Str;
@@ -700,7 +698,7 @@ static TemplateParamInfo convertTemplateArgToInfo(const clang::Decl *D,
 // Check if the DeclKind is one for which we support contextual relationships.
 // There might be other ContextDecls, like blocks, that we currently don't
 // handle at all.
-static bool isSupportedContext(Decl::Kind DeclKind) {
+bool ClangDocSerializer::isSupportedContext(Decl::Kind DeclKind) {
   switch (DeclKind) {
   case Decl::Kind::Record:
   case Decl::Kind::CXXRecord:
@@ -713,7 +711,7 @@ static bool isSupportedContext(Decl::Kind DeclKind) {
   }
 }
 
-static void findParent(Info &I, const Decl *D) {
+void ClangDocSerializer::findParent(Info &I, const Decl *D) {
   assert(D && "Invalid Decl");
 
   // Only walk up contexts if D is a record or namespace.
@@ -732,8 +730,8 @@ static void findParent(Info &I, const Decl *D) {
 }
 
 template <typename T>
-static void populateInfo(Info &I, const T *D, const FullComment *C,
-                         bool &IsInAnonymousNamespace) {
+void ClangDocSerializer::populateInfo(Info &I, const T *D, const FullComment *C,
+                                      bool &IsInAnonymousNamespace) {
   I.USR = getUSRForDecl(D);
   findParent(I, D);
 
@@ -752,8 +750,9 @@ static void populateInfo(Info &I, const T *D, const FullComment *C,
 }
 
 template <typename T>
-static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
-                               Location Loc, bool &IsInAnonymousNamespace) {
+void ClangDocSerializer::populateSymbolInfo(SymbolInfo &I, const T *D,
+                                            const FullComment *C, Location Loc,
+                                            bool &IsInAnonymousNamespace) {
   populateInfo(I, D, C, IsInAnonymousNamespace);
   if (D->isThisDeclarationADefinition())
     I.DefLoc = Loc;
@@ -778,9 +777,8 @@ static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
   delete Mangler;
 }
 
-static void
-handleCompoundConstraints(const Expr *Constraint,
-                          std::vector<ConstraintInfo> &ConstraintInfos) {
+void ClangDocSerializer::handleCompoundConstraints(
+    const Expr *Constraint, std::vector<ConstraintInfo> &ConstraintInfos) {
   if (Constraint->getStmtClass() == Stmt::ParenExprClass) {
     handleCompoundConstraints(dyn_cast<ParenExpr>(Constraint)->getSubExpr(),
                               ConstraintInfos);
@@ -798,7 +796,8 @@ handleCompoundConstraints(const Expr *Constraint,
   }
 }
 
-static void populateConstraints(TemplateInfo &I, const TemplateDecl *D) {
+void ClangDocSerializer::populateConstraints(TemplateInfo &I,
+                                             const TemplateDecl *D) {
   if (!D || !D->hasAssociatedConstraints())
     return;
 
@@ -822,9 +821,11 @@ static void populateConstraints(TemplateInfo &I, const TemplateDecl *D) {
   }
 }
 
-static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
-                                 const FullComment *FC, Location Loc,
-                                 bool &IsInAnonymousNamespace) {
+void ClangDocSerializer::populateFunctionInfo(FunctionInfo &I,
+                                              const FunctionDecl *D,
+                                              const FullComment *FC,
+                                              Location Loc,
+                                              bool &IsInAnonymousNamespace) {
   populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace);
   auto &LO = D->getLangOpts();
   I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
@@ -857,7 +858,8 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
 
 // TODO: Rename this, since this doesn't populate anything besides comments and
 // isn't exclusive to members
-template <typename T> static void populateMemberTypeInfo(T &I, const Decl *D) {
+template <typename T>
+void ClangDocSerializer::populateMemberTypeInfo(T &I, const Decl *D) {
   assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
 
   ASTContext &Context = D->getASTContext();
@@ -874,8 +876,10 @@ template <typename T> static void populateMemberTypeInfo(T &I, const Decl *D) {
   }
 }
 
-static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
-                                   const DeclaratorDecl *D, bool IsStatic) {
+void ClangDocSerializer::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
   // valid, as opposed to an assert.
   MemberTypeInfo &NewMember = I.Members.emplace_back(
@@ -885,10 +889,10 @@ static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
   populateMemberTypeInfo(NewMember, D);
 }
 
-static void
-parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
-           bool PublicOnly, bool IsParent,
-           AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
+void ClangDocSerializer::parseBases(RecordInfo &I, const CXXRecordDecl *D,
+                                    bool IsFileInRootDir, bool PublicOnly,
+                                    bool IsParent,
+                                    AccessSpecifier ParentAccess) {
   // Don't parse bases if this isn't a definition.
   if (!D->isThisDeclarationADefinition())
     return;
@@ -940,10 +944,9 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
   }
 }
 
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const NamespaceDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly) {
+std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+ClangDocSerializer::emitInfo(const NamespaceDecl *D, const FullComment *FC,
+                             Location Loc, bool PublicOnly) {
   auto NSI = allocatePtr<NamespaceInfo>();
   bool IsInAnonymousNamespace = false;
   populateInfo(*NSI, D, FC, IsInAnonymousNamespace);
@@ -962,7 +965,7 @@ std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const NamespaceDecl *D,
   return {std::move(NSI), makeAndInsertIntoParent<const NamespaceInfo &>(*NSI)};
 }
 
-static void parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
+void ClangDocSerializer::parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
   if (!D->hasDefinition() || !D->hasFriends())
     return;
 
@@ -1012,10 +1015,9 @@ static void parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
   }
 }
 
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const RecordDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly) {
+std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+ClangDocSerializer::emitInfo(const RecordDecl *D, const FullComment *FC,
+                             Location Loc, bool PublicOnly) {
 
   auto RI = allocatePtr<RecordInfo>();
   bool IsInAnonymousNamespace = false;
@@ -1085,10 +1087,9 @@ std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const RecordDecl *D,
   return {std::move(RI), std::move(Parent)};
 }
 
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const FunctionDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly) {
+std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+ClangDocSerializer::emitInfo(const FunctionDecl *D, const FullComment *FC,
+                             Location Loc, bool PublicOnly) {
   FunctionInfo Func;
   bool IsInAnonymousNamespace = false;
   populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
@@ -1100,10 +1101,9 @@ std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const FunctionDecl *D,
   return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
 }
 
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const CXXMethodDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly) {
+std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+ClangDocSerializer::emitInfo(const CXXMethodDecl *D, const FullComment *FC,
+                             Location Loc, bool PublicOnly) {
   FunctionInfo Func;
   bool IsInAnonymousNamespace = false;
   populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
@@ -1130,7 +1130,8 @@ std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const CXXMethodDecl *D,
   return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
 }
 
-static void extractCommentFromDecl(const Decl *D, TypedefInfo &Info) {
+void ClangDocSerializer::extractCommentFromDecl(const Decl *D,
+                                                TypedefInfo &Info) {
   assert(D && "Invalid Decl when extracting comment");
   ASTContext &Context = D->getASTContext();
   RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
@@ -1144,10 +1145,9 @@ static void extractCommentFromDecl(const Decl *D, TypedefInfo &Info) {
   }
 }
 
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const TypedefDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly) {
+std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+ClangDocSerializer::emitInfo(const TypedefDecl *D, const FullComment *FC,
+                             Location Loc, bool PublicOnly) {
   TypedefInfo Info;
   bool IsInAnonymousNamespace = false;
   populateInfo(Info, D, FC, IsInAnonymousNamespace);
@@ -1177,10 +1177,9 @@ std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const TypedefDecl *D,
 
 // A type alias is a C++ "using" declaration for a type. It gets mapped to a
 // TypedefInfo with the IsUsing flag set.
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const TypeAliasDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly) {
+std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+ClangDocSerializer::emitInfo(const TypeAliasDecl *D, const FullComment *FC,
+                             Location Loc, bool PublicOnly) {
   TypedefInfo Info;
   bool IsInAnonymousNamespace = false;
   populateInfo(Info, D, FC, IsInAnonymousNamespace);
@@ -1202,10 +1201,9 @@ std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const TypeAliasDecl *D,
   return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
 }
 
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const EnumDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly) {
+std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+ClangDocSerializer::emitInfo(const EnumDecl *D, const FullComment *FC,
+                             Location Loc, bool PublicOnly) {
   EnumInfo Enum;
   bool IsInAnonymousNamespace = false;
   populateSymbolInfo(Enum, D, FC, Loc, IsInAnonymousNamespace);
@@ -1224,10 +1222,9 @@ std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const EnumDecl *D,
   return {nullptr, makeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
 }
 
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const ConceptDecl *D,
-                                                   const FullComment *FC,
-                                                   const Location &Loc,
-                                                   bool PublicOnly) {
+std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+ClangDocSerializer::emitInfo(const ConceptDecl *D, const FullComment *FC,
+                             const Location &Loc, bool PublicOnly) {
   ConceptInfo Concept;
 
   bool IsInAnonymousNamespace = false;
@@ -1249,10 +1246,9 @@ std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const ConceptDecl *D,
   return {nullptr, makeAndInsertIntoParent<ConceptInfo &&>(std::move(Concept))};
 }
 
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const VarDecl *D,
-                                                   const FullComment *FC,
-                                                   const Location &Loc,
-                                                   bool PublicOnly) {
+std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+ClangDocSerializer::emitInfo(const VarDecl *D, const FullComment *FC,
+                             const Location &Loc, bool PublicOnly) {
   VarInfo Var;
   bool IsInAnonymousNamespace = false;
   populateSymbolInfo(Var, D, FC, Loc, IsInAnonymousNamespace);
diff --git a/clang-tools-extra/clang-doc/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h
index 4a27927d568f1..5bffb0cf7f246 100644
--- a/clang-tools-extra/clang-doc/Serialize.h
+++ b/clang-tools-extra/clang-doc/Serialize.h
@@ -33,54 +33,156 @@ namespace serialize {
 // EnumDecl, FunctionDecl and CXXMethodDecl; they are only returned wrapped in
 // its parent scope. For NamespaceDecl and RecordDecl both elements are not
 // nullptr.
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const NamespaceDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly);
-
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const RecordDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly);
-
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const EnumDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly);
-
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const FunctionDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly);
-
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
-emitInfo(const VarDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
-
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const CXXMethodDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly);
-
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const TypedefDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly);
-
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const TypeAliasDecl *D,
-                                                   const FullComment *FC,
-                                                   Location Loc,
-                                                   bool PublicOnly);
-
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const ConceptDecl *D,
-                                                   const FullComment *FC,
-                                                   const Location &Loc,
-                                                   bool PublicOnly);
-
-std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const VarDecl *D,
-                                                   const FullComment *FC,
-                                                   const Location &Loc,
-                                                   bool PublicOnly);
+class ClangDocSerializer {
+public:
+  ClangDocSerializer() = default;
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const NamespaceDecl *D,
+                                                     const FullComment *FC,
+                                                     Location Loc,
+                                                     bool PublicOnly);
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const RecordDecl *D,
+                                                     const FullComment *FC,
+                                                     Location Loc,
+                                                     bool PublicOnly);
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const EnumDecl *D,
+                                                     const FullComment *FC,
+                                                     Location Loc,
+                                                     bool PublicOnly);
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const FunctionDecl *D,
+                                                     const FullComment *FC,
+                                                     Location Loc,
+                                                     bool PublicOnly);
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
+  emitInfo(const VarDecl *D, const FullComment *FC, int LineNumber,
+           StringRef File, bool IsFileInRootDir, bool PublicOnly);
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const CXXMethodDecl *D,
+                                                     const FullComment *FC,
+                                                     Location Loc,
+                                                     bool PublicOnly);
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const TypedefDecl *D,
+                                                     const FullComment *FC,
+                                                     Location Loc,
+                                                     bool PublicOnly);
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const TypeAliasDecl *D,
+                                                     const FullComment *FC,
+                                                     Location Loc,
+                                                     bool PublicOnly);
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const ConceptDecl *D,
+                                                     const FullComment *FC,
+                                                     const Location &Loc,
+                                                     bool PublicOnly);
+
+  std::pair<OwnedPtr<Info>, OwnedPtr<Info>> emitInfo(const VarDecl *D,
+                                                     const FullComment *FC,
+                                                     const Location &Loc,
+                                                     bool PublicOnly);
+
+private:
+  void getTemplateParameters(const TemplateParameterList *TemplateParams,
+                             llvm::raw_ostream &Stream);
+
+  llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl);
+
+  llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias);
+
+  llvm::SmallString<128>
+  getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces);
+
+  llvm::SmallString<128> getInfoRelativePath(const Decl *D);
+
+  std::string getSourceCode(const Decl *D, const SourceRange &R);
+
+  void parseFullComment(const FullComment *C, CommentInfo &CI);
+
+  SymbolID getUSRForDecl(const Decl *D);
+
+  TagDecl *getTagDeclForType(const QualType &T);
+
+  RecordDecl *getRecordDeclForType(const QualType &T);
+
+  TypeInfo getTypeInfoForType(const QualType &T, const PrintingPolicy &Policy);
+
+  bool isPublic(const clang::AccessSpecifier AS, const clang::Linkage Link);
+
+  bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
+                           const NamedDecl *D);
+
+  void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info);
+  void InsertChild(ScopeChildren &Scope, const RecordInfo &Info);
+  void InsertChild(ScopeChildren &Scope, EnumInfo Info);
+  void InsertChild(ScopeChildren &Scope, FunctionInfo Info);
+  void InsertChild(ScopeChildren &Scope, TypedefInfo Info);
+  void InsertChild(ScopeChildren &Scope, ConceptInfo Info);
+  void InsertChild(ScopeChildren &Scope, VarInfo Info);
+
+  template <typename ChildType>
+  OwnedPtr<Info> makeAndInsertIntoParent(ChildType Child);
+
+  AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
+                                          AccessSpecifier SecondAS);
+
+  void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
+                   AccessSpecifier Access = AccessSpecifier::AS_public);
+
+  void parseEnumerators(EnumInfo &I, const EnumDecl *D);
+
+  void parseParameters(FunctionInfo &I, const FunctionDecl *D);
+
+  void parseBases(RecordInfo &I, const CXXRecordDecl *D);
+
+  void parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
+                  bool PublicOnly, bool IsParent,
+                  AccessSpecifier ParentAccess = AccessSpecifier::AS_public);
+
+  template <typename T>
+  void populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
+                                const T *D, bool &IsInAnonymousNamespace);
+
+  void populateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
+                                  const clang::Decl *D);
+
+  TemplateParamInfo convertTemplateArgToInfo(const clang::Decl *D,
+                                             const TemplateArgument &Arg);
+
+  bool isSupportedContext(Decl::Kind DeclKind);
+
+  void findParent(Info &I, const Decl *D);
+
+  template <typename T>
+  void populateInfo(Info &I, const T *D, const FullComment *C,
+                    bool &IsInAnonymousNamespace);
+
+  template <typename T>
+  void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
+                          Location Loc, bool &IsInAnonymousNamespace);
+
+  void handleCompoundConstraints(const Expr *Constraint,
+                                 std::vector<ConstraintInfo> &ConstraintInfos);
+
+  void populateConstraints(TemplateInfo &I, const TemplateDecl *D);
+
+  void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
+                            const FullComment *FC, Location Loc,
+                            bool &IsInAnonymousNamespace);
+
+  template <typename T> void populateMemberTypeInfo(T &I, const Decl *D);
+
+  void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
+                              const DeclaratorDecl *D, bool IsStatic = false);
+
+  void parseFriends(RecordInfo &RI, const CXXRecordDecl *D);
+
+  void extractCommentFromDecl(const Decl *D, TypedefInfo &Info);
+};
 
 // Function to hash a given USR value for storage.
 // As USRs (Unified Symbol Resolution) could be large, especially for functions
diff --git a/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp b/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp
index e5948b1f9da09..b5ceabb871941 100644
--- a/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp
+++ b/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp
@@ -60,7 +60,8 @@ static void BM_EmitInfoFunction(benchmark::State &State) {
   Location Loc;
 
   for (auto _ : State) {
-    auto Result = serialize::emitInfo(Func, FC, Loc, /*PublicOnly=*/false);
+    serialize::ClangDocSerializer Serializer;
+    auto Result = Serializer.emitInfo(Func, FC, Loc, /*PublicOnly=*/false);
     benchmark::DoNotOptimize(Result);
   }
 }
diff --git a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
index f59ea02f9e1a3..e1b6a2c3dbf9b 100644
--- a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
@@ -40,7 +40,8 @@ class ClangDocSerializeTestVisitor
 
   template <typename T> bool mapDecl(const T *D) {
     Location Loc(0, 0, "test.cpp");
-    auto [Child, Parent] = serialize::emitInfo(D, getComment(D), Loc, Public);
+    serialize::ClangDocSerializer Serializer;
+    auto [Child, Parent] = Serializer.emitInfo(D, getComment(D), Loc, Public);
     if (Child)
       EmittedInfos.emplace_back(std::move(Child));
     if (Parent)



More information about the llvm-branch-commits mailing list