Files
swift-mirror/test/Sema/exhaustive_switch_debugger.swift
Allan Shortlidge 3acc0d6655 SILGen/Sema: Avoid diagnosing @unknown default switch cases as unreachable.
Suppose you have an exhaustive switch statement which matches all the cases of
a Swift enum defined in a different module named `External`:

```
import External

var e: External.SomeEnum = //...

switch e {
case .a: break
}
```

If `External` is compiled with library evolution and `SomeEnum` is not frozen,
then the compiler will warn:

```
warning: switch covers known cases, but 'SomeEnum' may have additional unknown values
```

You add an `@unknown default` to the switch to resolve this warning. Now
suppose in another build configuration, `External` is built _without_ library
evolution. The compiler will complain about the unreachability of the default
case:

```
warning: Default will never be executed
```

These contradictory compiler diagnostics encourage the developer to change the
code in a way that will cause a diagnostic in the other configuration.
Developers should have the tools to address all warning diagnostics in a
reasonable fashion and this is a case where the compiler makes that especially
difficult. Given that writing `@unknown default` instead of `default` is a very
intentional action that would be the result of addressing the library evolution
configuration, it seems reasonable to suppress the `Default will never be
executed` diagnostic.
2024-03-09 12:26:22 -08:00

212 lines
5.4 KiB
Swift

// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-library-evolution -enable-nonfrozen-enum-exhaustivity-diagnostics -debugger-support
// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-library-evolution -enable-nonfrozen-enum-exhaustivity-diagnostics -playground
public enum NonExhaustive {
case a, b
}
public enum NonExhaustivePayload {
case a(Int), b(Bool)
}
// Inlineable code is considered "outside" the module and must include a default
// case.
@inlinable
public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePayload, 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 { // 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
}
// 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
}
// 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 { // 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}} 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
}
}
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
}
// 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
}
// 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
}
}