-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Omnibus fixes for telemetry-sourced crashes #20545
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
... we should turn on strict null checks 😠 |
@@ -191,8 +200,9 @@ namespace ts.codefix { | |||
sourceFile, | |||
token.getStart(sourceFile)); | |||
|
|||
Debug.assert(!!references, "Found no references!"); | |||
Debug.assert(references.length === 1, "Found more references than expected"); | |||
if (!references || references.length !== 1) { |
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.
Is the references.length !== 1
required? Seems weird to expect an array of exactly 1 object. If there are 2, is it better to return an empty array than ignore the 2nd unexpected element? Is it guaranteed/expected to always return 1 element in this context?
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 variable is probably badly named. references
is not an array of references to a symbol, it's (effectively) an array of arrays; each element of the outer array refers to a different symbol (as could occur if you had a type and a value with the same name used in a position where either meaning might be valid, such as an import
statement), and each subelement is the references to that symbol. In this case it would be very unusual (IOW we have no idea what's going on or what the right thing to do is) if we found more than one symbol matching a certain property.
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.
Is it better to remove the asserts or leave them? There is minimal user impact either way (though throwing errors probably isn't good for perf), since we merely end up swallowing the error and writing it as part of the failure response.
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.
Nice! Worth porting to 2.6? Were most of the reports coming back from VS usage?
Looking at CI failures. Some of the fixes were for crashes I found when trying to reconstruct repros for those stacks. I believe VS is the only place we get these stack traces from - @aozgaa ? |
The stack traces are from vscode only. |
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.
Good fixes, just want to clarify the desired failure scenarios.
@@ -71,6 +71,10 @@ namespace ts.codefix { | |||
} | |||
|
|||
const containingFunction = getContainingFunction(token); | |||
if (containingFunction === undefined) { | |||
// Possible in certain syntax error cases |
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.
Is a syntax error synonymous with a parse error?
@@ -3951,7 +3951,12 @@ namespace ts { | |||
writePunctuation(writer, SyntaxKind.CloseBracketToken); | |||
writePunctuation(writer, SyntaxKind.ColonToken); | |||
writeSpace(writer); | |||
buildTypeDisplay(info.type, writer, enclosingDeclaration, globalFlags, symbolStack); | |||
if (info.type) { | |||
buildTypeDisplay(info.type, writer, enclosingDeclaration, globalFlags, symbolStack); |
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.
FYI, I believe this will be deprecated by #18860. Good catch!
if (containingFunction.parameters.length !== types.length) { | ||
return undefined; | ||
} | ||
|
||
const textChanges = arrayFrom(mapDefinedIterator(zipToIterator(containingFunction.parameters, types), ([parameter, type]) => |
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.
Now that we are doing the length
check above, can we unroll this to an index-based for-loop? the intermediate iterator and maps are unnecessary
@@ -191,8 +200,9 @@ namespace ts.codefix { | |||
sourceFile, | |||
token.getStart(sourceFile)); | |||
|
|||
Debug.assert(!!references, "Found no references!"); | |||
Debug.assert(references.length === 1, "Found more references than expected"); | |||
if (!references || references.length !== 1) { |
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.
Is it better to remove the asserts or leave them? There is minimal user impact either way (though throwing errors probably isn't good for perf), since we merely end up swallowing the error and writing it as part of the failure response.
@@ -496,7 +496,7 @@ namespace ts.SymbolDisplay { | |||
addNewLineIfDisplayPartsExist(); | |||
if (symbolKind) { | |||
pushTypePart(symbolKind); | |||
if (!some(symbol.declarations, d => isArrowFunction(d) || (isFunctionExpression(d) || isClassExpression(d)) && !d.name)) { | |||
if (symbol && !some(symbol.declarations, d => isArrowFunction(d) || (isFunctionExpression(d) || isClassExpression(d)) && !d.name)) { |
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.
So if symbol === undefined
we will just leave the name blank? Is that desirable behavior? Should we perhaps instead fail to build the display completely?
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 code path builds either anonymous or non-anonymous declarations (e.g. function (x)
or function y(x)
), so it's already regularly producing non-named symbol displays. In the case where the originating type doesn't have a symbol, it's sort of de facto anonymous anyway so it seems like this won't ever produce any particularly weird output.
I see this as a correctness thing - in an ideal world, a failing assert in TS Server would pop an assert dialog in VS in debug builds. Since we now know that these conditions (e.g. |
Omnibus fixes for telemetry-sourced crashes
Fixes #20523 - occurs when the number of references found didn't match the function arity
Fixes #20520 - "impossible" case as described by asserts actually does occur, but can be handled safely
Fixes #20527, #20474, #20472 - containing function may be undefined in the presence of syntax errors
Fixes #20542 - symbol writer may sometimes encounter index signature with no types
Fixes #20475 - symbol writer may sometimes encounter types with no symbol
Recommend reviewing commit-by-commit. Some of these crashes are from telemetry and we've been unable to reconstruct a repro for them.