@@ -76,6 +76,8 @@ struct SymbolGraphLoader {
76
76
symbolGraph = try SymbolGraphConcurrentDecoder . decode ( data)
77
77
}
78
78
79
+ Self . applyWorkaroundFor139305015 ( to: & symbolGraph)
80
+
79
81
symbolGraphTransformer ? ( & symbolGraph)
80
82
81
83
let ( moduleName, isMainSymbolGraph) = Self . moduleNameFor ( symbolGraph, at: symbolGraphURL)
@@ -362,6 +364,62 @@ struct SymbolGraphLoader {
362
364
}
363
365
return ( moduleName, isMainSymbolGraph)
364
366
}
367
+
368
+ private static func applyWorkaroundFor139305015( to symbolGraph: inout SymbolGraph ) {
369
+ guard symbolGraph. symbols. values. mapFirst ( where: { SourceLanguage ( id: $0. identifier. interfaceLanguage) } ) == . objectiveC else {
370
+ return
371
+ }
372
+
373
+ // Clang emits anonymous structs and unions differently than anonymous enums (rdar://139305015).
374
+ //
375
+ // The anonymous structs, with empty names, causes issues in a few different places for DocC:
376
+ // - The IndexingRecords (one of the `--emit-digest` files) throws an error about the empty name.
377
+ // - The NavigatorIndex.Builder may throw an error about the empty name.
378
+ // - Their pages can't be navigated to because their URL path end with a leading slash.
379
+ // The corresponding static hosting 'index.html' copy also overrides the container's index.html file because
380
+ // its file path has two slashes, for example "/documentation/ModuleName/ContainerName//index.html".
381
+ //
382
+ // To avoid all those issues without handling empty names throughout the code,
383
+ // we fill in titles and navigator titles for these symbols using the same format as Clang uses for anonymous enums.
384
+
385
+ let relationshipsByTarget = [ String: [ SymbolGraph . Relationship] ] ( grouping: symbolGraph. relationships, by: \. target)
386
+
387
+ for (usr, symbol) in symbolGraph. symbols {
388
+ guard symbol. names. title. isEmpty,
389
+ symbol. names. navigator? . map ( \. spelling) . joined ( ) . isEmpty == true ,
390
+ symbol. pathComponents. last? . isEmpty == true
391
+ else {
392
+ continue
393
+ }
394
+
395
+ // This symbol has an empty title and an empty navigator title.
396
+ var modified = symbol
397
+ let fallbackTitle = " \( symbol. kind. identifier. identifier) (unnamed) "
398
+ modified. names. title = fallbackTitle
399
+ // Clang uses a single `identifier` fragment for anonymous enums.
400
+ modified. names. navigator = [ . init( kind: . identifier, spelling: fallbackTitle, preciseIdentifier: nil ) ]
401
+ // Don't update `modified.names.subHeading`. Clang _doesn't_ use "enum (unnamed)" for the `Symbol/Names/subHeading` so we don't add it here either.
402
+
403
+ // Clang uses the "enum (unnamed)" in the path components of anonymous enums so we follow that format for anonymous structs.
404
+ modified. pathComponents [ modified. pathComponents. count - 1 ] = fallbackTitle
405
+ symbolGraph. symbols [ usr] = modified
406
+
407
+ // Also update all the members whose path components start with the container's path components so that they're consistent.
408
+ if let relationships = relationshipsByTarget [ usr] {
409
+ let containerPathComponents = modified. pathComponents
410
+
411
+ for memberRelationship in relationships where memberRelationship. kind == . memberOf {
412
+ guard var modifiedMember = symbolGraph. symbols. removeValue ( forKey: memberRelationship. source) else { continue }
413
+ // Only update the member's path components if it starts with the original container's components.
414
+ guard modifiedMember. pathComponents. starts ( with: symbol. pathComponents) else { continue }
415
+
416
+ modifiedMember. pathComponents. replaceSubrange ( containerPathComponents. indices, with: containerPathComponents)
417
+
418
+ symbolGraph. symbols [ memberRelationship. source] = modifiedMember
419
+ }
420
+ }
421
+ }
422
+ }
365
423
}
366
424
367
425
extension SymbolGraph . SemanticVersion {
0 commit comments