-
Notifications
You must be signed in to change notification settings - Fork 10.5k
SR-2484: Improve diagnostic for incorrectly called private init #5237
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
Conversation
/cc @rudkx |
@jrose-apple /cc Jordan in case he wants to take a look. |
decl->getName(), decl->getFormalAccess()); | ||
} | ||
|
||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look right. The return is inside the loop, which means you only diagnose at most one candidate. What if there are two inaccessible candidates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just followed the common theme which can be found throughout diagnostics code - once a single problem is diagnosed is returns right away. But i can change this code in a way it where would try to diagnose as many problems with access as possible and return only then. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the problem is "there are no available initializers" and the diagnosis would then include all of the possible candidates. That more closely matches what CalleeCandidateInfo::diagnoseSimpleErrors does, although I'll admit the current code is not set up as well to do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like diagnoseSimpleErrors includes candidates which aren't the intended callees (e.g. if you call c.foo(a:) and there is a c.foo() and a c.foo(a:) and they are both private, it lists both of those). If only c.foo() is accessible and you call c.foo(a:) we say you're calling a function that takes no arguments.
I think we could do better for both of these, but this seems like a step in the right direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jrose-apple Do you think this still needs additional work before being committed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:-/ I really don't like it. It seems like the kind of thing where a user would say "but I wasn't trying to call that initializer anyway; I was trying to call [some other one that would have been a better match]."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to clarify, do you think it's a good idea to include all of the possible inaccessible candidates? Or you don't think this is correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, just including all possible inaccessible candidates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All right, I'll do that then :)
!baseObjTy->is<MetatypeType>()) { | ||
if (auto ctorRef = dyn_cast<UnresolvedDotExpr>(expr)) { | ||
if (isa<SuperRefExpr>(ctorRef->getBase())) { | ||
diagnose(anchor->getLoc(), diag::super_initializer_not_in_initializer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a test for this diagnostic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pre-existing diagnostic which just got moved, there is a test for it already in
test/Constraints/super_constructor.swift and test/Parse/super.swift
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, got it.
|
||
// Suggest inserting a call to 'type(of:)' to construct another object | ||
// of the same dynamic type. | ||
SourceRange fixItRng = ctorRef->getNameLoc().getSourceRange(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced this is a good idea. Most class initializers can't be called on a dynamic type because they're not required
, and most protocols don't have initializer requirements at all. Additionally, you've changed the lookup to search on the type rather than the instance all the time, so if we get here there might really be no viable initializers. Or am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, I've just move it, this was pre-existing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
decl->getName(), decl->getFormalAccess()); | ||
} | ||
|
||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like diagnoseSimpleErrors includes candidates which aren't the intended callees (e.g. if you call c.foo(a:) and there is a c.foo() and a c.foo(a:) and they are both private, it lists both of those). If only c.foo() is accessible and you call c.foo(a:) we say you're calling a function that takes no arguments.
I think we could do better for both of these, but this seems like a step in the right direction.
@swift-ci Smoke test. |
@jrose-apple I've made a change to diagnose all of the inaccessible candidates found. Please take a look and let me know! |
Ah, this isn't quite what I meant; this will produce N diagnostics at the same site with the same message (if there are multiple overloads with the same argument labels). I think something like what diagnoseSimpleErrors is doing is better: one error message, with notes for all the candidates (even if we don't display those notes very well in Xcode right now). That does have a downside of picking only one name for the primary message if there are multiple names, though. Thoughts? |
@jrose-apple ah, I've totally misunderstood what you meant, I'll do that momentarily! |
@jrose-apple Done, it's going to emit 'declared at' messages now for all of the candidates found if there was one with incorrect access. After looking at |
|
||
for (auto &candidate : result.ViableCandidates) { | ||
auto decl = candidate.getDecl(); | ||
CS->TC.diagnose(decl, diag::decl_declared_here, decl->getName()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a little funny here that we were checking for non-decls before and now we're going to skip that check.
Also, nitpick: please use getFullName
for these notes, which will help distinguish overloads. (Plain getName
is good for the main error because that will drop the argument labels, which is important if they're different.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, it looks like it's a bug in the code in diagnoseSimpleErrors
as well, I'll change here and in diagnoseSimpleErrors
as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, I was wondering if diagnoseSimpleErrors was in a context where it already knew everything was a decl, but that's probably silly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, it was only checking that for candidates[0], I'm running tests right now locally, will push in a few.
@jrose-apple Done, changed both new code and |
Build failed |
Once more for the force push. @swift-ci Please test |
@jrose-apple Thanks for looking at this! |
Fixes situation where lookup of the constructors is attempted on non MetatypeType base.
Minor refactoring is done around how discovered structural members are treated,
which reduced amount of condition logic.
Resolves SR-2484.