Skip to content

Commit 96bce1c

Browse files
save mixins alongside declarations for symbol alternates (#85)
* save mixins alongside declarations for symbol alternates rdar://133767750 * decode old 'alternateDeclarations' mixins as 'alternateSymbols'
1 parent 4c245d4 commit 96bce1c

File tree

3 files changed

+377
-15
lines changed

3 files changed

+377
-15
lines changed

Sources/SymbolKit/SymbolGraph/Symbol/AlternateDeclarations.swift

+109-10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extension SymbolGraph.Symbol {
1515
///
1616
/// This mixin is created while a symbol graph is decoded, to hold ``DeclarationFragments-swift.struct``
1717
/// for symbols which share the same precise identifier.
18+
@available(*, deprecated, message: "This type is now unused; alternate declaration information is stored in AlternateSymbols instead.")
1819
public struct AlternateDeclarations: Mixin, Codable {
1920
public static let mixinKey = "alternateDeclarations"
2021

@@ -36,23 +37,121 @@ extension SymbolGraph.Symbol {
3637
}
3738
}
3839

40+
/// A mixin to hold alternate symbol information for a symbol.
41+
///
42+
/// This mixin is created while a symbol graph is decoded,
43+
/// to hold distinct information for symbols with the same precise identifier.
44+
public struct AlternateSymbols: Mixin, Codable {
45+
public static let mixinKey = "alternateSymbols"
46+
47+
public struct AlternateSymbol: Codable {
48+
/// The doc comment for the alternate symbol.
49+
public let docComment: SymbolGraph.LineList?
50+
51+
/// The mixins for the alternate symbol.
52+
public var mixins: [String: Mixin] = [:]
53+
54+
public init(from decoder: any Decoder) throws {
55+
let container = try decoder.container(keyedBy: SymbolGraph.Symbol.CodingKeys.self)
56+
self.docComment = try container.decodeIfPresent(SymbolGraph.LineList.self, forKey: .docComment)
57+
58+
for key in container.allKeys {
59+
guard let info = SymbolGraph.Symbol.CodingKeys.mixinCodingInfo[key.stringValue] ?? decoder.registeredSymbolMixins?[key.stringValue] else {
60+
continue
61+
}
62+
63+
mixins[key.stringValue] = try info.decode(container)
64+
}
65+
}
66+
67+
public func encode(to encoder: Encoder) throws {
68+
var container = encoder.container(keyedBy: SymbolGraph.Symbol.CodingKeys.self)
69+
70+
try container.encodeIfPresent(docComment, forKey: .docComment)
71+
72+
for (key, mixin) in mixins {
73+
guard let info = SymbolGraph.Symbol.CodingKeys.mixinCodingInfo[key] ?? encoder.registeredSymbolMixins?[key] else {
74+
continue
75+
}
76+
77+
try info.encode(mixin, &container)
78+
}
79+
}
80+
81+
public init(symbol: SymbolGraph.Symbol) {
82+
self.docComment = symbol.docComment
83+
self.mixins = symbol.mixins
84+
}
85+
86+
public init(declarationFragments: DeclarationFragments) {
87+
self.docComment = nil
88+
self.mixins = [DeclarationFragments.mixinKey: declarationFragments]
89+
}
90+
91+
/// Whether this alternate has no information and should be discarded.
92+
public var isEmpty: Bool {
93+
docComment == nil && mixins.isEmpty
94+
}
95+
96+
/// Convenience accessor to fetch declaration fragments from the mixins dictionary.
97+
public var declarationFragments: DeclarationFragments? {
98+
self.mixins[DeclarationFragments.mixinKey] as? DeclarationFragments
99+
}
100+
101+
/// Convenience accessor to fetch function signature information from the mixins dictionary.
102+
public var functionSignature: FunctionSignature? {
103+
self.mixins[FunctionSignature.mixinKey] as? FunctionSignature
104+
}
105+
}
106+
107+
public init(alternateSymbols: [AlternateSymbol]) {
108+
self.alternateSymbols = alternateSymbols
109+
}
110+
111+
init(alternateDeclarations: AlternateDeclarations) {
112+
self.alternateSymbols = alternateDeclarations.declarations.map({ .init(declarationFragments: $0) })
113+
}
114+
115+
/// The list of alternate symbol information.
116+
public var alternateSymbols: [AlternateSymbol]
117+
}
118+
39119
/// Convenience accessor to fetch alternate declarations for this symbol.
40120
public var alternateDeclarations: [DeclarationFragments]? {
41-
(mixins[AlternateDeclarations.mixinKey] as? AlternateDeclarations)?.declarations
121+
alternateSymbols?.alternateSymbols.compactMap(\.declarationFragments)
42122
}
43123

44-
internal mutating func addAlternateDeclaration(_ declaration: DeclarationFragments) {
45-
if var alternateDeclarations = mixins[AlternateDeclarations.mixinKey] as? AlternateDeclarations {
46-
alternateDeclarations.declarations.append(declaration)
47-
mixins[AlternateDeclarations.mixinKey] = alternateDeclarations
48-
} else {
49-
mixins[AlternateDeclarations.mixinKey] = AlternateDeclarations(declarations: [declaration])
50-
}
124+
/// Convenience accessor to fetch alternate symbol information.
125+
///
126+
/// Returns `nil` if there were no alternate symbols found when decoding the symbol graph.
127+
public var alternateSymbols: AlternateSymbols? {
128+
mixins[AlternateSymbols.mixinKey] as? AlternateSymbols
129+
}
130+
131+
/// Convenience accessor to fetch a specific mixin from this symbol's alternate symbols.
132+
///
133+
/// Because the mixin key is inferred from the type parameter,
134+
/// the easiest way to call this method is by assigning it to a variable with an appropriate type:
135+
///
136+
/// ```swift
137+
/// let alternateFunctionSignatures: [SymbolGraph.Symbol.FunctionSignature]? = symbol.alternateSymbolMixins()
138+
/// ```
139+
///
140+
/// If there are multiple alternate symbols and only some of them have the requested mixin,
141+
/// any missing data is removed from the resulting array via a `compactMap`.
142+
public func alternateSymbolMixins<M: Mixin>(mixinKey: String = M.mixinKey) -> [M]? {
143+
alternateSymbols?.alternateSymbols.compactMap({ $0.mixins[mixinKey] as? M })
51144
}
52145

53146
internal mutating func addAlternateDeclaration(from symbol: SymbolGraph.Symbol) {
54-
if let declaration = symbol.mixins[DeclarationFragments.mixinKey] as? DeclarationFragments {
55-
addAlternateDeclaration(declaration)
147+
let alternate = AlternateSymbols.AlternateSymbol(symbol: symbol)
148+
guard !alternate.isEmpty else { return }
149+
150+
if var alternateSymbols = mixins[AlternateSymbols.mixinKey] as? AlternateSymbols {
151+
alternateSymbols.alternateSymbols.append(alternate)
152+
mixins[AlternateSymbols.mixinKey] = alternateSymbols
153+
} else {
154+
mixins[AlternateSymbols.mixinKey] = AlternateSymbols(alternateSymbols: [alternate])
56155
}
57156
}
58157
}

Sources/SymbolKit/SymbolGraph/Symbol/Symbol.swift

+11-3
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,15 @@ extension SymbolGraph {
151151
docComment = try container.decodeIfPresent(LineList.self, forKey: .docComment)
152152
accessLevel = try container.decode(AccessControl.self, forKey: .accessLevel)
153153
isVirtual = try container.decodeIfPresent(Bool.self, forKey: .isVirtual) ?? false
154-
154+
155+
// Special-case `alternateDeclarations` and decode it into `alternateSymbols`.
156+
// Do this before the next loop so that if both `alternateDeclarations` and
157+
// `alternateSymbols` are present, the latter takes priority.
158+
if container.contains(AlternateDeclarations.symbolCodingInfo.codingKey) {
159+
let alternateDeclarations = try container.decode(AlternateDeclarations.self, forKey: AlternateDeclarations.symbolCodingInfo.codingKey)
160+
mixins[AlternateSymbols.mixinKey] = AlternateSymbols(alternateDeclarations: alternateDeclarations)
161+
}
162+
155163
for key in container.allKeys {
156164
guard let info = CodingKeys.mixinCodingInfo[key.stringValue] ?? decoder.registeredSymbolMixins?[key.stringValue] else {
157165
continue
@@ -240,7 +248,7 @@ extension SymbolGraph.Symbol {
240248
static let httpEndpoint = HTTP.Endpoint.symbolCodingInfo
241249
static let httpParameterSource = HTTP.ParameterSource.symbolCodingInfo
242250
static let httpMediaType = HTTP.MediaType.symbolCodingInfo
243-
static let alternateDeclarations = AlternateDeclarations.symbolCodingInfo
251+
static let alternateSymbols = AlternateSymbols.symbolCodingInfo
244252
static let plistDetails = PlistDetails.symbolCodingInfo
245253

246254
static let mixinCodingInfo: [String: SymbolMixinCodingInfo] = [
@@ -266,7 +274,7 @@ extension SymbolGraph.Symbol {
266274
CodingKeys.httpEndpoint.codingKey.stringValue: Self.httpEndpoint,
267275
CodingKeys.httpParameterSource.codingKey.stringValue: Self.httpParameterSource,
268276
CodingKeys.httpMediaType.codingKey.stringValue: Self.httpMediaType,
269-
CodingKeys.alternateDeclarations.codingKey.stringValue: Self.alternateDeclarations,
277+
CodingKeys.alternateSymbols.codingKey.stringValue: Self.alternateSymbols,
270278
CodingKeys.plistDetails.codingKey.stringValue: Self.plistDetails
271279
]
272280

0 commit comments

Comments
 (0)