Skip to content

Commit 7c7bc25

Browse files
committed
Diagnose enum cases renamed to lowercase
When importing foreign enum cases, we create a new case with a lowercase name, and set the renamed unavailability bits on the imported name, so they are still visible to the compiler but unavailable, so we can throw a diagnostic. Renamed enum cases can be found by digging through a VarDecl in the AST, and there were no availability checks there. This area probably needs deeper refactoring, but add an UnavailableInCurrentSwift check for now. https://bugs.swift.org/browse/SR-1496 rdar://problem/26245512
1 parent 4b5e617 commit 7c7bc25

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

lib/Sema/TypeCheckPattern.cpp

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,17 @@ using namespace swift;
3131
/// This requires the getter's body to have a certain syntactic form. It should
3232
/// be kept in sync with importEnumCaseAlias in the ClangImporter library.
3333
static EnumElementDecl *
34-
extractEnumElement(const VarDecl *constant) {
34+
extractEnumElement(TypeChecker &TC, SourceLoc UseLoc, const VarDecl *constant) {
35+
if (auto Attr = AvailableAttr::isUnavailable(constant)) {
36+
auto Kind = Attr->getUnconditionalAvailability();
37+
if (Kind == UnconditionalAvailabilityKind::UnavailableInCurrentSwift) {
38+
auto diag = TC.diagnose(UseLoc, diag::availability_decl_unavailable_rename,
39+
constant->getName(), /*"replaced"*/false,
40+
/*special kind*/0, Attr->Rename);
41+
diag.fixItReplace(UseLoc, Attr->Rename);
42+
}
43+
}
44+
3545
const FuncDecl *getter = constant->getGetter();
3646
if (!getter)
3747
return nullptr;
@@ -61,7 +71,8 @@ extractEnumElement(const VarDecl *constant) {
6171
/// If there are no enum elements but there are properties, attempts to map
6272
/// an arbitrary property to an enum element using extractEnumElement.
6373
static EnumElementDecl *
64-
filterForEnumElement(LookupResult foundElements) {
74+
filterForEnumElement(TypeChecker &TC, SourceLoc UseLoc,
75+
LookupResult foundElements) {
6576
EnumElementDecl *foundElement = nullptr;
6677
VarDecl *foundConstant = nullptr;
6778

@@ -85,32 +96,32 @@ filterForEnumElement(LookupResult foundElements) {
8596
}
8697

8798
if (!foundElement && foundConstant && foundConstant->hasClangNode())
88-
foundElement = extractEnumElement(foundConstant);
99+
foundElement = extractEnumElement(TC, UseLoc, foundConstant);
89100

90101
return foundElement;
91102
}
92103

93104
/// Find an unqualified enum element.
94105
static EnumElementDecl *
95106
lookupUnqualifiedEnumMemberElement(TypeChecker &TC, DeclContext *DC,
96-
Identifier name) {
107+
Identifier name, SourceLoc UseLoc) {
97108
auto lookupOptions = defaultUnqualifiedLookupOptions;
98109
lookupOptions |= NameLookupFlags::KnownPrivate;
99110
auto lookup = TC.lookupUnqualified(DC, name, SourceLoc(), lookupOptions);
100-
return filterForEnumElement(lookup);
111+
return filterForEnumElement(TC, UseLoc, lookup);
101112
}
102113

103114
/// Find an enum element in an enum type.
104115
static EnumElementDecl *
105116
lookupEnumMemberElement(TypeChecker &TC, DeclContext *DC, Type ty,
106-
Identifier name) {
117+
Identifier name, SourceLoc UseLoc) {
107118
assert(ty->getAnyNominal());
108119
// Look up the case inside the enum.
109120
// FIXME: We should be able to tell if this is a private lookup.
110121
NameLookupOptions lookupOptions
111122
= defaultMemberLookupOptions - NameLookupFlags::DynamicLookup;
112123
LookupResult foundElements = TC.lookupMember(DC, ty, name, lookupOptions);
113-
return filterForEnumElement(foundElements);
124+
return filterForEnumElement(TC, UseLoc, foundElements);
114125
}
115126

116127
namespace {
@@ -435,7 +446,8 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
435446

436447
// FIXME: Argument labels?
437448
EnumElementDecl *referencedElement
438-
= lookupEnumMemberElement(TC, DC, ty, ude->getName().getBaseName());
449+
= lookupEnumMemberElement(TC, DC, ty, ude->getName().getBaseName(),
450+
ude->getLoc());
439451

440452
// Build a TypeRepr from the head of the full path.
441453
// FIXME: Compound names.
@@ -473,7 +485,8 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
473485
// Try looking up an enum element in context.
474486
if (EnumElementDecl *referencedElement
475487
= lookupUnqualifiedEnumMemberElement(TC, DC,
476-
ude->getName().getBaseName())) {
488+
ude->getName().getBaseName(),
489+
ude->getLoc())) {
477490
auto *enumDecl = referencedElement->getParentEnum();
478491
auto enumTy = enumDecl->getDeclaredTypeInContext();
479492
TypeLoc loc = TypeLoc::withoutLoc(enumTy);
@@ -557,7 +570,8 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
557570
if (auto compId = dyn_cast<ComponentIdentTypeRepr>(repr)) {
558571
// Try looking up an enum element in context.
559572
EnumElementDecl *referencedElement
560-
= lookupUnqualifiedEnumMemberElement(TC, DC, compId->getIdentifier());
573+
= lookupUnqualifiedEnumMemberElement(TC, DC, compId->getIdentifier(),
574+
repr->getLoc());
561575

562576
if (!referencedElement)
563577
return nullptr;
@@ -595,7 +609,8 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
595609
auto tailComponent = compoundR->Components.back();
596610

597611
EnumElementDecl *referencedElement
598-
= lookupEnumMemberElement(TC, DC, enumTy, tailComponent->getIdentifier());
612+
= lookupEnumMemberElement(TC, DC, enumTy, tailComponent->getIdentifier(),
613+
tailComponent->getLoc());
599614
if (!referencedElement)
600615
return nullptr;
601616

@@ -1381,8 +1396,10 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type,
13811396

13821397
Type enumTy;
13831398
if (!elt) {
1384-
if (type->getAnyNominal())
1385-
elt = lookupEnumMemberElement(*this, dc, type, EEP->getName());
1399+
if (type->getAnyNominal()) {
1400+
elt = lookupEnumMemberElement(*this, dc, type, EEP->getName(),
1401+
EEP->getLoc());
1402+
}
13861403
if (!elt) {
13871404
if (!type->is<ErrorType>())
13881405
diagnose(EEP->getLoc(), diag::enum_element_pattern_member_not_found,
@@ -1492,7 +1509,8 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type,
14921509
// If the element decl was not resolved (because it was spelled without a
14931510
// type as `.Foo`), resolve it now that we have a type.
14941511
if (!OP->getElementDecl()) {
1495-
auto *element = lookupEnumMemberElement(*this, dc, type, Context.Id_some);
1512+
auto *element = lookupEnumMemberElement(*this, dc, type, Context.Id_some,
1513+
OP->getLoc());
14961514
if (!element) {
14971515
diagnose(OP->getLoc(), diag::enum_element_pattern_member_not_found,
14981516
"Some", type);

test/ClangModules/swift2_warnings.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,10 @@ class X : NSDocument {
9595
}
9696

9797
func makeCopy<T: NSWobbling>(thing: T) {} // expected-error {{'NSWobbling' has been renamed to 'Wobbling'}} {{18-28=Wobbling}}
98+
99+
func useLowercasedEnumCase(x: RuncingMode) {
100+
switch x {
101+
case .Mince: return // expected-error {{'Mince' has been renamed to 'mince'}} {{11-16=mince}}
102+
case .Quince: return // expected-error {{'Quince' has been renamed to 'quince'}} {{11-17=quince}}
103+
}
104+
}

0 commit comments

Comments
 (0)