-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Report multiple overload errors #32092
Conversation
There are still separate errors instead of one + related spans for each sub-error.
In the worst possible way. Now it's sort of ready for creating a DiagnosticMessageTree.
Formatting is wrong, and I might want to format it as non-related spans, but the structure is exactly a tree.
JSX is still broken
The JSX code path stands on its own
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.
Some comments to explain where to start reading.
src/compiler/checker.ts
Outdated
@@ -11688,45 +11697,64 @@ namespace ts { | |||
return !!(type.flags & TypeFlags.Conditional || (type.flags & TypeFlags.Intersection && some((type as IntersectionType).types, isOrHasGenericConditional))); | |||
} | |||
|
|||
function elaborateError(node: Expression | undefined, source: Type, target: Type, relation: Map<RelationComparisonResult>, headMessage: DiagnosticMessage | undefined): boolean { | |||
function elaborateError( |
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.
the bulk of the lines in this PR go to making elaborateError and children handle containingMessageChain
and errorOutputContainer
the way that checkTypeRelatedTo
already does. These two parameters are the mechanism for returning errors rather than immediately logging them, which allows me to build a tree of errors. That actually happens near the bottom of this change in checker.
if (target.symbol && length(target.symbol.declarations)) { | ||
addRelatedInfo(resultObj.error, createDiagnosticForNode( | ||
addRelatedInfo(resultObj.errors[resultObj.errors.length - 1], createDiagnosticForNode( |
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 add related info to the most recently added error. This doesn't display currently.
src/compiler/checker.ts
Outdated
} | ||
|
||
function checkApplicableSignature( | ||
function getSignatureApplicabilityError( |
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.
the return type of this function flips, from true=applicable, false=not
to Diagnostic[]=error(s), undefined=applicable
.
src/compiler/checker.ts
Outdated
@@ -12355,6 +12427,7 @@ namespace ts { | |||
* @param errorNode The suggested node upon which all errors will be reported, if defined. This may or may not be the actual node used. | |||
* @param headMessage If the error chain should be prepended by a head message, then headMessage will be used. | |||
* @param containingMessageChain A chain of errors to prepend any new errors found. | |||
* @param errorOutputContainer Return the diagnostic. Do not log if 'skipLogging' is truthy. |
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.
skipLogging
is new, and prevents immediate logging of the error in checkTypeRelatedTo.
Initial message
Why are we dropping words? At the very least, I think this would be better. - No suitable overload for this call.
+ There is no suitable overload for this call. or maybe even
If you have an identifier or property access, you can write
Count of candidates
Are these always guaranteed to have an order? Do they have some relevant ordering within source code? Maybe it makes more sense to elaborate on the original error messages with something like.
Falling back to the last overload
Which overload is that? Give me a related span to point me in the direction of the overload if it wasn't applicable, or tell me the signature of the overload. Arity filtering
We need to be wary of how this affects editor scenarios. In editor scenarios, we used to check whether a call was still in the process of being typed to make decisions on filtering out higher-arity candidates. That may still be the case. Display as bare messages
I think this might actually be a blocker and we need to find a way to surface this for VS users. Duplicates on arrays/object literals
I don't think that's an acceptable experience and I would really like us not to look stupid here. In these cases I think I as a user would prefer that when no overloads apply, TypeScript should pick an overload, link me to it in a related span, and report as we used to. |
I should also mention, JSX cases seem to need a bit of improvement, but we chatted about that offline. Apart from that, I'm really excited about this work! |
cc @amcasey |
After some in-person discussion:
|
After talking to @amcasey in person, it sounds like pre-flattening the error to text will work for VS (and any other editor that doesn't want to support related spans). I'm going to create a prototype and make sure the experience is OK. |
Fun fact: since this PR changes error reporting for all overloads, I can see that we only have 44 / 13,000 tests with errors on overloaded functions. |
After talking to @amcasey: the most likely solution is to change the diagnostic protocol to start flattening related spans text into |
If there are multiple diagnostics per signature, choose the signature with the fewer diagnostics to report. If there are more than one with the minimum, choose the latest in the overload set.
Could always flatten the related spans at the protocol layer. So instead of
you get
they'll display in-order in the editor, after all. |
@weswigham my plan is to do exactly that, but start putting the flattened version in |
Isn't that going to regress our UI in every non-VS editor (until they all update in a way that to them is just pointless churn)? Seems like we should be able to deal with it differently, like by adding a VS-specific command or, alternatively, by flattening the text before display, in VS... |
@weswigham It's not just VS that's affected - it's any version of any editor that consumes the |
Adding things to VS doesn't fix 3.6 for users of old VS. I believe most of our other users update their editors (or at least their typescript plugins) regularly and that this is not true of VS. I'm going to discuss our options with the VS and VS Code people at our next meeting, so I'll have a better idea after that. For editors that don't yet support related spans, this will also be an improvement. That includes vim and sublime, as far as I can tell from their respective plugins' source. Edit: atom and eclipse don't support related spans either. So it's just VS Code and emacs. Oh, maybe webstorm too? I don't have webstorm installed, and I think it's closed source, so I don't know whether it handles related spans. |
I doubt that can be made as a blanket statement - most of the information in related spans is a related spans precisely because it's too much information for the original error (and is of limited use without a linkified span - I'm going to confidently state that we would never consider such a backwards incompatible reversal of protocol behavior (since that would cause everyone currently displaying related spans to display spans once badly in the message with no linkage and a second time correctly in their own UI) for anything other than the pressure applied by VS... And even with that pressure, this is a huge UI visible break compared to most changes we make, and should not be made lightly. Inlining everything into |
My bar is that the experience has to remain reasonable for any editor currently consuming either just |
Also: why are these overload elaborations related spans anyway? They're not associated with a new location or anything, they're just a lot of text - either they should be displayed, and therefore be inlined in the |
I disagree. For the UIs we care most about -- VS Code and the terminal -- related spans display every time the error does. Probably the other editors should behave the same.
I think the error should be a tree structure internally, and related spans are the existing method tree structure, so that's what I used. We could create two kinds of tree structure, one for errors and one for related spans. But the value of that is low if we always display related spans.
The declaration of the overload should be the location. Looks like that's wrong right now, though. |
OK, I switched from related spans to a tree of elaborations. @weswigham mind taking a look? |
src/compiler/checker.ts
Outdated
const related = map( | ||
max > 1 ? allDiagnostics[minIndex] : flatten(allDiagnostics), | ||
d => typeof d.messageText === "string" ? (d as DiagnosticMessageChain) : d.messageText); | ||
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, chainDiagnosticMessages(related, Diagnostics.No_overload_matches_this_call))); |
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.
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, chainDiagnosticMessages(related, Diagnostics.No_overload_matches_this_call))); | |
const diags = max > 1 ? allDiagnostics[minIndex] : flatten(allDiagnostics); | |
const related = map( | |
diags, | |
d => typeof d.messageText === "string" ? (d as DiagnosticMessageChain) : d.messageText); | |
diagnostics.add(createDiagnosticForNodeFromMessageChain(node, chainDiagnosticMessages(related, Diagnostics.No_overload_matches_this_call), reduceLeft(diags, (acc, d) => acc.concat(d.relatedInformation || []), [] as DiagnosticRelatedInformation[]))); |
?
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.
DiagnosticRelatedInformation doesn't have a property named relatedInformation, just Diagnostic. I'll see whether a cast is safe 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.
The cast in that suggestion is on an empty array, just so it's not inferred as a never[]
- or were you referencing something else?
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.
In acc.concat(d.relatedInformation || [])
: d: DiagnosticRelatedInformation
has no property relatedInformation
. Only its subtype Diagnostic
does.
It's so amazing how things that are incredibly obvious to humans can be difficult (or even undecidable!) for computers. |
Typescript currently reports assignability errors on the last overload for functions with multiple overloads, and it doesn't tell you that the function has more than one overload. Together, these two problems make fixing type errors on functions with overloads extremely frustrating.
This PR fixes the second problem for all overloaded functions, and addresses the first problem for overloaded functions with 3 or fewer overloads, which is 99.32% of them [1].
The resulting error output looks like this (from emacs; the formatting is similar in VS Code and the terminal [3]):
If there are more than 3 overloads, there are no related spans. Instead you see a single elaboration, which states that it applies to the last overload:
Limitations and quirks:
I don't think any of these limitations are worth fixing in this PR since it hits a good balance of effort compared to value. Since we do some filtering already, as in (2), it is a good idea to play with a bit more of it at some point. For example, I believe the surprising number of 100+ signature overload sets arise from the
on(kind: "string")
pattern, which has to be repeated in subclasses. This is one case where filtering by type, probably literal type only, would help a lot. But it's only a tiny percentage of functions.Fixes #27249
[1]
[2]
[3] Indentation of elaborations of related spans is incorrect on the terminal: #32091