@@ -5144,6 +5144,14 @@ class SpellingList {
5144
5144
5145
5145
Spellings[(size_t )Kind].push_back (Name);
5146
5146
}
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
+ }
5147
5155
};
5148
5156
5149
5157
class DocumentationData {
@@ -5301,31 +5309,89 @@ void EmitClangAttrDocs(const RecordKeeper &Records, raw_ostream &OS) {
5301
5309
return L->getValueAsString (" Name" ) < R->getValueAsString (" Name" );
5302
5310
}
5303
5311
};
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.
5306
5320
for (const auto *A : Records.getAllDerivedDefinitions (" Attr" )) {
5307
5321
const Record &Attr = *A;
5308
5322
std::vector<const Record *> Docs =
5309
5323
Attr.getValueAsListOfDefs (" Documentation" );
5324
+
5310
5325
for (const auto *D : Docs) {
5311
5326
const Record &Doc = *D;
5312
5327
const Record *Category = Doc.getValueAsDef (" Category" );
5313
5328
// If the category is "InternalOnly", then there cannot be any other
5314
5329
// documentation categories (otherwise, the attribute would be
5315
5330
// 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 )
5319
5333
PrintFatalError (Doc.getLoc (),
5320
5334
" Attribute is \" InternalOnly\" , but has multiple "
5321
5335
" documentation categories" );
5322
5336
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
+ }
5326
5375
}
5327
5376
}
5328
5377
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
+
5329
5395
// Having split the attributes out based on what documentation goes where,
5330
5396
// we can begin to generate sections of documentation.
5331
5397
for (auto &I : SplitDocs) {
0 commit comments