Skip to content

Sema: make sure missing enum case fixits are consistent with the diagnostics messages. #8026

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/ASTPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, tok keyword);
/// Get the length of a keyword or punctuator by its kind.
uint8_t getKeywordLen(tok keyword);

/// Get <#code#>;
StringRef getCodePlaceholder();

} // namespace swift

#endif // LLVM_SWIFT_AST_ASTPRINTER_H
2 changes: 0 additions & 2 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,6 @@ ERROR(missing_never_call,none,
"%select{function|closure}1 with uninhabited return type %0 is missing "
"call to another never-returning function on all paths",
(Type, unsigned))
ERROR(non_exhaustive_switch,none,
"switch must be exhaustive, consider adding a default clause", ())
ERROR(guard_body_must_not_fallthrough,none,
"'guard' body may not fall through, consider using a 'return' or 'throw'"
" to exit the scope", ())
Expand Down
7 changes: 6 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2670,7 +2670,12 @@ ERROR(trailing_closure_requires_parens,none,
" context", ())

ERROR(empty_switch_stmt,none,
"'switch' statement body must have at least one 'case' or 'default' block",())
"'switch' statement body must have at least one 'case' or 'default' block;"
" do you want to add "
"%select{missing cases?|a default case?}0",(bool))
ERROR(non_exhaustive_switch,none,
"switch must be exhaustive, consider adding "
"%select{missing cases|a default clause}0", (bool))
//------------------------------------------------------------------------------
// Type Check Patterns
//------------------------------------------------------------------------------
Expand Down
3 changes: 1 addition & 2 deletions include/swift/AST/DiagnosticsSema.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ namespace swift {
extern detail::DiagWithArguments<void Signature>::type ID;
#include "DiagnosticsSema.def"
}
void diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS,
Diagnostic Id);
void diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS);
}

#endif
2 changes: 2 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,8 @@ uint8_t swift::getKeywordLen(tok keyword) {
}
}

StringRef swift::getCodePlaceholder() { return "<#code#>"; }

ASTPrinter &operator<<(ASTPrinter &printer, tok keyword) {
SmallString<16> Buffer;
llvm::raw_svector_ostream OS(Buffer);
Expand Down
3 changes: 1 addition & 2 deletions lib/SILOptimizer/Mandatory/DataflowDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ static void diagnoseUnreachable(const SILInstruction *I,

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

Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2632,7 +2632,7 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
Options.PrintDocumentationComments = false;
Options.AccessibilityFilter = Accessibility::Private;
Options.PrintAccessibility = false;
Options.FunctionBody = [](const ValueDecl *VD) { return "<#code#>"; };
Options.FunctionBody = [](const ValueDecl *VD) { return getCodePlaceholder(); };
Options.setBaseType(AdopterTy);
Options.CurrentModule = Adopter->getParentModule();
if (!Adopter->isExtensionContext()) {
Expand Down
16 changes: 10 additions & 6 deletions lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,19 @@ removedHandledEnumElements(Pattern *P,
return false;
};

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

auto DefaultDiag = [&]() {
OS << tok::kw_default << ": " << Placeholder << "\n";
Context.Diags.diagnose(EndLoc, Id).fixItInsert(EndLoc, Buffer.str());
Context.Diags.diagnose(StartLoc, Empty ? diag::empty_switch_stmt :
diag::non_exhaustive_switch, true).fixItInsert(EndLoc, Buffer.str());
};
// To find the subject enum decl for this switch statement.
EnumDecl *SubjectED = nullptr;
Expand Down Expand Up @@ -307,7 +310,8 @@ void swift::diagnoseMissingCases(ASTContext &Context, const SwitchStmt *SwitchS,
printPayloads(EE, OS);
OS <<": " << Placeholder << "\n";
});
Context.Diags.diagnose(EndLoc, Id).fixItInsert(EndLoc, Buffer.str());
Context.Diags.diagnose(StartLoc, Empty ? diag::empty_switch_stmt :
diag::non_exhaustive_switch, false).fixItInsert(EndLoc, Buffer.str());
}

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

// Reject switch statements with empty blocks.
if (S->getCases().empty())
diagnoseMissingCases(TC.Context, S, diag::empty_switch_stmt);
diagnoseMissingCases(TC.Context, S);
for (unsigned i = 0, e = S->getCases().size(); i < e; ++i) {
auto *caseBlock = S->getCases()[i];
// Fallthrough transfers control to the next case block. In the
Expand Down
8 changes: 4 additions & 4 deletions test/ClangImporter/enum-dataflow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import user_objc

let aliasOriginal = NSAliasesEnum.byName

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

switch aliasOriginal {
switch aliasOriginal { // expected-error {{switch must be exhaustive, consider adding missing cases}}
case .bySameValue:
break
} // expected-error {{switch must be exhaustive, consider adding a default clause}}
}
2 changes: 1 addition & 1 deletion test/ClangImporter/enum-error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func testError() {
switch (terr) { case .TENone, .TEOne, .TETwo: break } // ok

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

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

Expand Down
8 changes: 4 additions & 4 deletions test/ClangImporter/enum-new.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ func test() {
case .Yellow, .Magenta, .Black, .Cyan: break
} // no-error

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

switch 5 as Int16 {
switch 5 as Int16 { // expected-error {{switch must be exhaustive, consider adding a default clause}}
case Zero: break // no-error
} // expected-error {{switch must be exhaustive, consider adding a default clause}}
}
}
8 changes: 4 additions & 4 deletions test/FixCode/fixits-empty-switch.swift.result
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ enum E1 {

func foo1(_ e: E1) {
switch e {
case .e1: <#Code#>
case .e2: <#Code#>
case .e3: <#Code#>
case .e1: <#code#>
case .e2: <#code#>
case .e3: <#code#>
}
}

func foo1 (_ i : Int) {
switch i {
default: <#Code#>
default: <#code#>
}
}
56 changes: 28 additions & 28 deletions test/FixCode/fixits-switch.swift.result
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,25 @@ func foo1(_ e : E1) -> Int {
switch(e) {
case .e1:
return 1
case .e2: <#Code#>
case .e3: <#Code#>
case .e4: <#Code#>
case .e2: <#code#>
case .e3: <#code#>
case .e4: <#code#>
}
}

func foo2(_ i : Int) -> Int {
switch i {
case 1:
return 1
default: <#Code#>
default: <#code#>
}
}

func foo3(_ c : Character) -> Character {
switch c {
case "a":
return "a"
default: <#Code#>
default: <#code#>
}
}

Expand All @@ -50,37 +50,37 @@ func foo4(_ e : E2) -> Int {
switch e {
case .e2:
return 1
case .e1(let a, let s): <#Code#>
case .e3(let a): <#Code#>
case .e4(_): <#Code#>
case .e5(_, _): <#Code#>
case .e6(let a, _): <#Code#>
case .e7: <#Code#>
case .e8(let a, _, _): <#Code#>
case .e9(_, _, _): <#Code#>
case .e1(let a, let s): <#code#>
case .e3(let a): <#code#>
case .e4(_): <#code#>
case .e5(_, _): <#code#>
case .e6(let a, _): <#code#>
case .e7: <#code#>
case .e8(let a, _, _): <#code#>
case .e9(_, _, _): <#code#>
}
}

func foo5(_ e : E1) -> Int {
switch e {
case _ where e.rawValue > 0:
return 1
default: <#Code#>
default: <#code#>
}
}

func foo6(_ e : E2) -> Int {
switch e {
case let .e1(x, y):
return x + y
case .e2(let a): <#Code#>
case .e3(let a): <#Code#>
case .e4(_): <#Code#>
case .e5(_, _): <#Code#>
case .e6(let a, _): <#Code#>
case .e7: <#Code#>
case .e8(let a, _, _): <#Code#>
case .e9(_, _, _): <#Code#>
case .e2(let a): <#code#>
case .e3(let a): <#code#>
case .e4(_): <#code#>
case .e5(_, _): <#code#>
case .e6(let a, _): <#code#>
case .e7: <#code#>
case .e8(let a, _, _): <#code#>
case .e9(_, _, _): <#code#>
}
}

Expand All @@ -89,11 +89,11 @@ func foo7(_ e : E2) -> Int {
case .e2(1): return 0
case .e1: return 0
case .e3: return 0
case .e4(_): <#Code#>
case .e5(_, _): <#Code#>
case .e6(let a, _): <#Code#>
case .e7: <#Code#>
case .e8(let a, _, _): <#Code#>
case .e9(_, _, _): <#Code#>
case .e4(_): <#code#>
case .e5(_, _): <#code#>
case .e6(let a, _): <#code#>
case .e7: <#code#>
case .e8(let a, _, _): <#code#>
case .e9(_, _, _): <#code#>
}
}
4 changes: 2 additions & 2 deletions test/Parse/implicit_getter_incomplete.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ func test1() {
// Would trigger assertion when AST verifier checks source ranges ("child source range not contained within its parent")
func test2() { // expected-note {{match}}
var a : Int { // expected-note {{match}}
switch i { // expected-error {{unresolved identifier}}
} // expected-error {{'switch' statement body must have at least one}}
switch i { // expected-error {{unresolved identifier}} expected-error{{'switch' statement body must have at least one 'case'}}
}
// expected-error@+1 2 {{expected '}'}}
4 changes: 2 additions & 2 deletions test/Parse/invalid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func testNotCoveredCase(x: Int) {
break
}

switch x {
switch x { // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}}
#if true // expected-error {{all statements inside a switch must be covered by a 'case' or 'default'}}
case 1:
break
Expand All @@ -60,7 +60,7 @@ func testNotCoveredCase(x: Int) {
default:
break
#endif
} // expected-error{{'switch' statement body must have at least one 'case' or 'default' block}}
}
}

// rdar://18926814
Expand Down
8 changes: 4 additions & 4 deletions test/Parse/recovery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,12 @@ func missingControllingExprInForEach() {
func missingControllingExprInSwitch() {
switch // expected-error {{expected expression in 'switch' statement}} expected-error {{expected '{' after 'switch' subject expression}}

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

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

switch { // expected-error {{expected expression in 'switch' statement}}
case _: return
Expand Down
8 changes: 4 additions & 4 deletions test/Parse/switch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ default: // expected-error{{additional 'case' blocks cannot appear after the 'de
x = 0
}

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

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

switch x {
default: // expected-error{{'default' label in a 'switch' should have at least one executable statement}} {{9-9= break}}
Expand Down
4 changes: 2 additions & 2 deletions test/SILOptimizer/switch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

func non_fully_covered_switch(x: Int) -> Int {
var x = x
switch x {
switch x { // expected-error{{switch must be exhaustive}}
case 0:
x += 1
case 3:
x -= 1
} // expected-error{{switch must be exhaustive}}
}
return x
}

Loading