Skip to content

Commit 328e8af

Browse files
authored
Merge pull request #75792 from ahoppen/no-editor-mode-switch
[Sema/IDE] Emit same diagnostics for missing switch cases independent of editor mode
2 parents 01f3be2 + 7d7eb6e commit 328e8af

25 files changed

+305
-1181
lines changed

lib/Sema/TypeCheckSwitchStmt.cpp

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,8 +1124,6 @@ namespace {
11241124
llvm::SmallString<128> buffer;
11251125
llvm::raw_svector_ostream OS(buffer);
11261126

1127-
bool InEditor = Context.LangOpts.DiagnosticsEditorMode;
1128-
11291127
// Decide whether we want an error or a warning.
11301128
std::optional<decltype(diag::non_exhaustive_switch)> mainDiagType =
11311129
diag::non_exhaustive_switch;
@@ -1274,61 +1272,49 @@ namespace {
12741272
}
12751273
};
12761274

1277-
// If editing is enabled, emit a formatted error of the form:
1275+
// Emit a formatted note of the form, which has a Fix-It associated with
1276+
// it, primarily to be used in IDEs:
12781277
//
12791278
// switch must be exhaustive, do you want to add missing cases?
12801279
// case (.none, .some(_)):
12811280
// <#code#>
12821281
// case (.some(_), .none):
12831282
// <#code#>
12841283
//
1285-
// else:
1286-
//
1287-
// switch must be exhaustive, consider adding missing cases:
1284+
// To also provide actionable output for command line errors, emit notes
1285+
// like the following:
12881286
//
12891287
// missing case '(.none, .some(_))'
12901288
// missing case '(.some(_), .none)'
1291-
if (InEditor) {
1292-
buffer.clear();
1293-
1294-
bool alreadyEmittedSomething = false;
1295-
processUncoveredSpaces([&](const Space &space,
1296-
bool onlyOneUncoveredSpace) {
1297-
if (space.getKind() == SpaceKind::UnknownCase) {
1298-
OS << "@unknown " << tok::kw_default;
1299-
if (onlyOneUncoveredSpace) {
1300-
OS << ":\n<#fatalError()#>\n";
1301-
DE.diagnose(startLoc, diag::missing_unknown_case)
1302-
.fixItInsert(insertLoc, buffer.str());
1303-
alreadyEmittedSomething = true;
1304-
return;
1305-
}
1306-
} else {
1307-
OS << tok::kw_case << " ";
1308-
space.show(OS);
1309-
}
1310-
OS << ":\n" << placeholder << "\n";
1311-
});
1312-
1313-
if (!alreadyEmittedSomething) {
1314-
DE.diagnose(startLoc, diag::missing_several_cases, false)
1315-
.fixItInsert(insertLoc, buffer.str());
1289+
SmallString<128> missingSeveralCasesFixIt;
1290+
int diagnosedCases = 0;
1291+
1292+
processUncoveredSpaces([&](const Space &space,
1293+
bool onlyOneUncoveredSpace) {
1294+
llvm::SmallString<64> fixItBuffer;
1295+
llvm::raw_svector_ostream fixItOS(fixItBuffer);
1296+
if (space.getKind() == SpaceKind::UnknownCase) {
1297+
fixItOS << "@unknown " << tok::kw_default << ":\n<#fatalError()#>\n";
1298+
DE.diagnose(startLoc, diag::missing_unknown_case)
1299+
.fixItInsert(insertLoc, fixItBuffer.str());
1300+
} else {
1301+
llvm::SmallString<64> spaceBuffer;
1302+
llvm::raw_svector_ostream spaceOS(spaceBuffer);
1303+
space.show(spaceOS);
1304+
1305+
fixItOS << tok::kw_case << " " << spaceBuffer << ":\n"
1306+
<< placeholder << "\n";
1307+
DE.diagnose(startLoc, diag::missing_particular_case,
1308+
spaceBuffer.str())
1309+
.fixItInsert(insertLoc, fixItBuffer);
13161310
}
1311+
diagnosedCases += 1;
1312+
missingSeveralCasesFixIt += fixItBuffer;
1313+
});
13171314

1318-
} else {
1319-
processUncoveredSpaces([&](const Space &space,
1320-
bool onlyOneUncoveredSpace) {
1321-
if (space.getKind() == SpaceKind::UnknownCase) {
1322-
auto note = DE.diagnose(startLoc, diag::missing_unknown_case);
1323-
if (onlyOneUncoveredSpace)
1324-
note.fixItInsert(insertLoc, "@unknown default:\n<#fatalError#>()\n");
1325-
return;
1326-
}
1327-
1328-
buffer.clear();
1329-
space.show(OS);
1330-
DE.diagnose(startLoc, diag::missing_particular_case, buffer.str());
1331-
});
1315+
if (diagnosedCases > 1) {
1316+
DE.diagnose(startLoc, diag::missing_several_cases, false)
1317+
.fixItInsert(insertLoc, missingSeveralCasesFixIt.str());
13321318
}
13331319
}
13341320

test/ClangImporter/availability_open_enums.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ func exhaustiveSwitch(e: NSEnumAddedCasesIn2017) {
1010
switch e { // expected-error{{switch must be exhaustive}}
1111
// expected-note@-1{{add missing case: '.newCaseOne'}}
1212
// expected-note@-2{{handle unknown values using "@unknown default"}}
13+
// expected-note@-3 {{add missing cases}}
1314
case .existingCaseOne:
1415
return
1516
case .existingCaseTwo:

test/ClangImporter/enum-dataflow.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ case .original:
1414
switch aliasOriginal { // expected-error {{switch must be exhaustive}}
1515
// expected-note@-1 {{add missing case: '.original'}}
1616
// expected-note@-2 {{add missing case: '.differentValue'}}
17+
// expected-note@-3 {{add missing cases}}
1718
case .bySameValue:
1819
break
1920
}

test/ClangImporter/swift2_warnings.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func useLowercasedEnumCase(x: NSRuncingMode) {
101101
switch x { // expected-error {{switch must be exhaustive}}
102102
// expected-note@-1 {{add missing case: '.mince'}}
103103
// expected-note@-2 {{add missing case: '.quince'}}
104+
// expected-note@-3 {{add missing cases}}
104105
case .Mince: return // expected-error {{'Mince' has been renamed to 'mince'}} {{11-16=mince}}
105106
case .Quince: return // expected-error {{'Quince' has been renamed to 'quince'}} {{11-17=quince}}
106107
}

test/Compatibility/exhaustive_switch.swift

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ func checkDiagnosticMinimality(x: Runcible?) {
368368
// expected-note@-1 {{add missing case: '(.fork, _)'}}
369369
// expected-note@-2 {{add missing case: '(.hat, .hat)'}}
370370
// expected-note@-3 {{add missing case: '(_, .fork)'}}
371+
// expected-note@-4 {{add missing cases}} {{+11:3-3=case (.fork, _):\n<#code#>\ncase (.hat, .hat):\n<#code#>\ncase (_, .fork):\n<#code#>\n}}
371372
case (.spoon, .spoon):
372373
break
373374
case (.spoon, .hat):
@@ -381,6 +382,7 @@ func checkDiagnosticMinimality(x: Runcible?) {
381382
// expected-note@-2 {{add missing case: '(.hat, .spoon)'}}
382383
// expected-note@-3 {{add missing case: '(.spoon, .hat)'}}
383384
// expected-note@-4 {{add missing case: '(_, .fork)'}}
385+
// expected-note@-5 {{add missing cases}} {{+10:3-3=case (.fork, _):\n<#code#>\ncase (.hat, .spoon):\n<#code#>\ncase (.spoon, .hat):\n<#code#>\ncase (_, .fork):\n<#code#>\n}}
384386
case (.spoon, .spoon):
385387
break
386388
case (.hat, .hat):
@@ -405,6 +407,7 @@ enum LargeSpaceEnum {
405407
func notQuiteBigEnough() -> Bool {
406408
switch (LargeSpaceEnum.case1, LargeSpaceEnum.case2) { // expected-error {{switch must be exhaustive}}
407409
// expected-note@-1 110 {{add missing case:}}
410+
// expected-note@-2 {{add missing cases}}
408411
case (.case0, .case0): return true
409412
case (.case1, .case1): return true
410413
case (.case2, .case2): return true
@@ -443,6 +446,7 @@ enum ContainsOverlyLargeEnum {
443446
func quiteBigEnough() -> Bool {
444447
switch (OverlyLargeSpaceEnum.case1, OverlyLargeSpaceEnum.case2) { // expected-error {{switch must be exhaustive}}
445448
// expected-note@-1 132 {{add missing case:}}
449+
// expected-note@-2 {{add missing cases}}
446450
case (.case0, .case0): return true
447451
case (.case1, .case1): return true
448452
case (.case2, .case2): return true
@@ -543,7 +547,7 @@ func quiteBigEnough() -> Bool {
543547
}
544548

545549
// Make sure we haven't just stopped emitting diagnostics.
546-
switch OverlyLargeSpaceEnum.case1 { // expected-error {{switch must be exhaustive}} expected-note 12 {{add missing case}}
550+
switch OverlyLargeSpaceEnum.case1 { // expected-error {{switch must be exhaustive}} expected-note 12 {{add missing case}} expected-note {{add missing cases}}
547551
}
548552
}
549553

@@ -564,12 +568,14 @@ indirect enum MutuallyRecursive {
564568
func infinitelySized() -> Bool {
565569
switch (InfinitelySized.one, InfinitelySized.one) { // expected-error {{switch must be exhaustive}}
566570
// expected-note@-1 8 {{add missing case:}}
571+
// expected-note@-2 {{add missing cases}}
567572
case (.one, .one): return true
568573
case (.two, .two): return true
569574
}
570575

571576
switch (MutuallyRecursive.one, MutuallyRecursive.one) { // expected-error {{switch must be exhaustive}}
572577
// expected-note@-1 8 {{add missing case:}}
578+
// expected-note@-2 {{add missing cases}}
573579
case (.one, .one): return true
574580
case (.two, .two): return true
575581
}
@@ -840,7 +846,7 @@ public enum NonExhaustivePayload {
840846
// case.
841847
@inlinable
842848
public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePayload, for interval: TemporalProxy, flag: Bool) {
843-
switch value { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{none}}
849+
switch value { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{+2:3-3=case .b:\n<#code#>\n}}
844850
case .a: break
845851
}
846852

@@ -861,12 +867,16 @@ public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePa
861867
@unknown case _: break // no-warning
862868
}
863869

864-
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{none}}
870+
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{+2:3-3=case .b:\n<#code#>\n}}
865871
case .a: break
866872
@unknown case _: break
867873
}
868874

869-
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.a'}} {{none}} expected-note {{add missing case: '.b'}} {{none}}
875+
switch value {
876+
// expected-warning@-1 {{switch must be exhaustive}} {{none}}
877+
// expected-note@-2 {{add missing case: '.a'}} {{+5:3-3=case .a:\n<#code#>\n}}
878+
// expected-note@-3 {{add missing case: '.b'}} {{+5:3-3=case .b:\n<#code#>\n}}
879+
// expected-note@-4 {{add missing cases}} {{+5:3-3=case .a:\n<#code#>\ncase .b:\n<#code#>\n}}
870880
@unknown case _: break
871881
}
872882

@@ -932,7 +942,7 @@ public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePa
932942
} // no-warning
933943

934944
// Test payloaded enums.
935-
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}}
945+
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{+2:3-3=case .b(_):\n<#code#>\n}}
936946
case .a: break
937947
}
938948

@@ -953,17 +963,17 @@ public func testNonExhaustive(_ value: NonExhaustive, _ payload: NonExhaustivePa
953963
@unknown case _: break // no-warning
954964
}
955965

956-
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}}
966+
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{+2:3-3=case .b(_):\n<#code#>\n}}
957967
case .a: break
958968
@unknown case _: break
959969
}
960970

961-
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}}
971+
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{+3:3-3=case .b(true):\n<#code#>\n}}
962972
case .a: break
963973
case .b(false): break
964974
}
965975

966-
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}}
976+
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{+3:3-3=case .b(true):\n<#code#>\n}}
967977
case .a: break
968978
case .b(false): break
969979
@unknown case _: break
@@ -1017,12 +1027,15 @@ public func testNonExhaustiveWithinModule(_ value: NonExhaustive, _ payload: Non
10171027
@unknown case _: break // no-warning
10181028
}
10191029

1020-
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{none}}
1030+
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b'}} {{+2:3-3=case .b:\n<#code#>\n}}
10211031
case .a: break
10221032
@unknown case _: break
10231033
}
10241034

1025-
switch value { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.a'}} {{none}} expected-note {{add missing case: '.b'}} {{none}}
1035+
switch value { // expected-warning {{switch must be exhaustive}} {{none}}
1036+
// expected-note@-1 {{add missing case: '.a'}} {{+4:3-3=case .a:\n<#code#>\n}}
1037+
// expected-note@-2 {{add missing case: '.b'}} {{+4:3-3=case .b:\n<#code#>\n}}
1038+
// expected-note@-3 {{add missing cases}} {{+4:3-3=case .a:\n<#code#>\ncase .b:\n<#code#>\n}}
10261039
@unknown case _: break
10271040
}
10281041

@@ -1071,7 +1084,7 @@ public func testNonExhaustiveWithinModule(_ value: NonExhaustive, _ payload: Non
10711084
}
10721085

10731086
// Test payloaded enums.
1074-
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}}
1087+
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{+2:3-3=case .b(_):\n<#code#>\n}}
10751088
case .a: break
10761089
}
10771090

@@ -1092,17 +1105,17 @@ public func testNonExhaustiveWithinModule(_ value: NonExhaustive, _ payload: Non
10921105
@unknown case _: break // no-warning
10931106
}
10941107

1095-
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{none}}
1108+
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(_)'}} {{+2:3-3=case .b(_):\n<#code#>\n}}
10961109
case .a: break
10971110
@unknown case _: break
10981111
}
10991112

1100-
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}}
1113+
switch payload { // expected-error {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{+3:3-3=case .b(true):\n<#code#>\n}}
11011114
case .a: break
11021115
case .b(false): break
11031116
}
11041117

1105-
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{none}}
1118+
switch payload { // expected-warning {{switch must be exhaustive}} {{none}} expected-note {{add missing case: '.b(true)'}} {{+3:3-3=case .b(true):\n<#code#>\n}}
11061119
case .a: break
11071120
case .b(false): break
11081121
@unknown case _: break
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
// RUN: not %swift -typecheck -target %target-triple %s %S/Inputs/fixits-enum-multifile.swift -emit-fixits-path %t.remap -I %S/Inputs -diagnostics-editor-mode
2-
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result
1+
// RUN: %target-typecheck-verify-swift %s %S/Inputs/fixits-enum-multifile.swift -I %S/Inputs
32

43
func foo1(_ e: EMulti) {
54
switch e {
5+
// expected-error@-1 {{switch must be exhaustive}}
6+
// expected-note@-2 {{add missing case: '.e1'}} {{+6:3-3=case .e1:\n<#code#>\n}}
7+
// expected-note@-3 {{add missing case: '.e2'}} {{+6:3-3=case .e2:\n<#code#>\n}}
8+
// expected-note@-4 {{add missing case: '.e3(_)'}} {{+6:3-3=case .e3(_):\n<#code#>\n}}
9+
// expected-note@-5 {{add missing cases}} {{+6:3-3=case .e1:\n<#code#>\ncase .e2:\n<#code#>\ncase .e3(_):\n<#code#>\n}}
610
}
711
}

test/FixCode/fixits-empty-switch-multifile.swift.result

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// RUN: not %swift -typecheck -target %target-triple %s -emit-fixits-path %t.remap -I %S/Inputs -diagnostics-editor-mode
2-
// RUN: c-arcmt-test %t.remap | arcmt-test -verify-transformed-files %s.result
1+
// RUN: %target-typecheck-verify-swift %s -I %S/Inputs -diagnostics-editor-mode
32

43
enum E1 {
54
case e1
@@ -9,10 +8,15 @@ enum E1 {
98

109
func foo1(_ e: E1) {
1110
switch e {
11+
//expected-error@-1 {{switch must be exhaustive}}
12+
//expected-note@-2 {{add missing case: '.e1'}} {{+6:3-3=case .e1:\n<#code#>\n}}
13+
//expected-note@-3 {{add missing case: '.e2'}} {{+6:3-3=case .e2:\n<#code#>\n}}
14+
//expected-note@-4 {{add missing case: '.e3'}} {{+6:3-3=case .e3:\n<#code#>\n}}
15+
//expected-note@-5 {{add missing cases}} {{+6:3-3=case .e1:\n<#code#>\ncase .e2:\n<#code#>\ncase .e3:\n<#code#>\n}}
1216
}
1317
}
1418

1519
func foo1 (_ i : Int) {
16-
switch i {
20+
switch i { // expected-error {{'switch' statement body must have at least one 'case' or 'default' block; add a default case}} {{+1:3-3=default:\n<#code#>\n}}
1721
}
1822
}

test/FixCode/fixits-empty-switch.swift.result

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)