Skip to content

Commit fb0c2d5

Browse files
add 'AlternateDeclarations' mixin (#60)
rdar://111227476
1 parent 31ee554 commit fb0c2d5

File tree

4 files changed

+101
-4
lines changed

4 files changed

+101
-4
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2023 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import Foundation
12+
13+
extension SymbolGraph.Symbol {
14+
/// A mixin to hold alternate declarations of a symbol.
15+
///
16+
/// This mixin is created while a symbol graph is decoded, to hold ``DeclarationFragments-swift.struct``
17+
/// for symbols which share the same precise identifier.
18+
public struct AlternateDeclarations: Mixin, Codable {
19+
public static let mixinKey = "alternateDeclarations"
20+
21+
/// Alternate declarations for this symbol.
22+
public var declarations: [DeclarationFragments]
23+
24+
public init(declarations: [DeclarationFragments]) {
25+
self.declarations = declarations
26+
}
27+
28+
public init(from decoder: Decoder) throws {
29+
let container = try decoder.singleValueContainer()
30+
declarations = try container.decode([DeclarationFragments].self)
31+
}
32+
33+
public func encode(to encoder: Encoder) throws {
34+
var container = encoder.singleValueContainer()
35+
try container.encode(declarations)
36+
}
37+
}
38+
39+
/// Convenience accessor to fetch alternate declarations for this symbol.
40+
public var alternateDeclarations: [DeclarationFragments]? {
41+
(mixins[AlternateDeclarations.mixinKey] as? AlternateDeclarations)?.declarations
42+
}
43+
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+
}
51+
}
52+
53+
internal mutating func addAlternateDeclaration(from symbol: SymbolGraph.Symbol) {
54+
if let declaration = symbol.mixins[DeclarationFragments.mixinKey] as? DeclarationFragments {
55+
addAlternateDeclaration(declaration)
56+
}
57+
}
58+
}

Sources/SymbolKit/SymbolGraph/Symbol/Symbol.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ extension SymbolGraph.Symbol {
236236
static let httpEndpoint = HTTP.Endpoint.symbolCodingInfo
237237
static let httpParameterSource = HTTP.ParameterSource.symbolCodingInfo
238238
static let httpMediaType = HTTP.MediaType.symbolCodingInfo
239-
239+
static let alternateDeclarations = AlternateDeclarations.symbolCodingInfo
240+
240241
static let mixinCodingInfo: [String: SymbolMixinCodingInfo] = [
241242
CodingKeys.availability.codingKey.stringValue: Self.availability,
242243
CodingKeys.declarationFragments.codingKey.stringValue: Self.declarationFragments,
@@ -260,6 +261,7 @@ extension SymbolGraph.Symbol {
260261
CodingKeys.httpEndpoint.codingKey.stringValue: Self.httpEndpoint,
261262
CodingKeys.httpParameterSource.codingKey.stringValue: Self.httpParameterSource,
262263
CodingKeys.httpMediaType.codingKey.stringValue: Self.httpMediaType,
264+
CodingKeys.alternateDeclarations.codingKey.stringValue: Self.alternateDeclarations,
263265
]
264266

265267
static func == (lhs: SymbolGraph.Symbol.CodingKeys, rhs: SymbolGraph.Symbol.CodingKeys) -> Bool {

Sources/SymbolKit/SymbolGraph/SymbolGraph.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,37 @@ public struct SymbolGraph: Codable {
6262

6363
public static func _symbolToKeepInCaseOfPreciseIdentifierConflict(_ lhs: Symbol, _ rhs: Symbol) -> Symbol {
6464
if lhs.declarationContainsAsyncKeyword() {
65-
return rhs
65+
var result = rhs
66+
result.addAlternateDeclaration(from: lhs)
67+
return result
6668
} else if rhs.declarationContainsAsyncKeyword() {
67-
return lhs
69+
var result = lhs
70+
result.addAlternateDeclaration(from: rhs)
71+
return result
6872
} else {
6973
// It's not expected to ever end up here, but if we do, we return the symbol with the longer name
7074
// to have consistent results.
71-
return lhs.names.title.count < rhs.names.title.count ? rhs : lhs
75+
var result: Symbol
76+
let other: Symbol
77+
if lhs.names.title.count < rhs.names.title.count {
78+
result = rhs
79+
other = lhs
80+
} else if rhs.names.title.count < lhs.names.title.count {
81+
result = lhs
82+
other = rhs
83+
} else {
84+
// If, by total coincidence, both symbols have the same length, try a lexicographic
85+
// sort and pick the first one.
86+
if lhs.names.title <= rhs.names.title {
87+
result = lhs
88+
other = rhs
89+
} else {
90+
result = rhs
91+
other = lhs
92+
}
93+
}
94+
result.addAlternateDeclaration(from: other)
95+
return result
7296
}
7397
}
7498
}

Tests/SymbolKitTests/SymbolGraph/SymbolGraphTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ class SymbolGraphTests: XCTestCase {
6363
XCTAssertTrue(declaration.declarationFragments.contains(where: { fragment in
6464
fragment.kind == .externalParameter && fragment.spelling == "completionHandler"
6565
}))
66+
67+
// The async declaration should have been saved as an alternate declaration
68+
let alternateDeclarations = try XCTUnwrap(symbol.alternateDeclarations)
69+
XCTAssertEqual(alternateDeclarations.count, 1)
70+
let alternate = alternateDeclarations[0]
71+
72+
XCTAssertTrue(alternate.declarationFragments.contains(where: { fragment in
73+
fragment.kind == .keyword && fragment.spelling == "async"
74+
}))
75+
76+
XCTAssertFalse(alternate.declarationFragments.contains(where: { fragment in
77+
fragment.kind == .externalParameter && fragment.spelling == "completionHandler"
78+
}))
6679
}
6780
}
6881

0 commit comments

Comments
 (0)