swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.58k stars 10.36k forks source link

[SR-13707] Fix-it for missing let/var in ill-typed expression pattern #56104

Open kavon opened 4 years ago

kavon commented 4 years ago
Previous ID SR-13707
Radar rdar://problem/70075207
Original Reporter @kavon
Type Improvement
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Improvement, DiagnosticsQoI, TypeChecker | |Assignee | None | |Priority | Medium | md5: 56d9c5d34022ff4984c7e5bdb640d5e2

relates to:

Issue Description:

Consider this incorrect code:

enum List {
    indirect case Cons(Int, List)
    case Nil
}

func hd(_ lst : List) -> Int? {
    switch lst {
        case .Cons(hd, _): return hd
        case .Nil: return nil
    }
}

let lst = List.Cons(1, List.Nil)
if let h = hd(lst) {
    print(h)
}

The programmer (me) thought they were introducing a new binding, `hd` in the Cons case pattern. But, that requires a let/var in the pattern, so the compiler interprets it as an expression pattern. Not to be confused with SR-13706, here `hd` is bound as the name of the function itself, so we get the following type error:

missing_let_int.swift:9:20: error: expression pattern of type '(List) -> Int?' cannot match values of type 'Int'
        case .Cons(hd, _): return hd
                   ^~
missing_let_int.swift:9:35: error: cannot convert return expression of type '(List) -> Int?' to return type 'Int?'
        case .Cons(hd, _): return hd
                                  ^~

In this case, a fix-it may be warranted to suggest that they add a let/var in the pattern. The errors can get quite nasty and confusing when making this mistake for a more complex enum type, such as in this code:

enum List<T> {
    indirect case Cons(T, List<T>)
    case Nil
}

func hd<T>(_ lst : List<T>) -> T? {
    switch lst {
        case .Cons(hd, _): return hd
        case .Nil: return nil
    }
}

let lst = List.Cons(1, List.Nil)
if let h = hd(lst) {
    print(h)
}

Which gives all of these errors:

missing_let.swift:9:20: error: expression pattern of type '(List<T>) -> T?' cannot match values of type 'T'
        case .Cons(hd, _): return hd
                   ^~
missing_let.swift:9:20: error: generic parameter 'T' could not be inferred
        case .Cons(hd, _): return hd
                   ^
missing_let.swift:7:6: note: in call to function 'hd'
func hd<T>(_ lst : List<T>) -> T? {
     ^
missing_let.swift:9:35: error: cannot convert return expression of type '(List<T>) -> T?' to return type 'T?'
        case .Cons(hd, _): return hd
                                  ^~
                                     as! T
missing_let.swift:9:35: error: generic parameter 'T' could not be inferred
        case .Cons(hd, _): return hd
                                  ^
missing_let.swift:7:6: note: in call to function 'hd'
func hd<T>(_ lst : List<T>) -> T? {
     ^

theindigamer (JIRA User) agrees that a fix-it may be good here.

typesanitizer commented 4 years ago

@swift-ci create