Skip to content

Commit b4cf37b

Browse files
authored
Sema: several improvements on missing switch cases diagnostics. (#8026)
1. Make sure the actions taken by fixits are reflected in diagnostics messages. 2. Issue missing cases diagnostics at the start of the switch statement instead of its end. 3. Use <#code#> instead of <#Code#> in the stub.
1 parent 6ee6378 commit b4cf37b

23 files changed

+126
-85
lines changed

include/swift/AST/ASTPrinter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, tok keyword);
310310
/// Get the length of a keyword or punctuator by its kind.
311311
uint8_t getKeywordLen(tok keyword);
312312

313+
/// Get <#code#>;
314+
StringRef getCodePlaceholder();
315+
313316
} // namespace swift
314317

315318
#endif // LLVM_SWIFT_AST_ASTPRINTER_H

include/swift/AST/DiagnosticsSIL.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,6 @@ ERROR(missing_never_call,none,
186186
"%select{function|closure}1 with uninhabited return type %0 is missing "
187187
"call to another never-returning function on all paths",
188188
(Type, unsigned))
189-
ERROR(non_exhaustive_switch,none,
190-
"switch must be exhaustive, consider adding a default clause", ())
191189
ERROR(guard_body_must_not_fallthrough,none,
192190
"'guard' body may not fall through, consider using a 'return' or 'throw'"
193191
" to exit the scope", ())

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2670,7 +2670,12 @@ ERROR(trailing_closure_requires_parens,none,
26702670
" context", ())
26712671

26722672
ERROR(empty_switch_stmt,none,
2673-
"'switch' statement body must have at least one 'case' or 'default' block",())
2673+
"'switch' statement body must have at least one 'case' or 'default' block;"
2674+
" do you want to add "
2675+
"%select{missing cases?|a default case?}0",(bool))
2676+
ERROR(non_exhaustive_switch,none,
2677+
"switch must be exhaustive, consider adding "
2678+
"%select{missing cases|a default clause}0", (bool))
26742679
//------------------------------------------------------------------------------
26752680
// Type Check Patterns
26762681
//------------------------------------------------------------------------------

include/swift/AST/DiagnosticsSema.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ namespace swift {
3737
extern detail::DiagWithArguments<void Signature>::type ID;
3838
#include "DiagnosticsSema.def"
3939
}
40-
void diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS,
41-
Diagnostic Id);
40+
void diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS);
4241
}
4342

4443
#endif

lib/AST/ASTPrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,8 @@ uint8_t swift::getKeywordLen(tok keyword) {
605605
}
606606
}
607607

608+
StringRef swift::getCodePlaceholder() { return "<#code#>"; }
609+
608610
ASTPrinter &operator<<(ASTPrinter &printer, tok keyword) {
609611
SmallString<16> Buffer;
610612
llvm::raw_svector_ostream OS(Buffer);

lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ static void diagnoseUnreachable(const SILInstruction *I,
8484

8585
// A non-exhaustive switch would also produce an unreachable instruction.
8686
if (L.isASTNode<SwitchStmt>()) {
87-
diagnoseMissingCases(Context, L.getAsASTNode<SwitchStmt>(),
88-
diag::non_exhaustive_switch);
87+
diagnoseMissingCases(Context, L.getAsASTNode<SwitchStmt>());
8988
return;
9089
}
9190

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2632,7 +2632,7 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
26322632
Options.PrintDocumentationComments = false;
26332633
Options.AccessibilityFilter = Accessibility::Private;
26342634
Options.PrintAccessibility = false;
2635-
Options.FunctionBody = [](const ValueDecl *VD) { return "<#code#>"; };
2635+
Options.FunctionBody = [](const ValueDecl *VD) { return getCodePlaceholder(); };
26362636
Options.setBaseType(AdopterTy);
26372637
Options.CurrentModule = Adopter->getParentModule();
26382638
if (!Adopter->isExtensionContext()) {

lib/Sema/TypeCheckStmt.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,16 +212,19 @@ removedHandledEnumElements(Pattern *P,
212212
return false;
213213
};
214214

215-
void swift::diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS,
216-
Diagnostic Id) {
215+
void swift::
216+
diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS) {
217+
bool Empty = SwitchS->getCases().empty();
218+
SourceLoc StartLoc = SwitchS->getStartLoc();
217219
SourceLoc EndLoc = SwitchS->getEndLoc();
218-
StringRef Placeholder = "<#Code#>";
220+
StringRef Placeholder = getCodePlaceholder();
219221
SmallString<32> Buffer;
220222
llvm::raw_svector_ostream OS(Buffer);
221223

222224
auto DefaultDiag = [&]() {
223225
OS << tok::kw_default << ": " << Placeholder << "\n";
224-
Context.Diags.diagnose(EndLoc, Id).fixItInsert(EndLoc, Buffer.str());
226+
Context.Diags.diagnose(StartLoc, Empty ? diag::empty_switch_stmt :
227+
diag::non_exhaustive_switch, true).fixItInsert(EndLoc, Buffer.str());
225228
};
226229
// To find the subject enum decl for this switch statement.
227230
EnumDecl *SubjectED = nullptr;
@@ -307,7 +310,8 @@ void swift::diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS,
307310
printPayloads(EE, OS);
308311
OS <<": " << Placeholder << "\n";
309312
});
310-
Context.Diags.diagnose(EndLoc, Id).fixItInsert(EndLoc, Buffer.str());
313+
Context.Diags.diagnose(StartLoc, Empty ? diag::empty_switch_stmt :
314+
diag::non_exhaustive_switch, false).fixItInsert(EndLoc, Buffer.str());
311315
}
312316

313317
static void setAutoClosureDiscriminators(DeclContext *DC, Stmt *S) {
@@ -981,7 +985,7 @@ class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
981985

982986
// Reject switch statements with empty blocks.
983987
if (S->getCases().empty())
984-
diagnoseMissingCases(TC.Context, S, diag::empty_switch_stmt);
988+
diagnoseMissingCases(TC.Context, S);
985989
for (unsigned i = 0, e = S->getCases().size(); i < e; ++i) {
986990
auto *caseBlock = S->getCases()[i];
987991
// Fallthrough transfers control to the next case block. In the

test/ClangImporter/enum-dataflow.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import user_objc
77

88
let aliasOriginal = NSAliasesEnum.byName
99

10-
switch aliasOriginal {
10+
switch aliasOriginal { // expected-error {{switch must be exhaustive, consider adding missing cases}}
1111
case .original:
1212
break
13-
} // expected-error {{switch must be exhaustive, consider adding a default clause}}
13+
}
1414

15-
switch aliasOriginal {
15+
switch aliasOriginal { // expected-error {{switch must be exhaustive, consider adding missing cases}}
1616
case .bySameValue:
1717
break
18-
} // expected-error {{switch must be exhaustive, consider adding a default clause}}
18+
}

test/ClangImporter/enum-error.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func testError() {
8282
switch (terr) { case .TENone, .TEOne, .TETwo: break } // ok
8383

8484
switch (terr) { case .TENone, .TEOne: break }
85-
// expected-error@-1 {{switch must be exhaustive, consider adding a default clause}}
85+
// expected-error@-1 {{switch must be exhaustive, consider adding missing cases}}
8686

8787
let _ = TestError.Code(rawValue: 2)!
8888

test/ClangImporter/enum-new.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ func test() {
1313
case .Yellow, .Magenta, .Black, .Cyan: break
1414
} // no-error
1515

16-
switch getColorOptions() {
16+
switch getColorOptions() { // expected-error {{switch must be exhaustive, consider adding a default clause}}
1717
case ColorOptions.Pastel: break
1818
case ColorOptions.Swift: break
19-
} // expected-error {{switch must be exhaustive, consider adding a default clause}}
19+
}
2020

21-
switch 5 as Int16 {
21+
switch 5 as Int16 { // expected-error {{switch must be exhaustive, consider adding a default clause}}
2222
case Zero: break // no-error
23-
} // expected-error {{switch must be exhaustive, consider adding a default clause}}
23+
}
2424
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ enum E1 {
99

1010
func foo1(_ e: E1) {
1111
switch e {
12-
case .e1: <#Code#>
13-
case .e2: <#Code#>
14-
case .e3: <#Code#>
12+
case .e1: <#code#>
13+
case .e2: <#code#>
14+
case .e3: <#code#>
1515
}
1616
}
1717

1818
func foo1 (_ i : Int) {
1919
switch i {
20-
default: <#Code#>
20+
default: <#code#>
2121
}
2222
}

test/FixCode/fixits-switch.swift.result

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,25 @@ func foo1(_ e : E1) -> Int {
1212
switch(e) {
1313
case .e1:
1414
return 1
15-
case .e2: <#Code#>
16-
case .e3: <#Code#>
17-
case .e4: <#Code#>
15+
case .e2: <#code#>
16+
case .e3: <#code#>
17+
case .e4: <#code#>
1818
}
1919
}
2020

2121
func foo2(_ i : Int) -> Int {
2222
switch i {
2323
case 1:
2424
return 1
25-
default: <#Code#>
25+
default: <#code#>
2626
}
2727
}
2828

2929
func foo3(_ c : Character) -> Character {
3030
switch c {
3131
case "a":
3232
return "a"
33-
default: <#Code#>
33+
default: <#code#>
3434
}
3535
}
3636

@@ -50,37 +50,37 @@ func foo4(_ e : E2) -> Int {
5050
switch e {
5151
case .e2:
5252
return 1
53-
case .e1(let a, let s): <#Code#>
54-
case .e3(let a): <#Code#>
55-
case .e4(_): <#Code#>
56-
case .e5(_, _): <#Code#>
57-
case .e6(let a, _): <#Code#>
58-
case .e7: <#Code#>
59-
case .e8(let a, _, _): <#Code#>
60-
case .e9(_, _, _): <#Code#>
53+
case .e1(let a, let s): <#code#>
54+
case .e3(let a): <#code#>
55+
case .e4(_): <#code#>
56+
case .e5(_, _): <#code#>
57+
case .e6(let a, _): <#code#>
58+
case .e7: <#code#>
59+
case .e8(let a, _, _): <#code#>
60+
case .e9(_, _, _): <#code#>
6161
}
6262
}
6363

6464
func foo5(_ e : E1) -> Int {
6565
switch e {
6666
case _ where e.rawValue > 0:
6767
return 1
68-
default: <#Code#>
68+
default: <#code#>
6969
}
7070
}
7171

7272
func foo6(_ e : E2) -> Int {
7373
switch e {
7474
case let .e1(x, y):
7575
return x + y
76-
case .e2(let a): <#Code#>
77-
case .e3(let a): <#Code#>
78-
case .e4(_): <#Code#>
79-
case .e5(_, _): <#Code#>
80-
case .e6(let a, _): <#Code#>
81-
case .e7: <#Code#>
82-
case .e8(let a, _, _): <#Code#>
83-
case .e9(_, _, _): <#Code#>
76+
case .e2(let a): <#code#>
77+
case .e3(let a): <#code#>
78+
case .e4(_): <#code#>
79+
case .e5(_, _): <#code#>
80+
case .e6(let a, _): <#code#>
81+
case .e7: <#code#>
82+
case .e8(let a, _, _): <#code#>
83+
case .e9(_, _, _): <#code#>
8484
}
8585
}
8686

@@ -89,11 +89,11 @@ func foo7(_ e : E2) -> Int {
8989
case .e2(1): return 0
9090
case .e1: return 0
9191
case .e3: return 0
92-
case .e4(_): <#Code#>
93-
case .e5(_, _): <#Code#>
94-
case .e6(let a, _): <#Code#>
95-
case .e7: <#Code#>
96-
case .e8(let a, _, _): <#Code#>
97-
case .e9(_, _, _): <#Code#>
92+
case .e4(_): <#code#>
93+
case .e5(_, _): <#code#>
94+
case .e6(let a, _): <#code#>
95+
case .e7: <#code#>
96+
case .e8(let a, _, _): <#code#>
97+
case .e9(_, _, _): <#code#>
9898
}
9999
}

test/Parse/implicit_getter_incomplete.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ func test1() {
1313
// Would trigger assertion when AST verifier checks source ranges ("child source range not contained within its parent")
1414
func test2() { // expected-note {{match}}
1515
var a : Int { // expected-note {{match}}
16-
switch i { // expected-error {{unresolved identifier}}
17-
} // expected-error {{'switch' statement body must have at least one}}
16+
switch i { // expected-error {{unresolved identifier}} expected-error{{'switch' statement body must have at least one 'case'}}
17+
}
1818
// expected-error@+1 2 {{expected '}'}}

test/Parse/invalid.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func testNotCoveredCase(x: Int) {
5151
break
5252
}
5353

54-
switch x {
54+
switch x { // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}}
5555
#if true // expected-error {{all statements inside a switch must be covered by a 'case' or 'default'}}
5656
case 1:
5757
break
@@ -60,7 +60,7 @@ func testNotCoveredCase(x: Int) {
6060
default:
6161
break
6262
#endif
63-
} // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}}
63+
}
6464
}
6565

6666
// rdar://18926814

test/Parse/recovery.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,12 @@ func missingControllingExprInForEach() {
203203
func missingControllingExprInSwitch() {
204204
switch // expected-error {{expected expression in 'switch' statement}} expected-error {{expected '{' after 'switch' subject expression}}
205205

206-
switch { // expected-error {{expected expression in 'switch' statement}}
207-
} // expected-error {{'switch' statement body must have at least one 'case' or 'default' block}}
206+
switch { // expected-error {{expected expression in 'switch' statement}} expected-error {{'switch' statement body must have at least one 'case' or 'default' block}}
207+
}
208208

209-
switch // expected-error {{expected expression in 'switch' statement}}
209+
switch // expected-error {{expected expression in 'switch' statement}} expected-error {{'switch' statement body must have at least one 'case' or 'default' block}}
210210
{
211-
} // expected-error {{'switch' statement body must have at least one 'case' or 'default' block}}
211+
}
212212

213213
switch { // expected-error {{expected expression in 'switch' statement}}
214214
case _: return

test/Parse/switch.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ default: // expected-error{{additional 'case' blocks cannot appear after the 'de
121121
x = 0
122122
}
123123

124-
switch x {
124+
switch x { // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}}
125125
x = 1 // expected-error{{all statements inside a switch must be covered by a 'case' or 'default'}}
126-
} // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}}
126+
}
127127

128-
switch x {
128+
switch x { // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}}
129129
x = 1 // expected-error{{all statements inside a switch must be covered by a 'case' or 'default'}}
130130
x = 2
131-
} // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}}
131+
}
132132

133133
switch x {
134134
default: // expected-error{{'default' label in a 'switch' should have at least one executable statement}} {{9-9= break}}

test/SILOptimizer/switch.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
func non_fully_covered_switch(x: Int) -> Int {
44
var x = x
5-
switch x {
5+
switch x { // expected-error{{switch must be exhaustive}}
66
case 0:
77
x += 1
88
case 3:
99
x -= 1
10-
} // expected-error{{switch must be exhaustive}}
10+
}
1111
return x
1212
}
1313

0 commit comments

Comments
 (0)