Skip to content

Commit c216370

Browse files
authored
Merge pull request #35465 from mininny/objc-override-hash-warning
[Diagnostics] Add specific warning for overriden `NSObject.hash(into:)`
2 parents af7f1e6 + 5c21cdf commit c216370

File tree

9 files changed

+52
-23
lines changed

9 files changed

+52
-23
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,8 +2454,10 @@ NOTE(overridden_near_match_here,none,
24542454
"potential overridden %0 %1 here",
24552455
(DescriptiveDeclKind, DeclName))
24562456
ERROR(override_decl_extension,none,
2457-
"overriding %select{|non-@objc }0declarations "
2458-
"%select{in extensions|from extensions}0 is not supported", (bool, bool))
2457+
"%select{|non-@objc}0 %2 %3 %select{"
2458+
"is declared in extension of %4 and cannot be overriden|"
2459+
"declared in %4 cannot be overriden from extension}1",
2460+
(bool, bool, DescriptiveDeclKind, DeclName, DeclName))
24592461
NOTE(overridden_here,none,
24602462
"overridden declaration is here", ())
24612463
NOTE(overridden_here_can_be_objc,none,
@@ -5351,6 +5353,10 @@ ERROR(override_nsobject_hashvalue_error,none,
53515353
"'NSObject.hashValue' is not overridable; "
53525354
"did you mean to override 'NSObject.hash'?", ())
53535355

5356+
ERROR(override_nsobject_hash_error,none,
5357+
"`NSObject.hash(into:)` is not overridable; "
5358+
"subclasses can customize hashing by overriding the `hash` property", ())
5359+
53545360
WARNING(hashvalue_implementation,none,
53555361
"'Hashable.hashValue' is deprecated as a protocol requirement; "
53565362
"conform type %0 to 'Hashable' by implementing 'hash(into:)' instead",

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,20 @@ static bool isNSObjectHashValue(ValueDecl *baseDecl) {
734734
return false;
735735
}
736736

737+
/// Returns true if the given declaration is for the `NSObject.hash(into:)`
738+
/// function.
739+
static bool isNSObjectHashMethod(ValueDecl *baseDecl) {
740+
auto baseFunc = dyn_cast<FuncDecl>(baseDecl);
741+
if (!baseFunc)
742+
return false;
743+
744+
if (auto classDecl = baseFunc->getDeclContext()->getSelfClassDecl()) {
745+
ASTContext &ctx = baseDecl->getASTContext();
746+
return baseFunc->getBaseName() == ctx.Id_hash && classDecl->isNSObject();
747+
}
748+
return false;
749+
}
750+
737751
namespace {
738752
/// Class that handles the checking of a particular declaration against
739753
/// superclass entities that it could override.
@@ -995,11 +1009,13 @@ static void checkOverrideAccessControl(ValueDecl *baseDecl, ValueDecl *decl,
9951009
!baseHasOpenAccess &&
9961010
baseDecl->getModuleContext() != decl->getModuleContext() &&
9971011
!isa<ConstructorDecl>(decl)) {
998-
// NSObject.hashValue was made non-overridable in Swift 5; one should
999-
// override NSObject.hash instead.
1012+
// NSObject.hashValue and NSObject.hash(into:) is not overridable;
1013+
// one should override NSObject.hash instead.
10001014
if (isNSObjectHashValue(baseDecl)) {
10011015
diags.diagnose(decl, diag::override_nsobject_hashvalue_error)
10021016
.fixItReplace(SourceRange(decl->getNameLoc()), "hash");
1017+
} else if (isNSObjectHashMethod(baseDecl)) {
1018+
diags.diagnose(decl, diag::override_nsobject_hash_error);
10031019
} else {
10041020
diags.diagnose(decl, diag::override_of_non_open,
10051021
decl->getDescriptiveKind());
@@ -1791,16 +1807,20 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
17911807
(isa<ExtensionDecl>(base->getDeclContext()) ||
17921808
isa<ExtensionDecl>(override->getDeclContext())) &&
17931809
!base->isObjC()) {
1794-
// Suppress this diagnostic for overrides of a non-open NSObject.hashValue
1795-
// property; these are diagnosed elsewhere. An error message complaining
1810+
// Suppress this diagnostic for overrides of a non-open NSObject.Hashable
1811+
// interfaces; these are diagnosed elsewhere. An error message complaining
17961812
// about extensions would be misleading in this case; the correct fix is to
17971813
// override NSObject.hash instead.
1798-
if (isNSObjectHashValue(base) &&
1814+
if ((isNSObjectHashValue(base) || isNSObjectHashMethod(base)) &&
17991815
!base->hasOpenAccess(override->getDeclContext()))
18001816
return true;
1817+
18011818
bool baseCanBeObjC = canBeRepresentedInObjC(base);
1819+
auto nominal = base->getDeclContext()->getSelfNominalTypeDecl();
18021820
diags.diagnose(override, diag::override_decl_extension, baseCanBeObjC,
1803-
!isa<ExtensionDecl>(base->getDeclContext()));
1821+
!isa<ExtensionDecl>(base->getDeclContext()),
1822+
override->getDescriptiveKind(), override->getName(),
1823+
nominal->getName());
18041824
// If the base and the override come from the same module, try to fix
18051825
// the base declaration. Otherwise we can wind up diagnosing into e.g. the
18061826
// SDK overlay modules.

test/ClangImporter/objc-cross-module-override.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import ImageInitializers
1212

1313
final class MyImage : Image {
14-
// CHECK: overriding non-@objc declarations from extensions is not supported
14+
// CHECK: non-@objc initializer 'init(imageLiteralResourceName:)' is declared in extension of 'Image' and cannot be overriden
1515
// Make sure we aren't emitting a fixit into the extant module...
1616
// CHECK-NOT: add '@objc' to make this declaration overridable
1717
// CHECK: ImageInitializers.Image:{{.*}}: note: overridden declaration is here

test/ClangImporter/objc_override.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ class MyHashableNSObject: NSObject {
114114
// expected-error@-1 {{'NSObject.hashValue' is not overridable; did you mean to override 'NSObject.hash'?}}
115115
return 0
116116
}
117+
118+
override func hash(into hasher: inout Hasher) {
119+
// expected-error@-1 {{`NSObject.hash(into:)` is not overridable; subclasses can customize hashing by overriding the `hash` property}}
120+
}
117121
}
118122

119123
// rdar://problem/47557376

test/attr/attr_objc_override.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ class B : A {
2929
get { return self } // expected-error{{subscript getter with Objective-C selector 'objectForKeyedSubscript:' conflicts with subscript getter from superclass 'A'}}
3030
}
3131

32-
override func foo() { } // expected-error{{overriding non-@objc declarations from extensions is not supported}}
32+
override func foo() { } // expected-error{{non-@objc instance method 'foo()' is declared in extension of 'A' and cannot be overriden}}
3333

34-
override func wibble(_: SwiftStruct) { } // expected-error{{overriding declarations in extensions is not supported}}
34+
override func wibble(_: SwiftStruct) { } // expected-error{{instance method 'wibble' is declared in extension of 'A' and cannot be overriden}}
3535
}
3636

3737
extension B {
38-
override func bar() { } // expected-error{{overriding non-@objc declarations from extensions is not supported}}
38+
override func bar() { } // expected-error{{non-@objc instance method 'bar()' declared in 'A' cannot be overriden from extension}}
3939
}

test/decl/func/static_func.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ class C_Derived : C {
8383
override class func f2() {}
8484
class override func f3() {}
8585

86-
override class func ef2() {} // expected-error {{not supported}}
87-
class override func ef3() {} // expected-error {{not supported}}
86+
override class func ef2() {} // expected-error {{cannot be overriden}}
87+
class override func ef3() {} // expected-error {{cannot be overriden}}
8888
override static func f7() {} // expected-error {{static method overrides a 'final' class method}}
8989
}
9090

@@ -98,11 +98,11 @@ class C_Derived3 : C {
9898
}
9999

100100
extension C_Derived {
101-
override class func f4() {} // expected-error {{not supported}}
102-
class override func f5() {} // expected-error {{not supported}}
101+
override class func f4() {} // expected-error {{cannot be overriden}}
102+
class override func f5() {} // expected-error {{cannot be overriden}}
103103

104-
override class func ef4() {} // expected-error {{not supported}}
105-
class override func ef5() {} // expected-error {{not supported}}
104+
override class func ef4() {} // expected-error {{cannot be overriden}}
105+
class override func ef5() {} // expected-error {{cannot be overriden}}
106106
}
107107

108108
protocol P { // expected-note{{extended type declared here}}

test/decl/inherit/override.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ extension B {
4646
override func f3D() { }
4747
override func f4D() -> ObjCClassB { }
4848

49-
func f5() { } // expected-error{{overridi}}
50-
func f6() -> A { } // expected-error{{overriding declarations in extensions is not supported}}
49+
func f5() { } // expected-error{{overri}}
50+
func f6() -> A { } // expected-error{{instance method 'f6()' is declared in extension of 'A' and cannot be overriden}}
5151

5252
@objc override func f7() { }
5353
@objc override func f8() -> ObjCClassA { }

test/decl/init/basic_init.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class InitSubclass: InitClass {}
2525
// expected-note@-1{{'init(baz:)' previously overridden here}}
2626
// expected-note@-2{{'init(bar:)' previously overridden here}}
2727
extension InitSubclass {
28-
convenience init(arg: Bool) {} // expected-error{{overriding non-@objc declarations from extensions is not supported}}
28+
convenience init(arg: Bool) {} // expected-error{{non-@objc initializer 'init(arg:)' declared in 'InitClass' cannot be overriden from extension}}
2929
convenience override init(baz: Int) {}
3030
// expected-error@-1 {{'init(baz:)' has already been overridden}}
3131
// expected-error@-2 {{cannot override a non-dynamic class declaration from an extension}}

test/stdlib/NSObject-hashing.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ class Foo: NSObject {
1212
}
1313

1414
override func hash(into hasher: inout Hasher) {
15-
// expected-error@-1 {{overriding non-open instance method outside of its defining module}}
16-
// expected-error@-2 {{overriding declarations in extensions is not supported}}
15+
// expected-error@-1 {{`NSObject.hash(into:)` is not overridable; subclasses can customize hashing by overriding the `hash` property}}
1716
}
1817
}

0 commit comments

Comments
 (0)