Skip to content

Commit ae1058a

Browse files
committed
[Sema] Provide warnings when accessing enum elements as instance members as a preparation for SE-0036
1 parent 911dfa8 commit ae1058a

File tree

11 files changed

+207
-15
lines changed

11 files changed

+207
-15
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ ERROR(could_not_use_type_member,none,
9393
ERROR(could_not_use_type_member_on_instance,none,
9494
"static member %1 cannot be used on instance of type %0",
9595
(Type, DeclName))
96+
WARNING(could_not_use_enum_element_on_instance,none,
97+
"referencing enum element %0 as instance member is deprecated and will "
98+
"be removed in Swift 3",
99+
(DeclName))
96100
ERROR(could_not_use_type_member_on_existential,none,
97101
"static member %1 cannot be used on protocol metatype %0",
98102
(Type, DeclName))

include/swift/AST/Expr.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,13 @@ class TypeExpr : public Expr {
10311031
TypeLoc Info;
10321032
TypeExpr(Type Ty);
10331033
public:
1034+
/// Whether this type reference actually references an instance but has been
1035+
/// promoted to a type reference to access an enum element
1036+
///
1037+
/// This is purely transitional and will be removed when referencing enum
1038+
/// elements on instance members becomes an error
1039+
bool IsPromotedInstanceRef = false;
1040+
10341041
// Create a TypeExpr with location information.
10351042
TypeExpr(TypeLoc Ty);
10361043

@@ -1051,7 +1058,8 @@ class TypeExpr : public Expr {
10511058

10521059
/// Return a TypeExpr for a TypeDecl and the specified location.
10531060
static TypeExpr *createForDecl(SourceLoc Loc, TypeDecl *D,
1054-
bool isImplicit);
1061+
bool isImplicit,
1062+
bool isPromotedInstanceRef = false);
10551063
static TypeExpr *createForSpecializedDecl(SourceLoc Loc, TypeDecl *D,
10561064
ArrayRef<TypeRepr*> args,
10571065
SourceRange angleLocs);

include/swift/AST/NameLookup.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ struct UnqualifiedLookupResult {
4141
ValueDecl *Value;
4242

4343
public:
44+
/// Whether this should actually reference an instance but has been promoted
45+
/// to a type reference to access an enum element
46+
///
47+
/// This is purely transitional and will be removed when referencing enum
48+
/// elements on instance members becomes an error
49+
bool IsPromotedInstanceRef = false;
50+
4451
UnqualifiedLookupResult(ValueDecl *value) : Base(nullptr), Value(value) { }
4552

4653
UnqualifiedLookupResult(ValueDecl *base, ValueDecl *value)

lib/AST/Expr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1345,12 +1345,14 @@ Type TypeExpr::getInstanceType() const {
13451345

13461346
/// Return a TypeExpr for a simple identifier and the specified location.
13471347
TypeExpr *TypeExpr::createForDecl(SourceLoc Loc, TypeDecl *Decl,
1348-
bool isImplicit) {
1348+
bool isImplicit,
1349+
bool isPromotedInstanceRef) {
13491350
ASTContext &C = Decl->getASTContext();
13501351
assert(Loc.isValid());
13511352
auto *Repr = new (C) SimpleIdentTypeRepr(Loc, Decl->getName());
13521353
Repr->setValue(Decl);
13531354
auto result = new (C) TypeExpr(TypeLoc(Repr, Type()));
1355+
result->IsPromotedInstanceRef = isPromotedInstanceRef;
13541356
if (isImplicit)
13551357
result->setImplicit();
13561358
return result;

lib/AST/NameLookup.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,11 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC,
556556
if (FD->isStatic() && !isMetatypeType)
557557
continue;
558558
} else if (isa<EnumElementDecl>(Result)) {
559-
Results.push_back(UnqualifiedLookupResult(MetaBaseDecl, Result));
559+
auto lookupRes = UnqualifiedLookupResult(MetaBaseDecl, Result);
560+
if (!BaseDecl->getType()->is<MetatypeType>()) {
561+
lookupRes.IsPromotedInstanceRef = true;
562+
}
563+
Results.push_back(lookupRes);
560564
continue;
561565
}
562566

lib/Sema/CSApply.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2514,6 +2514,24 @@ namespace {
25142514

25152515
public:
25162516
Expr *visitUnresolvedDotExpr(UnresolvedDotExpr *expr) {
2517+
if (auto ty = dyn_cast<TypeExpr>(expr->getBase())) {
2518+
if (ty->IsPromotedInstanceRef) {
2519+
// An enum element was looked up on an instance. Issue a warning
2520+
auto enumMetatype = ty->getType()->castTo<AnyMetatypeType>();
2521+
auto enumType = enumMetatype->getInstanceType()->castTo<EnumType>();
2522+
2523+
SmallString<32> enumTypeName;
2524+
llvm::raw_svector_ostream typeNameStream(enumTypeName);
2525+
typeNameStream << enumType->getDecl()->getName();
2526+
typeNameStream << ".";
2527+
2528+
TypeChecker &tc = cs.getTypeChecker();
2529+
tc.diagnose(expr->getLoc(),
2530+
diag::could_not_use_enum_element_on_instance,
2531+
expr->getName())
2532+
.fixItInsert(expr->getLoc(), typeNameStream.str());
2533+
}
2534+
}
25172535
return applyMemberRefExpr(expr, expr->getBase(), expr->getDotLoc(),
25182536
expr->getNameLoc(), expr->isImplicit());
25192537
}

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) {
556556

557557
ResultValues.clear();
558558
bool AllMemberRefs = true;
559+
bool PromotedInstanceRef = false;
559560
ValueDecl *Base = 0;
560561
for (auto Result : Lookup) {
561562
// Track the base for member declarations.
@@ -566,6 +567,9 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) {
566567
break;
567568
}
568569

570+
if (Result.IsPromotedInstanceRef) {
571+
PromotedInstanceRef = true;
572+
}
569573
Base = Result.Base;
570574
continue;
571575
}
@@ -577,7 +581,8 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) {
577581
if (AllMemberRefs) {
578582
Expr *BaseExpr;
579583
if (auto NTD = dyn_cast<NominalTypeDecl>(Base)) {
580-
BaseExpr = TypeExpr::createForDecl(Loc, NTD, /*implicit=*/true);
584+
BaseExpr = TypeExpr::createForDecl(Loc, NTD, /*implicit=*/true,
585+
PromotedInstanceRef);
581586
} else {
582587
BaseExpr = new (Context) DeclRefExpr(Base, UDRE->getNameLoc(),
583588
/*implicit=*/true);

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,12 @@ namespace {
103103
///
104104
/// \param foundInType The type through which we found the
105105
/// declaration.
106-
void add(ValueDecl *found, ValueDecl *base, Type foundInType) {
106+
///
107+
/// \param promotedInstanceRef true if the lookup result to be added was
108+
/// actually looked up on an instance but promted to a type to look up an
109+
/// enum element
110+
void add(ValueDecl *found, ValueDecl *base, Type foundInType,
111+
bool promotedInstanceRef = false) {
107112
// If we only want types, AST name lookup should not yield anything else.
108113
assert(!Options.contains(NameLookupFlags::OnlyTypes) ||
109114
isa<TypeDecl>(found));
@@ -139,7 +144,7 @@ namespace {
139144
isa<GenericTypeParamDecl>(found) ||
140145
(isa<FuncDecl>(found) && cast<FuncDecl>(found)->isOperator())) {
141146
if (Known.insert({{found, base}, false}).second) {
142-
Result.add({found, base});
147+
Result.add({found, base, promotedInstanceRef});
143148
FoundDecls.push_back(found);
144149
}
145150
return;
@@ -172,7 +177,7 @@ namespace {
172177
// default implementations in protocols.
173178
if (witness && !isa<ProtocolDecl>(witness->getDeclContext())) {
174179
if (Known.insert({{witness, base}, false}).second) {
175-
Result.add({witness, base});
180+
Result.add({witness, base, promotedInstanceRef});
176181
FoundDecls.push_back(witness);
177182
}
178183
}
@@ -229,7 +234,8 @@ LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclName name,
229234
assert(foundInType && "bogus base declaration?");
230235
}
231236

232-
builder.add(found.getValueDecl(), found.getBaseDecl(), foundInType);
237+
builder.add(found.getValueDecl(), found.getBaseDecl(), foundInType,
238+
found.IsPromotedInstanceRef);
233239
}
234240
return result;
235241
}
@@ -566,7 +572,7 @@ void TypeChecker::performTypoCorrection(DeclContext *DC, DeclRefKind refKind,
566572
entries.filterMaxScoreRange(MaxCallEditDistanceFromBestCandidate);
567573

568574
for (auto &entry : entries)
569-
result.add({ entry.Value, nullptr });
575+
result.add({ entry.Value, nullptr, false });
570576
}
571577

572578
static InFlightDiagnostic

lib/Sema/TypeCheckPattern.cpp

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,21 @@ extractEnumElement(TypeChecker &TC, DeclContext *DC, SourceLoc UseLoc,
6464
///
6565
/// If there are no enum elements but there are properties, attempts to map
6666
/// an arbitrary property to an enum element using extractEnumElement.
67+
///
68+
/// \param isPromoted If set to anything but the \c nullptr, this will be set to
69+
/// \c true if the found enum element is referenced as on an instance
70+
/// but the lookup has been promoted to be on the type instead
71+
/// This is purely transitional and will be removed when referencing enum
72+
/// elements on instance members becomes an error
73+
6774
static EnumElementDecl *
6875
filterForEnumElement(TypeChecker &TC, DeclContext *DC, SourceLoc UseLoc,
69-
LookupResult foundElements) {
76+
LookupResult foundElements, bool *isPromoted = nullptr) {
7077
EnumElementDecl *foundElement = nullptr;
7178
VarDecl *foundConstant = nullptr;
7279

73-
for (ValueDecl *e : foundElements) {
80+
for (LookupResult::Result result : foundElements) {
81+
ValueDecl *e = result.Decl;
7482
assert(e);
7583
if (e->isInvalid()) {
7684
continue;
@@ -80,6 +88,9 @@ filterForEnumElement(TypeChecker &TC, DeclContext *DC, SourceLoc UseLoc,
8088
// Ambiguities should be ruled out by parsing.
8189
assert(!foundElement && "ambiguity in enum case name lookup?!");
8290
foundElement = oe;
91+
if (isPromoted != nullptr) {
92+
*isPromoted = result.IsPromotedInstanceRef;
93+
}
8394
continue;
8495
}
8596

@@ -96,13 +107,20 @@ filterForEnumElement(TypeChecker &TC, DeclContext *DC, SourceLoc UseLoc,
96107
}
97108

98109
/// Find an unqualified enum element.
110+
///
111+
/// \param IsPromoted If set to anything but the \c nullptr, this will be set to
112+
/// \c true if the found enum element is referenced as on an instance
113+
/// but the lookup has been promoted to be on the type instead
114+
/// This is purely transitional and will be removed when referencing enum
115+
/// elements on instance members becomes an error
99116
static EnumElementDecl *
100117
lookupUnqualifiedEnumMemberElement(TypeChecker &TC, DeclContext *DC,
101-
Identifier name, SourceLoc UseLoc) {
118+
Identifier name, SourceLoc UseLoc,
119+
bool *IsPromoted = nullptr) {
102120
auto lookupOptions = defaultUnqualifiedLookupOptions;
103121
lookupOptions |= NameLookupFlags::KnownPrivate;
104122
auto lookup = TC.lookupUnqualified(DC, name, SourceLoc(), lookupOptions);
105-
return filterForEnumElement(TC, DC, UseLoc, lookup);
123+
return filterForEnumElement(TC, DC, UseLoc, lookup, IsPromoted);
106124
}
107125

108126
/// Find an enum element in an enum type.
@@ -477,10 +495,22 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
477495
// rdar://20879992 is addressed.
478496
//
479497
// Try looking up an enum element in context.
498+
499+
// Check if the enum element was actually accessed on an instance and was
500+
// promoted to be looked up on a type. If so, provide a warning
501+
bool isPromotedInstance = false;
502+
480503
if (EnumElementDecl *referencedElement
481504
= lookupUnqualifiedEnumMemberElement(TC, DC,
482505
ude->getName().getBaseName(),
483-
ude->getLoc())) {
506+
ude->getLoc(),
507+
&isPromotedInstance)) {
508+
if (isPromotedInstance) {
509+
TC.diagnose(ude->getLoc(), diag::could_not_use_enum_element_on_instance,
510+
ude->getName())
511+
.fixItInsert(ude->getLoc(), ".");
512+
}
513+
484514
auto *enumDecl = referencedElement->getParentEnum();
485515
auto enumTy = enumDecl->getDeclaredTypeInContext();
486516
TypeLoc loc = TypeLoc::withoutLoc(enumTy);
@@ -563,9 +593,21 @@ class ResolvePattern : public ASTVisitor<ResolvePattern,
563593
// If we had a single component, try looking up an enum element in context.
564594
if (auto compId = dyn_cast<ComponentIdentTypeRepr>(repr)) {
565595
// Try looking up an enum element in context.
596+
597+
// Check if the enum element was actually accessed on an instance and was
598+
// promoted to be looked up on a type. If so, provide a warning
599+
bool isPromoted = false;
566600
EnumElementDecl *referencedElement
567601
= lookupUnqualifiedEnumMemberElement(TC, DC, compId->getIdentifier(),
568-
repr->getLoc());
602+
repr->getLoc(), &isPromoted);
603+
604+
if (isPromoted) {
605+
TC.diagnose(compId->getLoc(),
606+
diag::could_not_use_enum_element_on_instance,
607+
compId->getIdentifier())
608+
.fixItInsert(compId->getLoc(), ".");
609+
}
610+
569611

570612
if (!referencedElement)
571613
return nullptr;

lib/Sema/TypeChecker.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ class LookupResult {
6363
/// The base declaration through which we found the declaration.
6464
ValueDecl *Base;
6565

66+
/// Whether this should actually reference an instance but has been promoted
67+
/// to a type reference to access an enum element
68+
///
69+
/// This is purely transitional and will be removed when referencing enum
70+
/// elements on instance members becomes an error
71+
bool IsPromotedInstanceRef;
72+
6673
operator ValueDecl*() const { return Decl; }
6774
ValueDecl *operator->() const { return Decl; }
6875
};

test/Parse/enum.swift

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,3 +433,92 @@ enum RawValueBTest: Double, RawValueB {
433433
enum foo : String {
434434
case bar = nil // expected-error {{cannot convert nil to raw type 'String'}}
435435
}
436+
437+
// Static member lookup from instance methods
438+
439+
struct EmptyStruct {}
440+
441+
enum EnumWithStaticMember {
442+
static let staticVar = EmptyStruct()
443+
444+
func foo() {
445+
let _ = staticVar // expected-error {{static member 'staticVar' cannot be used on instance of type 'EnumWithStaticMember'}}
446+
}
447+
}
448+
449+
// SE-0036:
450+
451+
struct SE0036_Auxiliary {}
452+
453+
enum SE0036 {
454+
case A
455+
case B(SE0036_Auxiliary)
456+
457+
static func staticReference() {
458+
_ = A
459+
_ = SE0036.A
460+
}
461+
462+
func staticReferencInInstanceMethod() {
463+
_ = A // expected-warning {{referencing enum element 'A' as instance member is deprecated and will be removed in Swift 3}} {{9-9=SE0036.}}
464+
_ = SE0036.A
465+
}
466+
467+
static func staticReferencInSwitchInStaticMethod() {
468+
switch SE0036.A {
469+
case A: break
470+
case B(_): break
471+
}
472+
}
473+
474+
func staticReferencInSwitchInInstanceMethod() {
475+
switch self {
476+
case A: break // expected-warning {{referencing enum element 'A' as instance member is deprecated and will be removed in Swift 3}} {{10-10=.}}
477+
case B(_): break // expected-warning {{referencing enum element 'B' as instance member is deprecated and will be removed in Swift 3}} {{10-10=.}}
478+
}
479+
}
480+
481+
func explicitReferencInSwitch() {
482+
switch SE0036.A {
483+
case SE0036.A: break
484+
case SE0036.B(_): break
485+
}
486+
}
487+
488+
func dotReferencInSwitchInInstanceMethod() {
489+
switch self {
490+
case .A: break
491+
case .B(_): break
492+
}
493+
}
494+
495+
static func dotReferencInSwitchInStaticMethod() {
496+
switch SE0036.A {
497+
case .A: break
498+
case .B(_): break
499+
}
500+
}
501+
502+
init() {
503+
self = .A
504+
self = A // expected-warning {{referencing enum element 'A' as instance member is deprecated and will be removed in Swift 3}} {{12-12=SE0036.}}
505+
self = SE0036.A
506+
self = .B(SE0036_Auxiliary())
507+
self = B(SE0036_Auxiliary()) // expected-warning {{referencing enum element 'B' as instance member is deprecated and will be removed in Swift 3}} {{12-12=SE0036.}}
508+
self = SE0036.B(SE0036_Auxiliary())
509+
}
510+
}
511+
512+
enum SE0036_Generic<T> {
513+
case A(x: T)
514+
515+
func foo() {
516+
switch self {
517+
case A(_): break // expected-warning {{referencing enum element 'A' as instance member is deprecated and will be removed in Swift 3}} {{10-10=.}}
518+
}
519+
520+
switch self {
521+
case SE0036_Generic.A(let a): print(a)
522+
}
523+
}
524+
}

0 commit comments

Comments
 (0)