Skip to content

Commit f138ce0

Browse files
committed
Add specific warning for overriden NSObject.hash(into:)
1 parent a6b8552 commit f138ce0

File tree

4 files changed

+30
-7
lines changed

4 files changed

+30
-7
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5351,6 +5351,10 @@ ERROR(override_nsobject_hashvalue_error,none,
53515351
"'NSObject.hashValue' is not overridable; "
53525352
"did you mean to override 'NSObject.hash'?", ())
53535353

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

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 21 additions & 5 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:) was made non-overridable in
1013+
// Swift 5; 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,11 +1807,11 @@ 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;
18011817
bool baseCanBeObjC = canBeRepresentedInObjC(base);

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/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)