Skip to content

Commit bdcb2a5

Browse files
correct Version's Hashable conformance
Because we have a non-synthesised `Equatable` conformance, the synthesised `Hashable` conformance composed of member-wise hashes is incorrect. `buildMetadataIdentifiers` does not participate in `Version`'s `Equatable` conformance, so it shouldn't participate in `Version`'s `Hashable` conformance either. Relevant: [SR-11588](https://bugs.swift.org/browse/SR-11588)
1 parent f089631 commit bdcb2a5

File tree

1 file changed

+18
-10
lines changed

1 file changed

+18
-10
lines changed

Sources/TSCUtility/Version.swift

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import TSCBasic
1212

1313
/// A struct representing a semver version.
14-
public struct Version: Hashable {
14+
public struct Version {
1515

1616
/// The major version.
1717
public let major: Int
@@ -45,7 +45,7 @@ public struct Version: Hashable {
4545
}
4646
}
4747

48-
extension Version: Comparable {
48+
extension Version: Comparable, Hashable {
4949

5050
func isEqualWithoutPrerelease(_ other: Version) -> Bool {
5151
return major == other.major && minor == other.minor && patch == other.patch
@@ -95,6 +95,14 @@ extension Version: Comparable {
9595
return lhs.prereleaseIdentifiers.count < rhs.prereleaseIdentifiers.count
9696
}
9797

98+
// Custom `Equatable` conformance leads to custom `Hashable` conformance.
99+
// [SR-11588](https://bugs.swift.org/browse/SR-11588)
100+
public func hash(into hasher: inout Hasher) {
101+
hasher.combine(major)
102+
hasher.combine(minor)
103+
hasher.combine(patch)
104+
hasher.combine(prereleaseIdentifiers)
105+
}
98106
}
99107

100108
extension Version: CustomStringConvertible {
@@ -138,22 +146,22 @@ extension Version: LosslessStringConvertible {
138146
self.minor = minor
139147
self.patch = patch
140148

141-
if prereleaseDelimiterIndex == nil {
142-
self.prereleaseIdentifiers = []
143-
} else {
144-
let prereleaseStartIndex = prereleaseDelimiterIndex.map(versionString.index(after:)) ?? metadataDelimiterIndex ?? versionString.endIndex
149+
if let prereleaseDelimiterIndex = prereleaseDelimiterIndex {
150+
let prereleaseStartIndex = versionString.index(after: prereleaseDelimiterIndex)
145151
let prereleaseIdentifiers = versionString[prereleaseStartIndex..<(metadataDelimiterIndex ?? versionString.endIndex)].split(separator: ".", omittingEmptySubsequences: false)
146152
guard prereleaseIdentifiers.allSatisfy( { $0.allSatisfy { $0.isLetter || $0.isNumber || $0 == "-" } } ) else { return nil }
147153
self.prereleaseIdentifiers = prereleaseIdentifiers.map { String($0) }
154+
} else {
155+
self.prereleaseIdentifiers = []
148156
}
149157

150-
if metadataDelimiterIndex == nil {
151-
self.buildMetadataIdentifiers = []
152-
} else {
153-
let metadataStartIndex = metadataDelimiterIndex.map(versionString.index(after:)) ?? versionString.endIndex
158+
if let metadataDelimiterIndex = metadataDelimiterIndex {
159+
let metadataStartIndex = versionString.index(after: metadataDelimiterIndex)
154160
let buildMetadataIdentifiers = versionString[metadataStartIndex...].split(separator: ".", omittingEmptySubsequences: false)
155161
guard buildMetadataIdentifiers.allSatisfy( { $0.allSatisfy { $0.isLetter || $0.isNumber || $0 == "-" } } ) else { return nil }
156162
self.buildMetadataIdentifiers = buildMetadataIdentifiers.map { String($0) }
163+
} else {
164+
self.buildMetadataIdentifiers = []
157165
}
158166
}
159167
}

0 commit comments

Comments
 (0)