-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Better error message for accidental calls to get-accessors #37800
Better error message for accidental calls to get-accessors #37800
Conversation
@typescript-bot pack this |
Heya @DanielRosenwasser, I've started to run the tarball bundle task on this PR at f92edb7. You can monitor the build here. |
src/compiler/diagnosticMessages.json
Outdated
@@ -4384,6 +4384,10 @@ | |||
"category": "Error", | |||
"code": 6231 | |||
}, | |||
"'{0}' is a 'get' accessor; did you mean to use it without '()'?": { |
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.
"'{0}' is a 'get' accessor; did you mean to use it without '()'?": { | |
"'{0}' is declared as a 'get' accessor and does not need to be called.": { |
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.
Actually, trying this out in an editor, I could see either one being good. It's up to you.
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 could see the wording that mentions ()
being better if we could actually provide a code fix that deletes the (). I had some questions about that in my PR description, would you happen to have experience with code fixes and could help me figure out how to implement it cleanly?
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.
With or without the codefix, I like the ()
message better since it shows what's wrong instead of naming it in terms that not everyone understands immediately.
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.
re codefixes: There are some simple ones that you can basically copy, then you just have to learn how to extract the property access from the call, which should also be easy.
However, I'd rather just take the error alone and follow up with the codefix.
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.
Actually, as a replacement for 'this expression is not callable', we should keep some elements of that:
"'{0}' is a 'get' accessor; did you mean to use it without '()'?": { | |
"This expression is not callable because it is a 'get' accessor; did you mean to use it without '()'?": { |
(You already have an error span so I don't think you need to reproduce the expression in the error -- long expressions result in too-long errors.)
Hey @DanielRosenwasser, I've packed this into an installable tgz. You can install it for testing by referencing it in your
and then running There is also a playground for this build. |
f92edb7
to
d00a5c9
Compare
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 error should replace "This expression is not callable", or at least be added as an elaboration.
src/compiler/diagnosticMessages.json
Outdated
@@ -4384,6 +4384,10 @@ | |||
"category": "Error", | |||
"code": 6231 | |||
}, | |||
"'{0}' is a 'get' accessor; did you mean to use it without '()'?": { |
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.
With or without the codefix, I like the ()
message better since it shows what's wrong instead of naming it in terms that not everyone understands immediately.
src/compiler/diagnosticMessages.json
Outdated
@@ -4384,6 +4384,10 @@ | |||
"category": "Error", | |||
"code": 6231 | |||
}, | |||
"'{0}' is a 'get' accessor; did you mean to use it without '()'?": { |
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.
re codefixes: There are some simple ones that you can basically copy, then you just have to learn how to extract the property access from the call, which should also be easy.
However, I'd rather just take the error alone and follow up with the codefix.
function test24554(x: Test24554) { | ||
return x.property(); | ||
~~~~~~~~ | ||
!!! error TS2349: This expression is not callable. |
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 would much rather replace this message instead of tacking on related info.
If it's too hard to replace the message, it should be an elaboration, not related info. Elaborations provide additional messages; related info is designed to point to a different location.
src/compiler/diagnosticMessages.json
Outdated
@@ -4384,6 +4384,10 @@ | |||
"category": "Error", | |||
"code": 6231 | |||
}, | |||
"'{0}' is a 'get' accessor; did you mean to use it without '()'?": { |
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.
Actually, as a replacement for 'this expression is not callable', we should keep some elements of that:
"'{0}' is a 'get' accessor; did you mean to use it without '()'?": { | |
"This expression is not callable because it is a 'get' accessor; did you mean to use it without '()'?": { |
(You already have an error span so I don't think you need to reproduce the expression in the error -- long expressions result in too-long errors.)
|
(Sorry, I didn't read your description until just now -- you asked about every single one of the aspects I commented on.) |
Thanks for the feedback!
That partially answers my question about the best way to make the code fix. But is it really desirable to replace this error message? I guess I'm worried about the rare cases where the |
I see, I guess starting with "This expression is not callable because…" alleviates my concern. And I suppose we can still keep the "Type {0} has no call signatures" sub-error. |
aa5838c
to
cd15560
Compare
I added a code fix, but to make it easier I modified the parser to save the open/close paren tokens in CallExpression. Is that alright?
Edit 2: going to remove that from this PR and do it separtely. |
a4322d1
to
d00f2b5
Compare
Alright, got the code fix working, except that I had to omit the |
Hi @sandersn just a friendly ping on this PR! No rush but I don't want it to languish for too long either :) |
!!! error TS2349: This expression is not callable. | ||
!!! error TS2349: Type 'Number' has no call signatures. | ||
!!! message TS6232: This expression is not callable because it is a 'get' accessor. Did you mean to use it without '()'? | ||
!!! related TS2728 tests/cases/conformance/classes/members/classTypes/instancePropertyInClassType.ts:4:13: 'y' is declared here. |
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 valuable. If it is, it should really be on the base "This expression is not callable." error as well. @DanielRosenwasser can you decide on this? I trust your opinion on related spans.
src/compiler/checker.ts
Outdated
// Diagnose get accessors incorrectly called as functions | ||
const { resolvedSymbol } = getNodeLinks(errorTarget); | ||
if (resolvedSymbol && resolvedSymbol.flags & SymbolFlags.GetAccessor) { | ||
diagnostic = createDiagnosticForNode(errorTarget, Diagnostics.This_expression_is_not_callable_because_it_is_a_get_accessor_Did_you_mean_to_use_it_without); |
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 change should be in invocationErrorDetails, very close to the base "Type_0_has_no_call_signatures", instead of out here in invocationError.
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.
invocationErrorDetails currently doesn't have enough information (access to the node/symbol) to produce the new error. I could rethink its signature, but that seemed more invasive than just putting it here. I'll take another look later if you think it's better that way!
Alright, I've moved the changes into |
Ping @sandersn 🙌 (Not really in a hurry, I'm sure you are quite busy — just want to avoid falling through the cracks!) |
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.
Sorry, noticed one more thing. (This might be the fault of my merge.)
src/compiler/diagnosticMessages.json
Outdated
@@ -4416,6 +4416,10 @@ | |||
"category": "Error", | |||
"code": 6233 | |||
}, | |||
"This expression is not callable because it is a 'get' accessor. Did you mean to use it without '()'?": { | |||
"category": "Message", |
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.
Should be an Error, not a Message.
Travis is all green when I click through to it, so I'm going to go ahead and merge. |
Great, thanks! I will send out another PR for the code fix in the near future. |
Fixes #24554 by adding a new Message when
resolveCallExpression
fails to find any call signatures, if thenode.expression
references aGetAccessor
. Example test case with new message:This is my first attempt to contribute to TS, so I'm eager for any feedback! Specifically here are some questions I'd like someone familiar with the code to answer:
resolveCallExpression
a good place to add this? The discussion on Clarify error message when an accessor is invoked. #27897 mentioned changingcheckPropertyAccessExpression
, but that seemed hard to me because the PropertyAccessExpression isn't really aware of its parent CallExpression (well, I guess it might be able to accessparent
, but that doesn't seem great—and there are some other diagnostics in this function already).getNodeLinks(node.expression).resolvedSymbol
? That was the easiest way I found to reach the function and determine whether it's a GetAccessor, but it also seems to implicitly depend on something else having resolved the symbol first.relatedInformation
the right place to attach such a message? I'm a bit confused about the diagnostic "chain" and how relatedInfo fits in, but this seems to work.()
as well. I started down that path, but it seems like fixes can't be triggered onrelatedInformation
, just on errors (I was able to create a new fix that got triggered onThis_expression_is_not_callable
errors, but it didn't trigger if I register it only for my new diagnostic message.) And since the fix'sgetCodeActions(context)
function doesn't receive the diagnostic itself, just the code, it doesn't seem feasible to register it for allThis_expression_is_not_callable
errors and look at their relatedInformation to see if my newdid you mean to use it without '()'?
is attached. One approach I could consider is making it a separate error, but then it wouldn't be attached to the existing "not callable" error. Any advice here?