I have a pull request up to introduce “near-miss” warnings for protocol 
conformances:

        https://github.com/apple/swift/pull/12645

A “near-miss” warning fires when there is a protocol conformance for which:

1) One of the requirements is satisfied by a “default” definition (e.g., one 
from a protocol extension), and
2) There is a member of the same nominal type declaration (or extension 
declaration) that declared the conformance that has the same name as the 
requirement and isn’t satisfying another requirement.

These are heuristics, of course, and we can tune the heuristics over time. By 
way of experiment, here are the results of running this on the standard 
library. Here’s the first one:

/Users/dgregor/Projects/swift/swift/stdlib/public/core/DoubleWidth.swift.gyb:27:3:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  init(_ _value: (High, Low)) {
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/DoubleWidth.swift.gyb:27:3:
 note: candidate has non-matching type '(DoubleWidth.High, DoubleWidth.Low)'
  init(_ _value: (High, Low)) {
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/DoubleWidth.swift.gyb:27:3:
 note: move 'init' to an extension to silence this warning
  init(_ _value: (High, Low)) {
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^

Unlabeled single-value initializers are probably going to cause a number of 
false positives, because we can’t figure out which one we meant.


/Users/dgregor/Projects/swift/swift/stdlib/public/core/HashedCollections.swift.gyb:835:15:
 warning: instance method 'filter' nearly matches defaulted requirement 
'filter' of protocol 'Sequence'
  public func filter(
              ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/HashedCollections.swift.gyb:835:15:
 note: candidate has non-matching type '<Element> ((Set.Element) throws -> 
Bool) throws -> Set<Element>' (aka '<τ_0_0> ((τ_0_0) throws -> Bool) throws -> 
Set<τ_0_0>') [with Element = Element, Iterator = SetIterator<Element>, 
SubSequence = Slice<Set<Element>>]
  public func filter(
              ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/HashedCollections.swift.gyb:835:15:
 note: move 'filter' to an extension to silence this warning
  public func filter(
              ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Sequence.swift:383:8: 
note: requirement 'filter' declared here
  func filter(
       ^

The filter method it’s warning on produces a Set, whereas the requirement 
expects an Array<Element>. This is a case of intentional overloading, but IMO 
it’s a reasonable thing to warn about.

/Users/dgregor/Projects/swift/swift/stdlib/public/core/HashedCollections.swift.gyb:2021:10:
 warning: subscript 'subscript' nearly matches defaulted requirement 
'subscript' of protocol 'Collection'
  public subscript(key: Key) -> Value? {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/HashedCollections.swift.gyb:2021:10:
 note: candidate has non-matching type '<Key, Value where Key : Hashable> 
(Dictionary.Key) -> Dictionary.Value?' (aka '<τ_0_0, τ_0_1 where τ_0_0 : 
Hashable> (τ_0_0) -> Optional<τ_0_1>') [with IndexDistance = Int, Iterator = 
DictionaryIterator<Key, Value>, SubSequence = Slice<Dictionary<Key, Value>>, 
Indices = DefaultIndices<Dictionary<Key, Value>>]
  public subscript(key: Key) -> Value? {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/HashedCollections.swift.gyb:2021:10:
 note: move 'subscript' to an extension to silence this warning
  public subscript(key: Key) -> Value? {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Collection.swift:454:3: 
note: requirement 'subscript' declared here
  subscript(bounds: Range<Index>) -> SubSequence { get }
  ^

The defaulted Range subscript operation for Dictionary is reasonable, but it 
has a few other subscripts. I think this is a reasonable diagnostic, despite 
being a false positive.


/Users/dgregor/Projects/swift/swift/stdlib/public/core/HashedCollections.swift.gyb:2063:15:
 warning: instance method 'filter' nearly matches defaulted requirement 
'filter' of protocol 'Sequence'
  public func filter(
              ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/HashedCollections.swift.gyb:2063:15:
 note: candidate has non-matching type '<Key, Value> ((Dictionary.Element) 
throws -> Bool) throws -> [Dictionary.Key : Dictionary.Value]' (aka '<τ_0_0, 
τ_0_1> (((key: τ_0_0, value: τ_0_1)) throws -> Bool) throws -> 
Dictionary<τ_0_0, τ_0_1>') [with Iterator = DictionaryIterator<Key, Value>, 
SubSequence = Slice<Dictionary<Key, Value>>]
  public func filter(
              ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/HashedCollections.swift.gyb:2063:15:
 note: move 'filter' to an extension to silence this warning
  public func filter(
              ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Sequence.swift:383:8: 
note: requirement 'filter' declared here
  func filter(
       ^

Same as the “filter” example above, but for Dictionary. This is a reasonable 
diagnostic.

/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 warning: initializer 'init' nearly matches defaulted requirement 'init' of 
protocol 'LosslessStringConvertible'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: candidate has non-matching type 'Float'
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/Integers.swift.gyb:2962:10:
 note: move 'init' to an extension to silence this warning
  public init(_ source: Float) {
         ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/OutputStream.swift:182:3:
 note: requirement 'init' declared here
  init?(_ description: String)
  ^

LosslessStringConvertible’s single-argument, unlabeled initializer is causing a 
*lot* of false positives :(.

/Users/dgregor/Projects/swift/swift/stdlib/public/core/UIntBuffer.swift:202:24: 
warning: instance method 'removeFirst()' nearly matches defaulted requirement 
'removeFirst()' of protocol 'RangeReplaceableCollection'
  public mutating func removeFirst() {
                       ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/UIntBuffer.swift:202:24: 
note: candidate has non-matching type '<Storage, Element> () -> ()'
  public mutating func removeFirst() {
                       ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/UIntBuffer.swift:202:24: 
note: move 'removeFirst()' to another extension to silence this warning
  public mutating func removeFirst() {
                       ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:323:17:
 note: requirement 'removeFirst()' declared here
  mutating func removeFirst() -> Element
                ^

Smells like a bug: we end up getting the default removeFirst() implementation, 
because the one written within the struct doesn’t return the element type.

/Users/dgregor/Projects/swift/swift/stdlib/public/core/ValidUTF8Buffer.swift:178:24:
 warning: instance method 'removeFirst()' nearly matches defaulted requirement 
'removeFirst()' of protocol 'RangeReplaceableCollection'
  public mutating func removeFirst() {
                       ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/ValidUTF8Buffer.swift:178:24:
 note: candidate has non-matching type '<Storage> () -> ()'
  public mutating func removeFirst() {
                       ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/ValidUTF8Buffer.swift:178:24:
 note: move 'removeFirst()' to another extension to silence this warning
  public mutating func removeFirst() {
                       ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:323:17:
 note: requirement 'removeFirst()' declared here
  mutating func removeFirst() -> Element
                ^

Also looks like a bug!

/Users/dgregor/Projects/swift/swift/stdlib/public/core/ValidUTF8Buffer.swift:205:24:
 warning: instance method 'append(contentsOf:)' nearly matches defaulted 
requirement 'append(contentsOf:)' of protocol 'RangeReplaceableCollection'
  public mutating func append<T>(contentsOf other: _ValidUTF8Buffer<T>) {
                       ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/ValidUTF8Buffer.swift:205:24:
 note: candidate has non-matching type '<Storage, T> (contentsOf: 
_ValidUTF8Buffer<T>) -> ()'
  public mutating func append<T>(contentsOf other: _ValidUTF8Buffer<T>) {
                       ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/ValidUTF8Buffer.swift:205:24:
 note: move 'append(contentsOf:)' to another extension to silence this warning
  public mutating func append<T>(contentsOf other: _ValidUTF8Buffer<T>) {
                       ^
/Users/dgregor/Projects/swift/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:200:17:
 note: requirement 'append(contentsOf:)' declared here
  mutating func append<S : Sequence>(contentsOf newElements: S)
                ^

It… might… be a false positive? I don’t really understand why 
_ValidUTF8Buffer’s append here takes a parameterized _ValidUTF8Buffer. At best, 
it’d likely be more efficient to implement a customization of 
append(contentsOf:) in _ValidUTF8Buffer, which would suppress the diagnostic.

Thoughts?

        - Doug

_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Reply via email to