mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Expression patterns (and cast patterns) don't actually contribute to the exhaustivity of a switch statement---if you're matching against a String, matching "abc" doesn't meaningfully reduce the full space of the values you have to match. This was already handled, but didn't do the right thing in a particular case involving a tuple payload in an enum after the Space Engine (exhaustivity checker) optimizations that went out in Swift 5. https://bugs.swift.org/browse/SR-10301
1178 lines
30 KiB
Swift
1178 lines
30 KiB
Swift
// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-library-evolution
|
|
// RUN: %target-typecheck-verify-swift -swift-version 4 -enable-library-evolution -enable-nonfrozen-enum-exhaustivity-diagnostics
|
|
|
|
func foo(a: Int?, b: Int?) -> Int {
|
|
switch (a, b) {
|
|
case (.none, _): return 1
|
|
case (_, .none): return 2
|
|
case (.some(_), .some(_)): return 3
|
|
}
|
|
|
|
switch (a, b) {
|
|
case (.none, _): return 1
|
|
case (_, .none): return 2
|
|
case (_?, _?): return 3
|
|
}
|
|
|
|
switch Optional<(Int?, Int?)>.some((a, b)) {
|
|
case .none: return 1
|
|
case let (_, x?)?: return x
|
|
case let (x?, _)?: return x
|
|
case (.none, .none)?: return 0
|
|
}
|
|
}
|
|
|
|
func bar(a: Bool, b: Bool) -> Int {
|
|
switch (a, b) {
|
|
case (false, false):
|
|
return 1
|
|
case (true, _):
|
|
return 2
|
|
case (false, true):
|
|
return 3
|
|
}
|
|
}
|
|
|
|
|
|
enum Result<T> {
|
|
case Ok(T)
|
|
case Error(Error)
|
|
|
|
func shouldWork<U>(other: Result<U>) -> Int {
|
|
switch (self, other) { // No warning
|
|
case (.Ok, .Ok): return 1
|
|
case (.Error, .Error): return 2
|
|
case (.Error, _): return 3
|
|
case (_, .Error): return 4
|
|
}
|
|
}
|
|
}
|
|
|
|
func overParenthesized() {
|
|
// SR-7492: Space projection needs to treat extra paren-patterns explicitly.
|
|
let x: Result<(Result<Int>, String)> = .Ok((.Ok(1), "World"))
|
|
switch x {
|
|
case let .Error(e):
|
|
print(e)
|
|
case let .Ok((.Error(e), b)):
|
|
print(e, b)
|
|
case let .Ok((.Ok(a), b)): // No warning here.
|
|
print(a, b)
|
|
}
|
|
}
|
|
|
|
enum Foo {
|
|
case A(Int)
|
|
case B(Int)
|
|
}
|
|
func foo() {
|
|
switch (Foo.A(1), Foo.B(1)) {
|
|
case (.A(_), .A(_)):
|
|
()
|
|
case (.B(_), _):
|
|
()
|
|
case (_, .B(_)):
|
|
()
|
|
}
|
|
|
|
switch (Foo.A(1), Optional<(Int, Int)>.some((0, 0))) {
|
|
case (.A(_), _):
|
|
break
|
|
case (.B(_), (let q, _)?):
|
|
print(q)
|
|
case (.B(_), nil):
|
|
break
|
|
}
|
|
}
|
|
|
|
class C {}
|
|
|
|
enum Bar {
|
|
case TheCase(C?)
|
|
}
|
|
|
|
func test(f: Bar) -> Bool {
|
|
switch f {
|
|
case .TheCase(_?):
|
|
return true
|
|
case .TheCase(nil):
|
|
return false
|
|
}
|
|
}
|
|
|
|
func op(this : Optional<Bool>, other : Optional<Bool>) -> Optional<Bool> {
|
|
switch (this, other) { // No warning
|
|
case let (.none, w):
|
|
return w
|
|
case let (w, .none):
|
|
return w
|
|
case let (.some(e1), .some(e2)):
|
|
return .some(e1 && e2)
|
|
}
|
|
}
|
|
|
|
enum Threepeat {
|
|
case a, b, c
|
|
}
|
|
|
|
func test3(x: Threepeat, y: Threepeat) {
|
|
switch (x, y) { // expected-error {{switch must be exhaustive}}
|
|
// expected-note@-1 {{add missing case: '(.a, .c)'}}
|
|
case (.a, .a):
|
|
()
|
|
case (.b, _):
|
|
()
|
|
case (.c, _):
|
|
()
|
|
case (_, .b):
|
|
()
|
|
}
|
|
}
|
|
|
|
enum A {
|
|
case A(Int)
|
|
case B(Bool)
|
|
case C
|
|
case D
|
|
}
|
|
|
|
enum B {
|
|
case A
|
|
case B
|
|
}
|
|
|
|
func s(a: A, b: B) {
|
|
switch (a, b) {
|
|
case (.A(_), .A):
|
|
break
|
|
case (.A(_), .B):
|
|
break
|
|
|
|
case (.B(_), let b):
|
|
// expected-warning@-1 {{immutable value 'b' was never used; consider replacing with '_' or removing it}}
|
|
break
|
|
|
|
case (.C, _), (.D, _):
|
|
break
|
|
}
|
|
}
|
|
|
|
|
|
enum Grimble {
|
|
case A
|
|
case B
|
|
case C
|
|
}
|
|
|
|
enum Gromble {
|
|
case D
|
|
case E
|
|
}
|
|
|
|
func doSomething(foo:Grimble, bar:Gromble) {
|
|
switch(foo, bar) { // No warning
|
|
case (.A, .D):
|
|
break
|
|
case (.A, .E):
|
|
break
|
|
case (.B, _):
|
|
break
|
|
case (.C, _):
|
|
break
|
|
}
|
|
}
|
|
|
|
enum E {
|
|
case A
|
|
case B
|
|
}
|
|
|
|
func f(l: E, r: E) {
|
|
switch (l, r) {
|
|
case (.A, .A):
|
|
return
|
|
case (.A, _):
|
|
return
|
|
case (_, .A):
|
|
return
|
|
case (.B, .B):
|
|
return
|
|
}
|
|
}
|
|
|
|
enum TestEnum {
|
|
case A, B
|
|
}
|
|
|
|
func switchOverEnum(testEnumTuple: (TestEnum, TestEnum)) {
|
|
|
|
switch testEnumTuple {
|
|
case (_,.B):
|
|
// Matches (.A, .B) and (.B, .B)
|
|
break
|
|
case (.A,_):
|
|
// Matches (.A, .A)
|
|
// Would also match (.A, .B) but first case takes precedent
|
|
break
|
|
case (.B,.A):
|
|
// Matches (.B, .A)
|
|
break
|
|
}
|
|
|
|
}
|
|
|
|
func tests(a: Int?, b: String?) {
|
|
switch (a, b) {
|
|
case let (.some(n), _): print("a: ", n, "?")
|
|
case (.none, _): print("Nothing", "?")
|
|
}
|
|
|
|
switch (a, b) {
|
|
case let (.some(n), .some(s)): print("a: ", n, "b: ", s)
|
|
case let (.some(n), .none): print("a: ", n, "Nothing")
|
|
case (.none, _): print("Nothing")
|
|
}
|
|
|
|
switch (a, b) {
|
|
case let (.some(n), .some(s)): print("a: ", n, "b: ", s)
|
|
case let (.some(n), .none): print("a: ", n, "Nothing")
|
|
case let (.none, .some(s)): print("Nothing", "b: ", s)
|
|
case (.none, _): print("Nothing", "?")
|
|
}
|
|
|
|
switch (a, b) {
|
|
case let (.some(n), .some(s)): print("a: ", n, "b: ", s)
|
|
case let (.some(n), .none): print("a: ", n, "Nothing")
|
|
case let (.none, .some(s)): print("Nothing", "b: ", s)
|
|
case (.none, .none): print("Nothing", "Nothing")
|
|
}
|
|
|
|
}
|
|
|
|
enum X {
|
|
case Empty
|
|
case A(Int)
|
|
case B(Int)
|
|
}
|
|
|
|
func f(a: X, b: X) {
|
|
switch (a, b) {
|
|
case (_, .Empty): ()
|
|
case (.Empty, _): ()
|
|
|
|
case (.A, .A): ()
|
|
case (.B, .B): ()
|
|
|
|
case (.A, .B): ()
|
|
case (.B, .A): ()
|
|
|
|
}
|
|
}
|
|
|
|
func f2(a: X, b: X) {
|
|
switch (a, b) {
|
|
|
|
case (.A, .A): ()
|
|
case (.B, .B): ()
|
|
|
|
case (.A, .B): ()
|
|
case (.B, .A): ()
|
|
|
|
case (_, .Empty): ()
|
|
case (.Empty, _): ()
|
|
|
|
case (.A, .A): () // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
case (.B, .B): () // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
|
|
case (.A, .B): () // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
case (.B, .A): () // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
|
|
default: ()
|
|
}
|
|
}
|
|
|
|
enum XX : Int {
|
|
case A
|
|
case B
|
|
case C
|
|
case D
|
|
case E
|
|
}
|
|
|
|
func switcheroo(a: XX, b: XX) -> Int {
|
|
switch(a, b) { // No warning
|
|
case (.A, _) : return 1
|
|
case (_, .A) : return 2
|
|
|
|
case (.C, _) : return 3
|
|
case (_, .C) : return 4
|
|
|
|
case (.B, .B) : return 5
|
|
case (.B, .D) : return 6
|
|
case (.D, .B) : return 7
|
|
|
|
case (.B, .E) : return 8
|
|
case (.E, .B) : return 9
|
|
|
|
case (.E, _) : return 10
|
|
case (_, .E) : return 11
|
|
case (.D, .D) : return 12
|
|
|
|
default:
|
|
print("never hits this:", a, b)
|
|
return 13
|
|
}
|
|
}
|
|
|
|
enum PatternCasts {
|
|
case one(Any)
|
|
case two
|
|
case three(String)
|
|
}
|
|
|
|
func checkPatternCasts() {
|
|
// Pattern casts with this structure shouldn't warn about duplicate cases.
|
|
let x: PatternCasts = .one("One")
|
|
switch x {
|
|
case .one(let s as String): print(s)
|
|
case .one: break
|
|
case .two: break
|
|
case .three: break
|
|
}
|
|
|
|
// But should warn here.
|
|
switch x {
|
|
case .one(_): print(s)
|
|
case .one: break // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
case .two: break
|
|
case .three: break
|
|
}
|
|
|
|
// And not here
|
|
switch x {
|
|
case .one: break
|
|
case .two: break
|
|
case .three(let s as String?): print(s as Any)
|
|
}
|
|
}
|
|
|
|
enum MyNever {}
|
|
func ~= (_ : MyNever, _ : MyNever) -> Bool { return true }
|
|
func myFatalError() -> MyNever { fatalError() }
|
|
|
|
@_frozen public enum UninhabitedT4<A> {
|
|
case x(A)
|
|
}
|
|
|
|
func checkUninhabited() {
|
|
// Scrutinees of uninhabited type may match any number and kind of patterns
|
|
// that Sema is willing to accept at will. After all, it's quite a feat to
|
|
// productively inhabit the type of crashing programs.
|
|
func test1(x : Never) {
|
|
switch x {} // No diagnostic.
|
|
}
|
|
|
|
func test2(x : Never) {
|
|
switch (x, x) {} // No diagnostic.
|
|
}
|
|
|
|
func test3(x : MyNever) {
|
|
switch x { // No diagnostic.
|
|
case myFatalError(): break
|
|
case myFatalError(): break
|
|
case myFatalError(): break
|
|
}
|
|
}
|
|
|
|
func test4(x: UninhabitedT4<Never>) {
|
|
switch x {} // No diagnostic.
|
|
}
|
|
}
|
|
|
|
enum Runcible {
|
|
case spoon
|
|
case hat
|
|
case fork
|
|
}
|
|
|
|
func checkDiagnosticMinimality(x: Runcible?) {
|
|
switch (x!, x!) { // expected-error {{switch must be exhaustive}}
|
|
// expected-note@-1 {{add missing case: '(.fork, _)'}}
|
|
// expected-note@-2 {{add missing case: '(.hat, .hat)'}}
|
|
// expected-note@-3 {{add missing case: '(_, .fork)'}}
|
|
case (.spoon, .spoon):
|
|
break
|
|
case (.spoon, .hat):
|
|
break
|
|
case (.hat, .spoon):
|
|
break
|
|
}
|
|
|
|
switch (x!, x!) { // expected-error {{switch must be exhaustive}}
|
|
// expected-note@-1 {{add missing case: '(.fork, _)'}}
|
|
// expected-note@-2 {{add missing case: '(.hat, .spoon)'}}
|
|
// expected-note@-3 {{add missing case: '(.spoon, .hat)'}}
|
|
// expected-note@-4 {{add missing case: '(_, .fork)'}}
|
|
case (.spoon, .spoon):
|
|
break
|
|
case (.hat, .hat):
|
|
break
|
|
}
|
|
}
|
|
|
|
indirect enum InfinitelySized {
|
|
case one
|
|
case two
|
|
case recur(InfinitelySized)
|
|
case mutualRecur(MutuallyRecursive, InfinitelySized)
|
|
}
|
|
|
|
indirect enum MutuallyRecursive {
|
|
case one
|
|
case two
|
|
case recur(MutuallyRecursive)
|
|
case mutualRecur(InfinitelySized, MutuallyRecursive)
|
|
}
|
|
|
|
func infinitelySized() -> Bool {
|
|
switch (InfinitelySized.one, InfinitelySized.one) { // expected-error {{switch must be exhaustive}}
|
|
// expected-note@-1 8 {{add missing case:}}
|
|
case (.one, .one): return true
|
|
case (.two, .two): return true
|
|
}
|
|
|
|
switch (MutuallyRecursive.one, MutuallyRecursive.one) { // expected-error {{switch must be exhaustive}}
|
|
// expected-note@-1 8 {{add missing case:}}
|
|
case (.one, .one): return true
|
|
case (.two, .two): return true
|
|
}
|
|
}
|
|
|
|
func sr6316() {
|
|
let bool1 = false
|
|
let bool2 = false
|
|
let bool3 = false
|
|
let bool4 = true
|
|
let bool5 = false
|
|
let bool6 = true
|
|
let bool7 = true
|
|
let bool8 = false
|
|
let bool9 = false
|
|
|
|
switch (bool1, (bool2, bool4, bool6, bool8), (bool3, bool5, bool7, bool9)) {
|
|
// expected-error@-1 {{switch must be exhaustive}}
|
|
// expected-note@-2 {{add missing case: '(false, (_, false, true, _), (_, true, _, _))'}}
|
|
// expected-note@-3 {{add missing case: '(_, (_, true, _, _), (_, false, true, _))'}}
|
|
case (true, (_, _, _, _), (_, true, true, _)):
|
|
break
|
|
case (true, (_, _, _, _), (_, _, false, _)):
|
|
break
|
|
case (_, (_, true, true, _), (_, _, false, _)):
|
|
break
|
|
case (_, (_, _, false, _), (_, true, true, _)):
|
|
break
|
|
case (_, (_, true, true, _), (_, true, true, _)):
|
|
break
|
|
case (_, (_, _, false, _), (_, _, false, _)):
|
|
break
|
|
case (_, (_, false, _, _), (_, false, _, _)):
|
|
break
|
|
}
|
|
}
|
|
|
|
func sr6652() {
|
|
enum A {
|
|
indirect case a([A], foo: Bool)
|
|
indirect case b(Dictionary<String, Int>)
|
|
indirect case c(A, foo: [A])
|
|
indirect case d(if: A, then: A, else: A)
|
|
indirect case e(A, A, foo: Bool)
|
|
indirect case f(A)
|
|
case g(String, foo: Bool)
|
|
case string(String)
|
|
case `nil`
|
|
|
|
static func eke(_ lhs: A, _ rhs: A) -> Bool { return false }
|
|
}
|
|
|
|
enum B {
|
|
static func eke(_ lhs: B, _ rhs: B) -> Bool { return false }
|
|
}
|
|
|
|
enum C {
|
|
static func eke(_ lhs: C, _ rhs: C) -> Bool { return false }
|
|
}
|
|
|
|
enum D {
|
|
case a(A)
|
|
case b(B)
|
|
case c(C)
|
|
indirect case d([D])
|
|
|
|
// No diagnostic
|
|
static func eke(_ lhs: D, _ rhs: D) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case (.a(let r1), .a(let r2)): return A.eke(r1, r2)
|
|
case (.a, _): return false
|
|
case (.b(let r1), .b(let r2)): return B.eke(r1, r2)
|
|
case (.b, _): return false
|
|
case (.c(let r1), .c(let r2)): return C.eke(r1, r2)
|
|
case (.c, _): return false
|
|
case (.d(let r1), .d(let r2)): return zip(r1, r2).allSatisfy(D.eke)
|
|
case (.d, _): return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func diagnoseDuplicateLiterals() {
|
|
let str = "def"
|
|
let int = 2
|
|
let dbl = 2.5
|
|
|
|
// No Diagnostics
|
|
switch str {
|
|
case "abc": break
|
|
case "def": break
|
|
case "ghi": break
|
|
default: break
|
|
}
|
|
|
|
switch str {
|
|
case "abc": break
|
|
case "def": break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case "def": break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case "ghi": break
|
|
default: break
|
|
}
|
|
|
|
switch str {
|
|
case "abc", "def": break // expected-note 2 {{first occurrence of identical literal pattern is here}}
|
|
case "ghi", "jkl": break
|
|
case "abc", "def": break // expected-warning 2 {{literal value is already handled by previous pattern; consider removing it}}
|
|
default: break
|
|
}
|
|
|
|
switch str {
|
|
case "xyz": break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case "ghi": break
|
|
case "def": break
|
|
case "abc": break
|
|
case "xyz": break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
default: break
|
|
}
|
|
|
|
func someStr() -> String { return "sdlkj" }
|
|
let otherStr = "ifnvbnwe"
|
|
switch str {
|
|
case "sdlkj": break
|
|
case "ghi": break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case someStr(): break
|
|
case "def": break
|
|
case otherStr: break
|
|
case "xyz": break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case "ifnvbnwe": break
|
|
case "ghi": break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case "xyz": break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
default: break
|
|
}
|
|
|
|
// No Diagnostics
|
|
switch int {
|
|
case -2: break
|
|
case -1: break
|
|
case 0: break
|
|
case 1: break
|
|
case 2: break
|
|
case 3: break
|
|
default: break
|
|
}
|
|
|
|
switch int {
|
|
case -2: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case -2: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 1: break
|
|
case 2: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 2: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 3: break
|
|
default: break
|
|
}
|
|
|
|
switch int {
|
|
case -2, -2: break // expected-note {{first occurrence of identical literal pattern is here}} expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 1, 2: break // expected-note 3 {{first occurrence of identical literal pattern is here}}
|
|
case 2, 3: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 1, 2: break // expected-warning 2 {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 4, 5: break
|
|
case 7, 7: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
// expected-warning@-1 {{literal value is already handled by previous pattern; consider removing it}}
|
|
default: break
|
|
}
|
|
|
|
switch int {
|
|
case 1: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 2: break // expected-note 2 {{first occurrence of identical literal pattern is here}}
|
|
case 3: break
|
|
case 17: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 4: break
|
|
case 2: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 001: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 5: break
|
|
case 0x11: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 0b10: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
default: break
|
|
}
|
|
|
|
switch int {
|
|
case 10: break
|
|
case 0b10: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case -0b10: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 3000: break
|
|
case 0x12: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 400: break
|
|
case 2: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case -2: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 18: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
default: break
|
|
}
|
|
|
|
func someInt() -> Int { return 0x1234 }
|
|
let otherInt = 13254
|
|
switch int {
|
|
case 13254: break
|
|
case 3000: break
|
|
case 00000002: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 0x1234: break
|
|
case someInt(): break
|
|
case 400: break
|
|
case 2: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 18: break
|
|
case otherInt: break
|
|
case 230: break
|
|
default: break
|
|
}
|
|
|
|
// No Diagnostics
|
|
switch dbl {
|
|
case -3.5: break
|
|
case -2.5: break
|
|
case -1.5: break
|
|
case 1.5: break
|
|
case 2.5: break
|
|
case 3.5: break
|
|
default: break
|
|
}
|
|
|
|
switch dbl {
|
|
case -3.5: break
|
|
case -2.5: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case -2.5: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case -1.5: break
|
|
case 1.5: break
|
|
case 2.5: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 2.5: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 3.5: break
|
|
default: break
|
|
}
|
|
|
|
switch dbl {
|
|
case 1.5, 4.5, 7.5, 6.9: break // expected-note 2 {{first occurrence of identical literal pattern is here}}
|
|
case 3.4, 1.5: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 7.5, 2.3: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
default: break
|
|
}
|
|
|
|
switch dbl {
|
|
case 1: break
|
|
case 1.5: break // expected-note 2 {{first occurrence of identical literal pattern is here}}
|
|
case 2.5: break
|
|
case 3.5: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 5.3132: break
|
|
case 1.500: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 46.2395: break
|
|
case 1.5000: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 0003.50000: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 23452.43: break
|
|
default: break
|
|
}
|
|
|
|
func someDouble() -> Double { return 324.4523 }
|
|
let otherDouble = 458.2345
|
|
switch dbl {
|
|
case 1: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 1.5: break
|
|
case 2.5: break
|
|
case 3.5: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 5.3132: break
|
|
case 46.2395: break
|
|
case someDouble(): break
|
|
case 0003.50000: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case otherDouble: break
|
|
case 2.50505: break // expected-note {{first occurrence of identical literal pattern is here}}
|
|
case 23452.43: break
|
|
case 00001: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
case 123453: break
|
|
case 2.50505000000: break // expected-warning {{literal value is already handled by previous pattern; consider removing it}}
|
|
default: break
|
|
}
|
|
}
|
|
|
|
func checkLiteralTuples() {
|
|
let str1 = "abc"
|
|
let str2 = "def"
|
|
let int1 = 23
|
|
let int2 = 7
|
|
let dbl1 = 4.23
|
|
let dbl2 = 23.45
|
|
|
|
// No Diagnostics
|
|
switch (str1, str2) {
|
|
case ("abc", "def"): break
|
|
case ("def", "ghi"): break
|
|
case ("ghi", "def"): break
|
|
case ("abc", "def"): break // We currently don't catch this
|
|
default: break
|
|
}
|
|
|
|
// No Diagnostics
|
|
switch (int1, int2) {
|
|
case (94, 23): break
|
|
case (7, 23): break
|
|
case (94, 23): break // We currently don't catch this
|
|
case (23, 7): break
|
|
default: break
|
|
}
|
|
|
|
// No Diagnostics
|
|
switch (dbl1, dbl2) {
|
|
case (543.21, 123.45): break
|
|
case (543.21, 123.45): break // We currently don't catch this
|
|
case (23.45, 4.23): break
|
|
case (4.23, 23.45): break
|
|
default: break
|
|
}
|
|
}
|
|
|
|
func sr6975() {
|
|
enum E {
|
|
case a, b
|
|
}
|
|
|
|
let e = E.b
|
|
switch e {
|
|
case .a as E: // expected-warning {{'as' test is always true}}
|
|
print("a")
|
|
case .b: // Valid!
|
|
print("b")
|
|
case .a: // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
print("second a")
|
|
}
|
|
|
|
func foo(_ str: String) -> Int {
|
|
switch str { // expected-error {{switch must be exhaustive}}
|
|
// expected-note@-1 {{do you want to add a default clause?}}
|
|
case let (x as Int) as Any:
|
|
return x
|
|
}
|
|
}
|
|
_ = foo("wtf")
|
|
}
|
|
|
|
public enum NonExhaustive {
|
|
case a, b
|
|
}
|
|
|
|
public enum NonExhaustivePayload {
|
|
case a(Int), b(Bool)
|
|
}
|
|
|
|
@_frozen public enum TemporalProxy {
|
|
case seconds(Int)
|
|
case milliseconds(Int)
|
|
case microseconds(Int)
|
|
case nanoseconds(Int)
|
|
case never
|
|
}
|
|
|
|
// Inlinable code is considered "outside" the module and must include a default
|
|
// case.
|
|
@inlinable
|
|
public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePayload, for interval: TemporalProxy, flag: Bool) {
|
|
switch value { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{none}} expected-note {{handle unknown values using "@unknown default"}} {{none}}
|
|
case .a: break
|
|
}
|
|
|
|
switch value { // expected-warning {{switch covers known cases, but 'NonExhaustive' may have additional unknown values}} {{none}} expected-note {{handle unknown values using "@unknown default"}} {{3-3=@unknown default:\n<#fatalError#>()\n}}
|
|
case .a: break
|
|
case .b: break
|
|
}
|
|
|
|
switch value {
|
|
case .a: break
|
|
case .b: break
|
|
default: break // no-warning
|
|
}
|
|
|
|
switch value {
|
|
case .a: break
|
|
case .b: break
|
|
@unknown case _: break // no-warning
|
|
}
|
|
|
|
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{none}}
|
|
case .a: break
|
|
@unknown case _: break
|
|
}
|
|
|
|
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.a'}} {{none}} expected-note {{add missing case: '.b'}} {{none}}
|
|
@unknown case _: break
|
|
}
|
|
|
|
switch value {
|
|
case _: break
|
|
@unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
}
|
|
|
|
// Test being part of other spaces.
|
|
switch value as Optional { // expected-warning {{switch covers known cases, but 'Optional<NonExhaustive>' may have additional unknown values}} {{none}} expected-note {{add missing case: '.some(_)'}}
|
|
case .a?: break
|
|
case .b?: break
|
|
case nil: break
|
|
}
|
|
|
|
switch value as Optional {
|
|
case .a?: break
|
|
case .b?: break
|
|
case nil: break
|
|
@unknown case _: break
|
|
} // no-warning
|
|
|
|
switch value as Optional {
|
|
case _?: break
|
|
case nil: break
|
|
} // no-warning
|
|
|
|
switch (value, flag) { // expected-warning {{switch covers known cases, but '(NonExhaustive, Bool)' may have additional unknown values}} {{none}} expected-note {{add missing case: '(_, false)'}}
|
|
case (.a, _): break
|
|
case (.b, false): break
|
|
case (_, true): break
|
|
}
|
|
|
|
switch (value, flag) {
|
|
case (.a, _): break
|
|
case (.b, false): break
|
|
case (_, true): break
|
|
@unknown case _: break
|
|
} // no-warning
|
|
|
|
switch (flag, value) { // expected-warning {{switch covers known cases, but '(Bool, NonExhaustive)' may have additional unknown values}} {{none}} expected-note {{add missing case: '(false, _)'}}
|
|
case (_, .a): break
|
|
case (false, .b): break
|
|
case (true, _): break
|
|
}
|
|
|
|
switch (flag, value) {
|
|
case (_, .a): break
|
|
case (false, .b): break
|
|
case (true, _): break
|
|
@unknown case _: break
|
|
} // no-warning
|
|
|
|
switch (value, value) { // expected-warning {{switch covers known cases, but '(NonExhaustive, NonExhaustive)' may have additional unknown values}} {{none}} expected-note {{add missing case: '(_, _)'}}
|
|
case (.a, _), (_, .a): break
|
|
case (.b, _), (_, .b): break
|
|
}
|
|
|
|
switch (value, value) {
|
|
case (.a, _), (_, .a): break
|
|
case (.b, _), (_, .b): break
|
|
@unknown case _: break
|
|
} // no-warning
|
|
|
|
// Test payloaded enums.
|
|
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}} expected-note {{handle unknown values using "@unknown default"}} {{none}}
|
|
case .a: break
|
|
}
|
|
|
|
switch payload { // expected-warning {{switch covers known cases, but 'NonExhaustivePayload' may have additional unknown values}} {{none}} expected-note {{handle unknown values using "@unknown default"}} {{3-3=@unknown default:\n<#fatalError#>()\n}}
|
|
case .a: break
|
|
case .b: break
|
|
}
|
|
|
|
switch payload {
|
|
case .a: break
|
|
case .b: break
|
|
default: break // no-warning
|
|
}
|
|
|
|
switch payload {
|
|
case .a: break
|
|
case .b: break
|
|
@unknown case _: break // no-warning
|
|
}
|
|
|
|
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}}
|
|
case .a: break
|
|
@unknown case _: break
|
|
}
|
|
|
|
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}} expected-note {{handle unknown values using "@unknown default"}} {{none}}
|
|
case .a: break
|
|
case .b(false): break
|
|
}
|
|
|
|
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}}
|
|
case .a: break
|
|
case .b(false): break
|
|
@unknown case _: break
|
|
}
|
|
|
|
// Test fully-covered switches.
|
|
switch interval {
|
|
case .seconds, .milliseconds, .microseconds, .nanoseconds: break
|
|
case .never: break
|
|
@unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
}
|
|
|
|
switch flag {
|
|
case true: break
|
|
case false: break
|
|
@unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
}
|
|
|
|
switch flag as Optional {
|
|
case _?: break
|
|
case nil: break
|
|
@unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
}
|
|
|
|
switch (flag, value) {
|
|
case (true, _): break
|
|
case (false, _): break
|
|
@unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
}
|
|
}
|
|
|
|
public func testNonExhaustiveWithinModule(_ value: NonExhaustive, _ payload: NonExhaustivePayload, flag: Bool) {
|
|
switch value { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}}
|
|
case .a: break
|
|
}
|
|
|
|
switch value { // no-warning
|
|
case .a: break
|
|
case .b: break
|
|
}
|
|
|
|
switch value {
|
|
case .a: break
|
|
case .b: break
|
|
default: break // no-warning
|
|
}
|
|
|
|
switch value {
|
|
case .a: break
|
|
case .b: break
|
|
@unknown case _: break // no-warning
|
|
}
|
|
|
|
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{none}}
|
|
case .a: break
|
|
@unknown case _: break
|
|
}
|
|
|
|
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.a'}} {{none}} expected-note {{add missing case: '.b'}} {{none}}
|
|
@unknown case _: break
|
|
}
|
|
|
|
switch value {
|
|
case _: break
|
|
@unknown case _: break // expected-warning {{case is already handled by previous patterns; consider removing it}}
|
|
}
|
|
|
|
// Test being part of other spaces.
|
|
switch value as Optional { // no-warning
|
|
case .a?: break
|
|
case .b?: break
|
|
case nil: break
|
|
}
|
|
|
|
switch value as Optional {
|
|
case _?: break
|
|
case nil: break
|
|
} // no-warning
|
|
|
|
switch (value, flag) { // no-warning
|
|
case (.a, _): break
|
|
case (.b, false): break
|
|
case (_, true): break
|
|
}
|
|
|
|
switch (flag, value) { // no-warning
|
|
case (_, .a): break
|
|
case (false, .b): break
|
|
case (true, _): break
|
|
}
|
|
|
|
switch (value, value) { // no-warning
|
|
case (.a, _): break
|
|
case (.b, _): break
|
|
case (_, .a): break
|
|
case (_, .b): break
|
|
}
|
|
|
|
switch (value, value) { // no-warning
|
|
case (.a, _): break
|
|
case (.b, _): break
|
|
case (_, .a): break
|
|
case (_, .b): break
|
|
@unknown case _: break
|
|
}
|
|
|
|
// Test payloaded enums.
|
|
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}}
|
|
case .a: break
|
|
}
|
|
|
|
switch payload { // no-warning
|
|
case .a: break
|
|
case .b: break
|
|
}
|
|
|
|
switch payload {
|
|
case .a: break
|
|
case .b: break
|
|
default: break // no-warning
|
|
}
|
|
|
|
switch payload {
|
|
case .a: break
|
|
case .b: break
|
|
@unknown case _: break // no-warning
|
|
}
|
|
|
|
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}}
|
|
case .a: break
|
|
@unknown case _: break
|
|
}
|
|
|
|
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}}
|
|
case .a: break
|
|
case .b(false): break
|
|
}
|
|
|
|
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}}
|
|
case .a: break
|
|
case .b(false): break
|
|
@unknown case _: break
|
|
}
|
|
}
|
|
|
|
enum UnavailableCase {
|
|
case a
|
|
case b
|
|
@available(*, unavailable)
|
|
case oopsThisWasABadIdea
|
|
}
|
|
|
|
enum UnavailableCaseOSSpecific {
|
|
case a
|
|
case b
|
|
|
|
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
|
@available(macOS, unavailable)
|
|
@available(iOS, unavailable)
|
|
@available(tvOS, unavailable)
|
|
@available(watchOS, unavailable)
|
|
case unavailableOnAllTheseApplePlatforms
|
|
#else
|
|
@available(*, unavailable)
|
|
case dummyCaseForOtherPlatforms
|
|
#endif
|
|
}
|
|
|
|
enum UnavailableCaseOSIntroduced {
|
|
case a
|
|
case b
|
|
|
|
@available(macOS 50, iOS 50, tvOS 50, watchOS 50, *)
|
|
case notYetIntroduced
|
|
}
|
|
|
|
func testUnavailableCases(_ x: UnavailableCase, _ y: UnavailableCaseOSSpecific, _ z: UnavailableCaseOSIntroduced) {
|
|
switch x {
|
|
case .a: break
|
|
case .b: break
|
|
} // no-error
|
|
|
|
switch y {
|
|
case .a: break
|
|
case .b: break
|
|
} // no-error
|
|
|
|
switch z {
|
|
case .a: break
|
|
case .b: break
|
|
case .notYetIntroduced: break
|
|
} // no-error
|
|
}
|
|
|
|
// The following test used to behave differently when the uninhabited enum was
|
|
// defined in the same module as the function (as opposed to using Swift.Never).
|
|
enum NoError {}
|
|
extension Result where T == NoError {
|
|
func testUninhabited() {
|
|
switch self {
|
|
case .Error(_):
|
|
break
|
|
// No .Ok case possible because of the 'NoError'.
|
|
}
|
|
|
|
switch self {
|
|
case .Error(_):
|
|
break
|
|
case .Ok(_):
|
|
break // But it's okay to write one.
|
|
}
|
|
}
|
|
}
|
|
|
|
enum SR10301<T,E> {
|
|
case value(T)
|
|
case error(E)
|
|
}
|
|
enum SR10301Error: Error {
|
|
case bad
|
|
}
|
|
|
|
func sr10301(_ foo: SR10301<String,(Int,Error)>) {
|
|
switch foo {
|
|
case .value: return
|
|
case .error((_, SR10301Error.bad)): return
|
|
case .error((_, let err)):
|
|
_ = err
|
|
return
|
|
}
|
|
}
|
|
|
|
func sr10301_is(_ foo: SR10301<String,(Int,Error)>) {
|
|
switch foo {
|
|
case .value: return
|
|
case .error((_, is SR10301Error)): return
|
|
case .error((_, let err)):
|
|
_ = err
|
|
return
|
|
}
|
|
}
|
|
|
|
func sr10301_as(_ foo: SR10301<String,(Int,Error)>) {
|
|
switch foo {
|
|
case .value: return
|
|
case .error((_, let err as SR10301Error)):
|
|
_ = err
|
|
return
|
|
case .error((_, let err)):
|
|
_ = err
|
|
return
|
|
}
|
|
}
|