Skip to content

[Diagnostics] Add specific warning for overriden NSObject.hash(into:) #35465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 24, 2021

Conversation

mininny
Copy link
Contributor

@mininny mininny commented Jan 17, 2021

import Foundation
class C: NSObject {
    override func hash(into hasher: inout Hasher) { }
}

The above code produces the following errors:

tmp.swift:3:19: error: overriding non-open instance method outside of its defining module
    override func hash(into hasher: inout Hasher) { }
                  ^
tmp.swift:3:19: error: overriding declarations in extensions is not supported
    override func hash(into hasher: inout Hasher) { }
                  ^
ObjectiveC.NSObject:4:17: note: overridden declaration is here
    public func hash(into hasher: inout Hasher)

which is possibly misleading as the code doesn't have any extensions. This case should be handled together with NSObject.hashValue as invalid overrided interfaces.

I added more detailed information about the error:
'NSObject.hash(into:)' is not overridable; subclasses can customize hashing by overriding the 'hash' property
which correctly guides the user to use NSObject.hash property instead.

I also modified existing override_decl_extension error to produce better diagnostics like this:
method 'foo()' is declared in extension of 'A' and cannot be overriden
and
method 'foo()' declared in 'A' cannot be overriden from extension

Resolves SR-13593.

@mininny mininny force-pushed the objc-override-hash-warning branch from a26fa4a to f8a7f65 Compare January 18, 2021 12:38
Copy link
Member

@lorentey lorentey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. The original hashValue error was added to make it easier for people to update (usually broken) code that depended on it being overridable. There is no such source compatibility issue with NSObject.hash(into:) (it was never overridable), but the special diagnostic is still helpful.

@lorentey
Copy link
Member

tmp.swift:3:19: error: overriding declarations in extensions is not supported
    override func hash(into hasher: inout Hasher) { }

If this is supposed to mean that it isn't possible to override a member that was originally defined in an extension, then it may be worth adjusting the diagnostic text to make that clearer. (Although this seems like an irrelevant/unnecessary diagnostic when we also have the error for overriding a non-open method.)

@mininny
Copy link
Contributor Author

mininny commented Jan 20, 2021

tmp.swift:3:19: error: overriding declarations in extensions is not supported
    override func hash(into hasher: inout Hasher) { }

If this is supposed to mean that it isn't possible to override a member that was originally defined in an extension, then it may be worth adjusting the diagnostic text to make that clearer. (Although this seems like an irrelevant/unnecessary diagnostic when we also have the error for overriding a non-open method.)

I agree that we would benefit from a better message for overriding declarations in extensions is not supported this warning. As suggested in the jira, I'll add another more generic diagnostic to handle these cases. 😄

@mininny mininny force-pushed the objc-override-hash-warning branch 2 times, most recently from 7c59931 to 9ca7764 Compare January 21, 2021 14:31
@@ -2454,8 +2454,9 @@ NOTE(overridden_near_match_here,none,
"potential overridden %0 %1 here",
(DescriptiveDeclKind, DeclName))
ERROR(override_decl_extension,none,
"overriding %select{|non-@objc }0declarations "
"%select{in extensions|from extensions}0 is not supported", (bool, bool))
"%select{|non-@objc}0 method %2 %select{"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have DescriptiveDeclKind that can print out a more accurate descriptor than method when e.g. a constructor is the subject of an override.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I don't believe the @objc-ness of a method is a salient detail here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the method is not @objc, there is an additional diagnosis that suggests the user make the method @objc so that it can be overridden in extensions. I think adding some detail to this diagnosis as well might help. What do you think?

@mininny mininny force-pushed the objc-override-hash-warning branch from 9ca7764 to 5c21cdf Compare January 22, 2021 16:05
@CodaFi
Copy link
Contributor

CodaFi commented Jan 23, 2021

Very nice! ⛵ .

@swift-ci smoke test and merge

@LucianoPAlmeida
Copy link
Contributor

@swift-ci Please smoke test MacOS Platform

1 similar comment
@LucianoPAlmeida
Copy link
Contributor

@swift-ci Please smoke test MacOS Platform

@CodaFi CodaFi merged commit c216370 into swiftlang:main Jan 24, 2021
Copy link
Collaborator

@xwu xwu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always nice to get a more specific warning! Some post-merge comments to consider for a follow-up PR.

"overriding %select{|non-@objc }0declarations "
"%select{in extensions|from extensions}0 is not supported", (bool, bool))
"%select{|non-@objc}0 %2 %3 %select{"
"is declared in extension of %4 and cannot be overriden|"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a typo: the correct spelling is "overridden."

"%select{in extensions|from extensions}0 is not supported", (bool, bool))
"%select{|non-@objc}0 %2 %3 %select{"
"is declared in extension of %4 and cannot be overriden|"
"declared in %4 cannot be overriden from extension}1",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise here, same typo.

@@ -5351,6 +5353,10 @@ ERROR(override_nsobject_hashvalue_error,none,
"'NSObject.hashValue' is not overridable; "
"did you mean to override 'NSObject.hash'?", ())

ERROR(override_nsobject_hash_error,none,
"`NSObject.hash(into:)` is not overridable; "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backticks are not used for code voice in diagnostics; please use single quotes instead (see the other diagnostics).

@@ -5351,6 +5353,10 @@ ERROR(override_nsobject_hashvalue_error,none,
"'NSObject.hashValue' is not overridable; "
"did you mean to override 'NSObject.hash'?", ())

ERROR(override_nsobject_hash_error,none,
"`NSObject.hash(into:)` is not overridable; "
"subclasses can customize hashing by overriding the `hash` property", ())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason that this message is worded differently from the one for overriding hashValue? Typically, diagnostic messages use a concise style, and I think the question posed above is appropriate here ("did you mean to override 'NSObject.hash'?").

@@ -995,11 +1009,13 @@ static void checkOverrideAccessControl(ValueDecl *baseDecl, ValueDecl *decl,
!baseHasOpenAccess &&
baseDecl->getModuleContext() != decl->getModuleContext() &&
!isa<ConstructorDecl>(decl)) {
// NSObject.hashValue was made non-overridable in Swift 5; one should
// override NSObject.hash instead.
// NSObject.hashValue and NSObject.hash(into:) is not overridable;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar: it should read "are not overridable".

@@ -1791,16 +1807,20 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
(isa<ExtensionDecl>(base->getDeclContext()) ||
isa<ExtensionDecl>(override->getDeclContext())) &&
!base->isObjC()) {
// Suppress this diagnostic for overrides of a non-open NSObject.hashValue
// property; these are diagnosed elsewhere. An error message complaining
// Suppress this diagnostic for overrides of a non-open NSObject.Hashable
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar: should read "overrides of non-open NSObject Hashable interfaces" (i.e., delete "a" and remove the dot between "NSObject" and "Hashable".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants