Skip to content

Commit 74cfee1

Browse files
authored
[Typechecker] Extended ambiguous none warning to cases as well (#29356)
1 parent a2f9b66 commit 74cfee1

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,8 +1583,12 @@ WARNING(optional_ambiguous_case_ref,none,
15831583
(StringRef, StringRef, StringRef))
15841584
NOTE(optional_fixit_ambiguous_case_ref,none,
15851585
"explicitly specify 'Optional' to silence this warning", ())
1586+
NOTE(optional_fixit_ambiguous_case_ref_switch,none,
1587+
"use 'nil' to silence this warning", ())
15861588
NOTE(type_fixit_optional_ambiguous_case_ref,none,
15871589
"use '%0.%1' instead", (StringRef, StringRef))
1590+
NOTE(type_fixit_optional_ambiguous_case_ref_switch,none,
1591+
"use '%0' instead", (StringRef))
15881592

15891593
ERROR(nscoding_unstable_mangled_name,none,
15901594
"%select{private|fileprivate|nested|local}0 class %1 has an "

lib/Sema/TypeCheckPattern.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,43 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern,
13401340
if (!elt)
13411341
return nullptr;
13421342

1343+
// Emit an ambiguous none diagnostic if:
1344+
// 1) We have an Optional<T> type.
1345+
// 2) We're matching a 'none' enum case.
1346+
// 3) The 'none' enum case exists in T too.
1347+
if (EEP->getName().isSimpleName("none") &&
1348+
type->getOptionalObjectType()) {
1349+
SmallVector<Type, 4> allOptionals;
1350+
auto baseTyUnwrapped = type->lookThroughAllOptionalTypes(allOptionals);
1351+
if (lookupEnumMemberElement(dc, baseTyUnwrapped, EEP->getName(),
1352+
EEP->getLoc())) {
1353+
auto baseTyName = type->getCanonicalType().getString();
1354+
auto baseTyUnwrappedName = baseTyUnwrapped->getString();
1355+
diags.diagnoseWithNotes(
1356+
diags.diagnose(EEP->getLoc(), diag::optional_ambiguous_case_ref,
1357+
baseTyName, baseTyUnwrappedName, "none"),
1358+
[&]() {
1359+
// Emit a note to swap '.none' with 'nil' to match with
1360+
// the 'none' case in Optional<T>.
1361+
diags.diagnose(EEP->getLoc(),
1362+
diag::optional_fixit_ambiguous_case_ref_switch)
1363+
.fixItReplace(EEP->getSourceRange(), "nil");
1364+
// Emit a note to swap '.none' with 'none?' to match with the
1365+
// 'none' case in T. Add as many '?' as needed to look though
1366+
// all the optionals.
1367+
std::string fixItString = "none";
1368+
llvm::for_each(allOptionals,
1369+
[&](const Type) { fixItString += "?"; });
1370+
diags.diagnose(
1371+
EEP->getLoc(),
1372+
diag::type_fixit_optional_ambiguous_case_ref_switch,
1373+
fixItString)
1374+
.fixItReplace(EEP->getNameLoc().getSourceRange(),
1375+
fixItString);
1376+
});
1377+
}
1378+
}
1379+
13431380
enumTy = type;
13441381
} else {
13451382
// Check if the explicitly-written enum type matches the type we're

test/decl/enum/enumtest.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,65 @@ let _: EnumWithStructNone? = .none // Okay
554554
let _: EnumWithTypealiasNone? = .none // Okay
555555
let _: EnumWithBothStructAndComputedNone? = .none // Okay
556556
let _: EnumWithBothTypealiasAndComputedNone? = .none // Okay
557+
558+
// SR-12063
559+
560+
let foo1: Foo? = Foo.none
561+
let foo2: Foo?? = Foo.none
562+
563+
switch foo1 {
564+
case .none: break
565+
// expected-warning@-1 {{assuming you mean 'Optional<Foo>.none'; did you mean 'Foo.none' instead?}}
566+
// expected-note@-2 {{use 'nil' to silence this warning}}{{8-13=nil}}
567+
// expected-note@-3 {{use 'none?' instead}}{{9-13=none?}}
568+
case .bar: break
569+
default: break
570+
}
571+
572+
switch foo2 {
573+
case .none: break
574+
// expected-warning@-1 {{assuming you mean 'Optional<Optional<Foo>>.none'; did you mean 'Foo.none' instead?}}
575+
// expected-note@-2 {{use 'nil' to silence this warning}}{{8-13=nil}}
576+
// expected-note@-3 {{use 'none??' instead}}{{9-13=none??}}
577+
case .bar: break
578+
default: break
579+
}
580+
581+
if case .none = foo1 {}
582+
// expected-warning@-1 {{assuming you mean 'Optional<Foo>.none'; did you mean 'Foo.none' instead?}}
583+
// expected-note@-2 {{use 'nil' to silence this warning}}{{9-14=nil}}
584+
// expected-note@-3 {{use 'none?' instead}}{{10-14=none?}}
585+
586+
if case .none = foo2 {}
587+
// expected-warning@-1 {{assuming you mean 'Optional<Optional<Foo>>.none'; did you mean 'Foo.none' instead?}}
588+
// expected-note@-2 {{use 'nil' to silence this warning}}{{9-14=nil}}
589+
// expected-note@-3 {{use 'none??' instead}}{{10-14=none??}}
590+
591+
switch foo1 {
592+
case nil: break // Okay
593+
case .bar: break
594+
default: break
595+
}
596+
597+
switch foo1 {
598+
case .none?: break // Okay
599+
case .bar: break
600+
default: break
601+
}
602+
603+
switch foo2 {
604+
case nil: break // Okay
605+
case .bar: break
606+
default: break
607+
}
608+
609+
switch foo2 {
610+
case .none??: break // Okay
611+
case .bar: break
612+
default: break
613+
}
614+
615+
if case nil = foo1 {} // Okay
616+
if case .none? = foo1 {} // Okay
617+
if case nil = foo2 {} // Okay
618+
if case .none?? = foo2 {} // Okay

0 commit comments

Comments
 (0)