diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index ad3f600d03cf8..900cc418cf6f7 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4022,6 +4022,10 @@ NOTE(redundant_particular_literal_case_here,none, WARNING(non_exhaustive_switch_warn,none, "switch must be exhaustive", ()) +WARNING(override_nsobject_hashvalue,none, + "override of 'NSObject.hashValue' is deprecated; " + "override 'NSObject.hash' to get consistent hashing behavior", ()) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 6666b6d670101..3f1d3772b1f0b 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -6119,6 +6119,18 @@ class DeclChecker : public DeclVisitor { diagnoseOverrideForAvailability(TC, override, base); } + // Overrides of NSObject.hashValue are deprecated; one should override + // NSObject.hash instead. + if (auto baseVar = dyn_cast(base)) { + if (auto classDecl = + baseVar->getDeclContext()->getAsClassOrClassExtensionContext()) { + if (classDecl->getBaseName().userFacingName() == "NSObject" && + baseVar->getBaseName().userFacingName() == "hashValue") { + TC.diagnose(override, diag::override_nsobject_hashvalue); + } + } + } + /// Check attributes associated with the base; some may need to merged with /// or checked against attributes in the overriding declaration. AttributeOverrideChecker attrChecker(TC, base, override); diff --git a/test/ClangImporter/objc_override.swift b/test/ClangImporter/objc_override.swift index cdaee597e9f98..303d3cf1d83ec 100644 --- a/test/ClangImporter/objc_override.swift +++ b/test/ClangImporter/objc_override.swift @@ -108,6 +108,14 @@ class CallbackSubC : CallbackBase { override func perform(optNonescapingHandler: @escaping () -> Void) {} // expected-error {{method does not override any method from its superclass}} } +// +class MyHashableNSObject: NSObject { + override var hashValue: Int { // expected-warning{{override of 'NSObject.hashValue' is deprecated}} + return 0 + } +} + + // FIXME: Remove -verify-ignore-unknown. // :0: error: unexpected note produced: overridden declaration is here // :0: error: unexpected note produced: setter for 'boolProperty' declared here diff --git a/test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift b/test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift index 246836efa5055..d2e84f8061c18 100644 --- a/test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift +++ b/test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift @@ -2,8 +2,14 @@ @_exported import CoreGraphics @_exported import Foundation -public func == (lhs: NSObject, rhs: NSObject) -> Bool { - return lhs.isEqual(rhs) +extension NSObject : Equatable, Hashable { + @objc open var hashValue: Int { + return hash + } + + public static func == (lhs: NSObject, rhs: NSObject) -> Bool { + return lhs.isEqual(rhs) + } } public let NSUTF8StringEncoding: UInt = 8 diff --git a/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift b/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift index 11338a687b0d9..2e13ec565ecbe 100644 --- a/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift +++ b/test/Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift @@ -86,13 +86,3 @@ public func _convertObjCBoolToBool(_ x: ObjCBool) -> Bool { public func ~=(x: NSObject, y: NSObject) -> Bool { return true } - -extension NSObject : Equatable, Hashable { - public var hashValue: Int { - return hash - } -} - -public func == (lhs: NSObject, rhs: NSObject) -> Bool { - return lhs.isEqual(rhs) -}