Author: YLChenZ
Date: 2025-04-07T14:42:57-04:00
New Revision: 529e9127f0d61f177f2c62ade531d876cf89bf59

URL: 
https://github.com/llvm/llvm-project/commit/529e9127f0d61f177f2c62ade531d876cf89bf59
DIFF: 
https://github.com/llvm/llvm-project/commit/529e9127f0d61f177f2c62ade531d876cf89bf59.diff

LOG: [clang][doc]: Merge entries with duplicate content. (#134089)

Before the patch:

![docb4](https://github.com/user-attachments/assets/6db1000f-d555-48b8-8a19-85c41b043fd8)
after the patch:

![doc-after](https://github.com/user-attachments/assets/1cff64b6-db2e-48d8-b0a9-a403fd61f8af)

Fixes #133706

Added: 
    

Modified: 
    clang/utils/TableGen/ClangAttrEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 06f6b073240ba..07844c8f0967b 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -5144,6 +5144,14 @@ class SpellingList {
 
     Spellings[(size_t)Kind].push_back(Name);
   }
+
+  void merge(const SpellingList &Other) {
+    for (size_t Kind = 0; Kind < NumSpellingKinds; ++Kind) {
+      Spellings[Kind].insert(Spellings[Kind].end(),
+                             Other.Spellings[Kind].begin(),
+                             Other.Spellings[Kind].end());
+    }
+  }
 };
 
 class DocumentationData {
@@ -5301,31 +5309,89 @@ void EmitClangAttrDocs(const RecordKeeper &Records, 
raw_ostream &OS) {
       return L->getValueAsString("Name") < R->getValueAsString("Name");
     }
   };
-  std::map<const Record *, std::vector<DocumentationData>, CategoryLess>
-      SplitDocs;
+
+  std::map<const Record *, std::map<uint32_t, DocumentationData>, CategoryLess>
+      MergedDocs;
+
+  std::vector<DocumentationData> UndocumentedDocs;
+  const Record *UndocumentedCategory = nullptr;
+
+  // Collect documentation data, grouping by category and heading.
   for (const auto *A : Records.getAllDerivedDefinitions("Attr")) {
     const Record &Attr = *A;
     std::vector<const Record *> Docs =
         Attr.getValueAsListOfDefs("Documentation");
+
     for (const auto *D : Docs) {
       const Record &Doc = *D;
       const Record *Category = Doc.getValueAsDef("Category");
       // If the category is "InternalOnly", then there cannot be any other
       // documentation categories (otherwise, the attribute would be
       // emitted into the docs).
-      const StringRef Cat = Category->getValueAsString("Name");
-      bool InternalOnly = Cat == "InternalOnly";
-      if (InternalOnly && Docs.size() > 1)
+      StringRef Cat = Category->getValueAsString("Name");
+      if (Cat == "InternalOnly" && Docs.size() > 1)
         PrintFatalError(Doc.getLoc(),
                         "Attribute is \"InternalOnly\", but has multiple "
                         "documentation categories");
 
-      if (!InternalOnly)
-        SplitDocs[Category].push_back(DocumentationData(
-            Doc, Attr, GetAttributeHeadingAndSpellings(Doc, Attr, Cat)));
+      if (Cat == "InternalOnly")
+        continue;
+
+      // Track the Undocumented category Record for later grouping
+      if (Cat == "Undocumented" && !UndocumentedCategory)
+        UndocumentedCategory = Category;
+
+      // Generate Heading and Spellings.
+      auto HeadingAndSpellings =
+          GetAttributeHeadingAndSpellings(Doc, Attr, Cat);
+
+      // Handle Undocumented category separately - no content merging
+      if (Cat == "Undocumented" && UndocumentedCategory) {
+        UndocumentedDocs.push_back(
+            DocumentationData(Doc, Attr, HeadingAndSpellings));
+        continue;
+      }
+
+      auto &CategoryDocs = MergedDocs[Category];
+
+      std::string key = Doc.getValueAsString("Content").str();
+      uint32_t keyHash = llvm::hash_value(key);
+
+      // If the content already exists, merge the documentation.
+      auto It = CategoryDocs.find(keyHash);
+      if (It != CategoryDocs.end()) {
+        // Merge heading
+        if (It->second.Heading != HeadingAndSpellings.first)
+          It->second.Heading += ", " + HeadingAndSpellings.first;
+        // Merge spellings
+        It->second.SupportedSpellings.merge(HeadingAndSpellings.second);
+        // Merge content
+        It->second.Documentation = &Doc; // Update reference
+      } else {
+        // Create new entry for unique content
+        CategoryDocs.emplace(keyHash,
+                             DocumentationData(Doc, Attr, 
HeadingAndSpellings));
+      }
     }
   }
 
+  std::map<const Record *, std::vector<DocumentationData>, CategoryLess>
+      SplitDocs;
+
+  for (auto &CategoryPair : MergedDocs) {
+
+    std::vector<DocumentationData> MD;
+    for (auto &DocPair : CategoryPair.second)
+      MD.push_back(std::move(DocPair.second));
+
+    SplitDocs.emplace(CategoryPair.first, MD);
+  }
+
+  // Append Undocumented category entries
+  if (!UndocumentedDocs.empty() && UndocumentedCategory) {
+    SplitDocs.emplace(UndocumentedCategory, UndocumentedDocs);
+  }
+
   // Having split the attributes out based on what documentation goes where,
   // we can begin to generate sections of documentation.
   for (auto &I : SplitDocs) {


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to