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:  after the patch:  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