Skip to content

Commit 4f76d25

Browse files
authored
Merge pull request #21708 from jrose-apple/5.0-into-the-unknown
[5.0] Special-case diagnostic for when you just need `@unknown default`
2 parents 13ac05e + 6925f86 commit 4f76d25

11 files changed

+87
-24
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4276,6 +4276,9 @@ NOTE(redundant_particular_literal_case_here,none,
42764276
"first occurrence of identical literal pattern is here", ())
42774277

42784278
WARNING(non_exhaustive_switch_warn,none, "switch must be exhaustive", ())
4279+
WARNING(non_exhaustive_switch_unknown_only,none,
4280+
"switch covers known cases, but %0 may have additional unknown values"
4281+
"%select{|, possibly added in future versions}1", (Type, bool))
42794282

42804283
WARNING(override_nsobject_hashvalue,none,
42814284
"override of 'NSObject.hashValue' is deprecated; "

lib/Sema/TypeCheckSwitchStmt.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,8 @@ namespace {
11501150
bool InEditor = TC.Context.LangOpts.DiagnosticsEditorMode;
11511151

11521152
// Decide whether we want an error or a warning.
1153-
auto mainDiagType = diag::non_exhaustive_switch;
1153+
Optional<decltype(diag::non_exhaustive_switch)> mainDiagType =
1154+
diag::non_exhaustive_switch;
11541155
if (unknownCase) {
11551156
switch (defaultReason) {
11561157
case RequiresDefault::EmptySwitchBody:
@@ -1177,16 +1178,25 @@ namespace {
11771178
switch (uncovered.checkDowngradeToWarning()) {
11781179
case DowngradeToWarning::No:
11791180
break;
1180-
case DowngradeToWarning::ForUnknownCase:
1181+
case DowngradeToWarning::ForUnknownCase: {
11811182
if (TC.Context.LangOpts.DebuggerSupport ||
11821183
TC.Context.LangOpts.Playground ||
11831184
!TC.getLangOpts().EnableNonFrozenEnumExhaustivityDiagnostics) {
11841185
// Don't require covering unknown cases in the debugger or in
11851186
// playgrounds.
11861187
return;
11871188
}
1188-
// Missing '@unknown' is just a warning.
1189-
mainDiagType = diag::non_exhaustive_switch_warn;
1189+
assert(defaultReason == RequiresDefault::No);
1190+
Type subjectType = Switch->getSubjectExpr()->getType();
1191+
bool shouldIncludeFutureVersionComment = false;
1192+
if (auto *theEnum = subjectType->getEnumOrBoundGenericEnum()) {
1193+
shouldIncludeFutureVersionComment =
1194+
theEnum->getParentModule()->isSystemModule();
1195+
}
1196+
TC.diagnose(startLoc, diag::non_exhaustive_switch_unknown_only,
1197+
subjectType, shouldIncludeFutureVersionComment);
1198+
mainDiagType = None;
1199+
}
11901200
break;
11911201
}
11921202

@@ -1201,7 +1211,7 @@ namespace {
12011211
return;
12021212
case RequiresDefault::UncoveredSwitch: {
12031213
OS << tok::kw_default << ":\n" << placeholder << "\n";
1204-
TC.diagnose(startLoc, mainDiagType);
1214+
TC.diagnose(startLoc, mainDiagType.getValue());
12051215
TC.diagnose(startLoc, diag::missing_several_cases, /*default*/true)
12061216
.fixItInsert(insertLoc, buffer.str());
12071217
}
@@ -1218,7 +1228,9 @@ namespace {
12181228
// If there's nothing else to diagnose, bail.
12191229
if (uncovered.isEmpty()) return;
12201230

1221-
TC.diagnose(startLoc, mainDiagType);
1231+
// Check if we still have to emit the main diganostic.
1232+
if (mainDiagType.hasValue())
1233+
TC.diagnose(startLoc, mainDiagType.getValue());
12221234

12231235
// Add notes to explain what's missing.
12241236
auto processUncoveredSpaces =

test/ClangImporter/enum-error.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,9 @@ func testError() {
8787
// CHECK: sil_witness_table shared [serialized] ExhaustiveError: _BridgedStoredNSError module __ObjC
8888
let terr = getErr()
8989
switch (terr) { case .TENone, .TEOne, .TETwo: break }
90-
// EXHAUSTIVE: [[@LINE-1]]:{{.+}}: warning: switch must be exhaustive
90+
// EXHAUSTIVE: [[@LINE-1]]:{{.+}}: warning: switch covers known cases, but 'TestError.Code' may have additional unknown values
9191
// EXHAUSTIVE: [[@LINE-2]]:{{.+}}: note: handle unknown values using "@unknown default"
9292

93-
// FIXME: This should still be an error because there are /known/ cases that
94-
// aren't covered.
9593
switch (terr) { case .TENone, .TEOne: break }
9694
// EXHAUSTIVE: [[@LINE-1]]:{{.+}}: error: switch must be exhaustive
9795
// EXHAUSTIVE: [[@LINE-2]]:{{.+}}: note: add missing case: '.TETwo'
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %target-swift-frontend -typecheck %s -Xcc -isystem -Xcc %S/Inputs/custom-modules -verify -enable-nonfrozen-enum-exhaustivity-diagnostics
2+
3+
import EnumExhaustivity
4+
5+
func test(_ value: RegularEnum, _ exhaustiveValue: ExhaustiveEnum) {
6+
switch value { // expected-warning {{switch covers known cases, but 'RegularEnum' may have additional unknown values, possibly added in future versions}} expected-note {{handle unknown values using "@unknown default"}}
7+
case .A: break
8+
case .B: break
9+
}
10+
11+
switch exhaustiveValue { // always okay
12+
case .A: break
13+
case .B: break
14+
}
15+
}
16+
17+
func testAttributes(
18+
_ rete: RegularEnumTurnedExhaustive,
19+
_ arete: AnotherRegularEnumTurnedExhaustive,
20+
_ retetb: RegularEnumTurnedExhaustiveThenBackViaAPINotes,
21+
_ fdte: ForwardDeclaredTurnedExhaustive,
22+
_ fdo: ForwardDeclaredOnly
23+
) {
24+
switch rete {
25+
case .A, .B: break
26+
}
27+
28+
switch arete {
29+
case .A, .B: break
30+
}
31+
32+
switch retetb { // expected-warning {{switch covers known cases, but 'RegularEnumTurnedExhaustiveThenBackViaAPINotes' may have additional unknown values, possibly added in future versions}} expected-note {{handle unknown values using "@unknown default"}}
33+
case .A, .B: break
34+
}
35+
36+
switch fdte {
37+
case .A, .B: break
38+
}
39+
40+
switch fdo {
41+
case .A, .B: break
42+
}
43+
}
44+
45+
func testUnavailableCases(_ value: UnavailableCases) {
46+
switch value { // okay
47+
case .A: break
48+
case .B: break
49+
}
50+
}

test/ClangImporter/enum-exhaustivity.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import EnumExhaustivity
1818

1919
func test(_ value: RegularEnum, _ exhaustiveValue: ExhaustiveEnum) {
20-
switch value { // expected-error {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}}
20+
switch value { // expected-error {{switch covers known cases, but 'RegularEnum' may have additional unknown values}} expected-note {{handle unknown values using "@unknown default"}}
2121
case .A: break
2222
case .B: break
2323
}
@@ -43,7 +43,7 @@ func testAttributes(
4343
case .A, .B: break
4444
}
4545

46-
switch retetb { // expected-error {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}}
46+
switch retetb { // expected-error {{switch covers known cases, but 'RegularEnumTurnedExhaustiveThenBackViaAPINotes' may have additional unknown values}} expected-note {{handle unknown values using "@unknown default"}}
4747
case .A, .B: break
4848
}
4949

test/ClangImporter/enum-inferred-exhaustivity.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
func test(_ value: EnumWithDefaultExhaustivity) {
88
// We want to assume such enums are non-frozen.
9-
switch value { // expected-error {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}}
9+
switch value { // expected-error {{switch covers known cases, but 'EnumWithDefaultExhaustivity' may have additional unknown values}} expected-note {{handle unknown values using "@unknown default"}}
1010
case .loneCase: break
1111
}
1212
}
1313

1414
func test(_ value: EnumWithSpecialAttributes) {
1515
// Same, but with the attributes macro shipped in the Xcode 9 SDKs.
16-
switch value { // expected-error {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}}
16+
switch value { // expected-error {{switch covers known cases, but 'EnumWithSpecialAttributes' may have additional unknown values}} expected-note {{handle unknown values using "@unknown default"}}
1717
case .loneCase: break
1818
}
1919
}

test/ClangImporter/enum-new.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ _ = .Red as Color
55
_ = .Cyan as MoreColor
66

77
func test() {
8-
switch getColor() { // expected-warning {{switch must be exhaustive}} expected-note{{handle unknown values using "@unknown default"}}
8+
switch getColor() { // expected-warning {{switch covers known cases, but 'Color' may have additional unknown values}} expected-note{{handle unknown values using "@unknown default"}}
99
case .Red, .Blue, .Green: break
1010
}
1111

12-
switch getMoreColor() { // expected-warning {{switch must be exhaustive}} expected-note{{handle unknown values using "@unknown default"}}
12+
switch getMoreColor() { // expected-warning {{switch covers known cases, but 'MoreColor' may have additional unknown values}} expected-note{{handle unknown values using "@unknown default"}}
1313
case .Yellow, .Magenta, .Black, .Cyan: break
1414
}
1515

test/ClangImporter/enum-objc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// REQUIRES: objc_interop
44

55
func test(_ value: SwiftEnum, _ exhaustiveValue: ExhaustiveEnum) {
6-
switch value { // expected-warning {{switch must be exhaustive}} expected-note {{handle unknown values using "@unknown default"}}
6+
switch value { // expected-warning {{switch covers known cases, but 'SwiftEnum' may have additional unknown values}} expected-note {{handle unknown values using "@unknown default"}}
77
case .one: break
88
case .two: break
99
case .three: break

test/Sema/exhaustive_switch.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePa
883883
case .a: break
884884
}
885885

886-
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{handle unknown values using "@unknown default"}} {{3-3=@unknown default:\n<#fatalError#>()\n}}
886+
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}}
887887
case .a: break
888888
case .b: break
889889
}
@@ -915,7 +915,7 @@ public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePa
915915
}
916916

917917
// Test being part of other spaces.
918-
switch value as Optional { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.some(_)'}}
918+
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(_)'}}
919919
case .a?: break
920920
case .b?: break
921921
case nil: break
@@ -933,7 +933,7 @@ public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePa
933933
case nil: break
934934
} // no-warning
935935

936-
switch (value, flag) { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '(_, false)'}}
936+
switch (value, flag) { // expected-warning {{switch covers known cases, but '(NonExhaustive, Bool)' may have additional unknown values}} {{none}} expected-note {{add missing case: '(_, false)'}}
937937
case (.a, _): break
938938
case (.b, false): break
939939
case (_, true): break
@@ -946,7 +946,7 @@ public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePa
946946
@unknown case _: break
947947
} // no-warning
948948

949-
switch (flag, value) { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '(false, _)'}}
949+
switch (flag, value) { // expected-warning {{switch covers known cases, but '(Bool, NonExhaustive)' may have additional unknown values}} {{none}} expected-note {{add missing case: '(false, _)'}}
950950
case (_, .a): break
951951
case (false, .b): break
952952
case (true, _): break
@@ -959,7 +959,7 @@ public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePa
959959
@unknown case _: break
960960
} // no-warning
961961

962-
switch (value, value) { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '(_, _)'}}
962+
switch (value, value) { // expected-warning {{switch covers known cases, but '(NonExhaustive, NonExhaustive)' may have additional unknown values}} {{none}} expected-note {{add missing case: '(_, _)'}}
963963
case (.a, _), (_, .a): break
964964
case (.b, _), (_, .b): break
965965
}
@@ -975,7 +975,7 @@ public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePa
975975
case .a: break
976976
}
977977

978-
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{handle unknown values using "@unknown default"}} {{3-3=@unknown default:\n<#fatalError#>()\n}}
978+
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}}
979979
case .a: break
980980
case .b: break
981981
}

test/Sema/exhaustive_switch_testable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func testFrozen(_ e: FrozenEnum) -> Int {
1717
}
1818

1919
func testNonFrozen(_ e: NonFrozenEnum) -> Int {
20-
// VERIFY-NON-FROZEN: exhaustive_switch_testable.swift:[[@LINE+1]]:{{[0-9]+}}: warning: switch must be exhaustive
20+
// VERIFY-NON-FROZEN: exhaustive_switch_testable.swift:[[@LINE+1]]:{{[0-9]+}}: warning: switch covers known cases, but 'NonFrozenEnum' may have additional unknown values
2121
switch e {
2222
case .a: return 1
2323
case .b, .c: return 2

0 commit comments

Comments
 (0)