Skip to content

[clang-doc] mangle specialization file names #144617

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

evelez7
Copy link
Member

@evelez7 evelez7 commented Jun 17, 2025

Currently, class template specializations are written to the same file as the template. This presents a problem in JSON since only one JSON object should be written per file. This patch adds a mangled name field to TemplateSpecializationInfo to distinguish files during documentation generation.

Copy link
Member Author

evelez7 commented Jun 17, 2025

This stack of pull requests is managed by Graphite. Learn more about stacking.

@evelez7 evelez7 requested review from ilovepi and petrhosek June 18, 2025 16:35
llvm::raw_string_ostream Stream(MangledName);
Mangler->mangleCXXVTT(dyn_cast<CXXRecordDecl>(D), Stream);
Specialization.MangledName.emplace(MangledName);
delete Mangler;
Copy link
Member Author

Choose a reason for hiding this comment

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

It'd be nice to keep this mangler somewhere so that it's not allocated/deleted constantly. If it was kept in the Clang-Doc Context, it'd have to be passed specifically to this emitInfo which would break the pattern we have.

@evelez7 evelez7 marked this pull request as ready for review June 18, 2025 16:47
@llvmbot
Copy link
Member

llvmbot commented Jun 18, 2025

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

Author: Erick Velez (evelez7)

Changes

Currently, class template specializations are written to the same file as the template. This presents a problem in JSON since only one JSON object should be written per file. This patch adds a mangled name field to TemplateSpecializationInfo to distinguish files during documentation generation.


Full diff: https://github.com/llvm/llvm-project/pull/144617.diff

7 Files Affected:

  • (modified) clang-tools-extra/clang-doc/BitcodeReader.cpp (+9)
  • (modified) clang-tools-extra/clang-doc/BitcodeWriter.cpp (+6-1)
  • (modified) clang-tools-extra/clang-doc/BitcodeWriter.h (+1)
  • (modified) clang-tools-extra/clang-doc/JSONGenerator.cpp (+8-1)
  • (modified) clang-tools-extra/clang-doc/Representation.h (+3)
  • (modified) clang-tools-extra/clang-doc/Serialize.cpp (+8)
  • (added) clang-tools-extra/test/clang-doc/json/specialization-mangled-name.cpp (+15)
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 35058abab0663..9227cf2bf51a2 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -83,6 +83,13 @@ static llvm::Error decodeRecord(const Record &R, std::optional<Location> &Field,
   return llvm::Error::success();
 }
 
+static llvm::Error decodeRecord(const Record &R,
+                                std::optional<SmallString<16>> &Field,
+                                llvm::StringRef Blob) {
+  Field.emplace(Blob);
+  return llvm::Error::success();
+}
+
 static llvm::Error decodeRecord(const Record &R, InfoType &Field,
                                 llvm::StringRef Blob) {
   switch (auto IT = static_cast<InfoType>(R[0])) {
@@ -379,6 +386,8 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
                                TemplateSpecializationInfo *I) {
   if (ID == TEMPLATE_SPECIALIZATION_OF)
     return decodeRecord(R, I->SpecializationOf, Blob);
+  if (ID == TEMPLATE_SPECIALIZATION_MANGLED_NAME)
+    return decodeRecord(R, I->MangledName, Blob);
   return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                  "invalid field for TemplateParamInfo");
 }
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index f8a6859169b01..e2c58731fbc67 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -202,6 +202,8 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
           {TEMPLATE_PARAM_CONTENTS, {"Contents", &genStringAbbrev}},
           {TEMPLATE_SPECIALIZATION_OF,
            {"SpecializationOf", &genSymbolIdAbbrev}},
+          {TEMPLATE_SPECIALIZATION_MANGLED_NAME,
+           {"MangledName", &genStringAbbrev}},
           {TYPEDEF_USR, {"USR", &genSymbolIdAbbrev}},
           {TYPEDEF_NAME, {"Name", &genStringAbbrev}},
           {TYPEDEF_DEFLOCATION, {"DefLocation", &genLocationAbbrev}},
@@ -263,7 +265,8 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
         // Template Blocks.
         {BI_TEMPLATE_BLOCK_ID, {}},
         {BI_TEMPLATE_PARAM_BLOCK_ID, {TEMPLATE_PARAM_CONTENTS}},
-        {BI_TEMPLATE_SPECIALIZATION_BLOCK_ID, {TEMPLATE_SPECIALIZATION_OF}}};
+        {BI_TEMPLATE_SPECIALIZATION_BLOCK_ID,
+         {TEMPLATE_SPECIALIZATION_OF, TEMPLATE_SPECIALIZATION_MANGLED_NAME}}};
 
 // AbbreviationMap
 
@@ -638,6 +641,8 @@ void ClangDocBitcodeWriter::emitBlock(const TemplateInfo &T) {
 void ClangDocBitcodeWriter::emitBlock(const TemplateSpecializationInfo &T) {
   StreamSubBlockGuard Block(Stream, BI_TEMPLATE_SPECIALIZATION_BLOCK_ID);
   emitRecord(T.SpecializationOf, TEMPLATE_SPECIALIZATION_OF);
+  if (T.MangledName)
+    emitRecord(T.MangledName->str(), TEMPLATE_SPECIALIZATION_MANGLED_NAME);
   for (const auto &P : T.Params)
     emitBlock(P);
 }
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index e33a1aece883c..c0b879af59194 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -131,6 +131,7 @@ enum RecordId {
   REFERENCE_FIELD,
   TEMPLATE_PARAM_CONTENTS,
   TEMPLATE_SPECIALIZATION_OF,
+  TEMPLATE_SPECIALIZATION_MANGLED_NAME,
   TYPEDEF_USR,
   TYPEDEF_NAME,
   TYPEDEF_DEFLOCATION,
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 0f7cbafcf5135..ca56e669038b4 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -491,7 +491,14 @@ Error JSONGenerator::generateDocs(
       CreatedDirs.insert(Path);
     }
 
-    sys::path::append(Path, Info->getFileBaseName() + ".json");
+    SmallString<16> FileBaseName = Info->getFileBaseName();
+    if (Info->IT == InfoType::IT_record) {
+      if (auto Template = static_cast<RecordInfo *>(Info)->Template;
+          Template && Template->Specialization &&
+          Template->Specialization->MangledName)
+        FileBaseName = Template->Specialization->MangledName.value();
+    }
+    sys::path::append(Path, FileBaseName + ".json");
     FileToInfos[Path].push_back(Info);
   }
 
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 75da500645819..6c88712706dc0 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -209,6 +209,9 @@ struct TemplateSpecializationInfo {
 
   // Template parameters applying to the specialized record/function.
   std::vector<TemplateParamInfo> Params;
+
+  // Used to distinguish class specialization file names.
+  std::optional<SmallString<16>> MangledName;
 };
 
 // Records the template information for a struct or function that is a template
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index e8f1a9cee2675..d82d2cd2c9eeb 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -10,6 +10,7 @@
 #include "BitcodeWriter.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/Comment.h"
+#include "clang/AST/Mangle.h"
 #include "clang/Index/USRGeneration.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/StringExtras.h"
@@ -909,6 +910,13 @@ emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
       RI->Template.emplace();
     RI->Template->Specialization.emplace();
     auto &Specialization = *RI->Template->Specialization;
+    auto *Mangler = ItaniumMangleContext::create(
+        D->getASTContext(), D->getASTContext().getDiagnostics());
+    std::string MangledName;
+    llvm::raw_string_ostream Stream(MangledName);
+    Mangler->mangleCXXVTT(dyn_cast<CXXRecordDecl>(D), Stream);
+    Specialization.MangledName.emplace(MangledName);
+    delete Mangler;
 
     // What this is a specialization of.
     auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
diff --git a/clang-tools-extra/test/clang-doc/json/specialization-mangled-name.cpp b/clang-tools-extra/test/clang-doc/json/specialization-mangled-name.cpp
new file mode 100644
index 0000000000000..5d25c53f37a36
--- /dev/null
+++ b/clang-tools-extra/test/clang-doc/json/specialization-mangled-name.cpp
@@ -0,0 +1,15 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: clang-doc --output=%t --format=json --executor=standalone %s
+// RUN: FileCheck %s < %t/GlobalNamespace/_ZTT7MyClassIiE.json
+
+template<typename T> class MyClass;
+
+template<> class MyClass<int>;
+
+// CHECK:       "Name": "MyClass",
+// CHECK:       "Template": {
+// CHECK-NEXT:    "Specialization": {
+// CHECK-NEXT:      "Parameters": [
+// CHECK-NEXT:        "int"
+// CHECK-NEXT:      ],
+// CHECK-NEXT:      "SpecializationOf": "{{[0-9A-F]*}}"

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.

2 participants