@@ -5144,6 +5144,14 @@ class SpellingList {
51445144
51455145 Spellings[(size_t )Kind].push_back (Name);
51465146 }
5147+
5148+ void merge (const SpellingList &Other) {
5149+ for (size_t Kind = 0 ; Kind < NumSpellingKinds; ++Kind) {
5150+ Spellings[Kind].insert (Spellings[Kind].end (),
5151+ Other.Spellings [Kind].begin (),
5152+ Other.Spellings [Kind].end ());
5153+ }
5154+ }
51475155};
51485156
51495157class DocumentationData {
@@ -5301,31 +5309,89 @@ void EmitClangAttrDocs(const RecordKeeper &Records, raw_ostream &OS) {
53015309 return L->getValueAsString (" Name" ) < R->getValueAsString (" Name" );
53025310 }
53035311 };
5304- std::map<const Record *, std::vector<DocumentationData>, CategoryLess>
5305- SplitDocs;
5312+
5313+ std::map<const Record *, std::map<uint32_t , DocumentationData>, CategoryLess>
5314+ MergedDocs;
5315+
5316+ std::vector<DocumentationData> UndocumentedDocs;
5317+ const Record *UndocumentedCategory = nullptr ;
5318+
5319+ // Collect documentation data, grouping by category and heading.
53065320 for (const auto *A : Records.getAllDerivedDefinitions (" Attr" )) {
53075321 const Record &Attr = *A;
53085322 std::vector<const Record *> Docs =
53095323 Attr.getValueAsListOfDefs (" Documentation" );
5324+
53105325 for (const auto *D : Docs) {
53115326 const Record &Doc = *D;
53125327 const Record *Category = Doc.getValueAsDef (" Category" );
53135328 // If the category is "InternalOnly", then there cannot be any other
53145329 // documentation categories (otherwise, the attribute would be
53155330 // emitted into the docs).
5316- const StringRef Cat = Category->getValueAsString (" Name" );
5317- bool InternalOnly = Cat == " InternalOnly" ;
5318- if (InternalOnly && Docs.size () > 1 )
5331+ StringRef Cat = Category->getValueAsString (" Name" );
5332+ if (Cat == " InternalOnly" && Docs.size () > 1 )
53195333 PrintFatalError (Doc.getLoc (),
53205334 " Attribute is \" InternalOnly\" , but has multiple "
53215335 " documentation categories" );
53225336
5323- if (!InternalOnly)
5324- SplitDocs[Category].push_back (DocumentationData (
5325- Doc, Attr, GetAttributeHeadingAndSpellings (Doc, Attr, Cat)));
5337+ if (Cat == " InternalOnly" )
5338+ continue ;
5339+
5340+ // Track the Undocumented category Record for later grouping
5341+ if (Cat == " Undocumented" && !UndocumentedCategory)
5342+ UndocumentedCategory = Category;
5343+
5344+ // Generate Heading and Spellings.
5345+ auto HeadingAndSpellings =
5346+ GetAttributeHeadingAndSpellings (Doc, Attr, Cat);
5347+
5348+ // Handle Undocumented category separately - no content merging
5349+ if (Cat == " Undocumented" && UndocumentedCategory) {
5350+ UndocumentedDocs.push_back (
5351+ DocumentationData (Doc, Attr, HeadingAndSpellings));
5352+ continue ;
5353+ }
5354+
5355+ auto &CategoryDocs = MergedDocs[Category];
5356+
5357+ std::string key = Doc.getValueAsString (" Content" ).str ();
5358+ uint32_t keyHash = llvm::hash_value (key);
5359+
5360+ // If the content already exists, merge the documentation.
5361+ auto It = CategoryDocs.find (keyHash);
5362+ if (It != CategoryDocs.end ()) {
5363+ // Merge heading
5364+ if (It->second .Heading != HeadingAndSpellings.first )
5365+ It->second .Heading += " , " + HeadingAndSpellings.first ;
5366+ // Merge spellings
5367+ It->second .SupportedSpellings .merge (HeadingAndSpellings.second );
5368+ // Merge content
5369+ It->second .Documentation = &Doc; // Update reference
5370+ } else {
5371+ // Create new entry for unique content
5372+ CategoryDocs.emplace (keyHash,
5373+ DocumentationData (Doc, Attr, HeadingAndSpellings));
5374+ }
53265375 }
53275376 }
53285377
5378+ std::map<const Record *, std::vector<DocumentationData>, CategoryLess>
5379+ SplitDocs;
5380+
5381+ for (auto &CategoryPair : MergedDocs) {
5382+
5383+ std::vector<DocumentationData> MD;
5384+ for (auto &DocPair : CategoryPair.second )
5385+ MD.push_back (std::move (DocPair.second ));
5386+
5387+ SplitDocs.emplace (CategoryPair.first , MD);
5388+ }
5389+
5390+ // Append Undocumented category entries
5391+ if (!UndocumentedDocs.empty () && UndocumentedCategory) {
5392+ SplitDocs.emplace (UndocumentedCategory, UndocumentedDocs);
5393+ }
5394+
53295395 // Having split the attributes out based on what documentation goes where,
53305396 // we can begin to generate sections of documentation.
53315397 for (auto &I : SplitDocs) {
0 commit comments