-
Notifications
You must be signed in to change notification settings - Fork 12.4k
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
Fixed crashes when looking up symbols of jsdoc nodes in TS files #57110
Conversation
src/services/findAllReferences.ts
Outdated
@@ -1837,6 +1837,9 @@ export namespace Core { | |||
} | |||
// falls through I guess | |||
case SyntaxKind.Identifier: | |||
if (node.flags & NodeFlags.JSDoc && !isInJSFile(node)) { |
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 need to additionally check if this is in an "any type tag" as those should be ignored whereas nodes in semantic~ jsdoc tags shouldn't (like in @see
, @link
, etc). I couldn't find any utility that would check something like this - I could create such and manually curate the list of those tags in it.
But there is another question: should those references in type-like tags always be ignored? Or should TS try to resolve corresponding TS types? There is always a risk that those won't match but in such a case displaying any
(likely as a result of displaying errorType
) could actually be OK.
An alternative fix for the crashes would be to ignore symbol-less declarations for now but I intentionally didn't go that route. It seemed like a bandaid for a deeper problem and I think handling this at a higher level would be better. I'm just not sure now what's the exact desired behavior.
cc @sandersn
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 you're asking whether find-all-refs should skip the references in @see
et al? Is that right? Ideally the answer is No. Symbol resolution for those tags works outside the normal name resolution code, as I recall, via resolveJSDocMemberName. That function is called from getSymbolAtLocation, the usual entrypoint for public users of the API. You might want to call one of those two functions.
A utility to identify tags with semantics in Typescript would be useful, and I don't think it exists yet. scanner.ts does have a regex, but that's not useful for a parse tree. (and, besides, @deprecated
should be included in the post-parser predicate, but not the scanner one.)
Please add a test either way.
//// data: string[]; | ||
////}; | ||
|
||
// @Filename: node_modules/use-query/package.json |
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 should be other
to match the following index.d.ts
Although I don't know if package.json is really needed for either package.
And they might not need to be in separate packages for the problem to reproduce, right? It seems like this should even work inside a single file, but at least should repro between files of a single package.
// @Filename: node_modules/use-query/package.json | |
// @Filename: node_modules/other/package.json |
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.
yes, it should - the package split shouldn't be required for this to be reproduced at all, it was just faster for me to keep the package structure as-is (like in the original real-world code)
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.
If u feel strongly about it, I can simplify this further or add an extra test case. I need to refresh myself on the whole situation (which I'll do soon since I plan to finish this PR) - but the file split itself might have been important for this (I'm not 100% sure about it right now though)
////interface BottomSheetModalProps { | ||
//// /** | ||
//// * A scrollable node or normal view. | ||
//// * @type null | (({ data: any }?) => any) |
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 null |
the only difference between NoCrash2 and 1? If so, I don't see what value it adds above NoCrash1, especially since the new code doesn't reference Typescript types really.
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 test case exercises a braceless @type
which was somewhat important when I started to reduce this and for some moments I couldn't figure out why the crash went away on certain reduction attempts. So I included that here to be extra sure that it's gonna be fixed in such a scenario too
src/services/findAllReferences.ts
Outdated
@@ -1837,6 +1837,9 @@ export namespace Core { | |||
} | |||
// falls through I guess | |||
case SyntaxKind.Identifier: | |||
if (node.flags & NodeFlags.JSDoc && !isInJSFile(node)) { |
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 you're asking whether find-all-refs should skip the references in @see
et al? Is that right? Ideally the answer is No. Symbol resolution for those tags works outside the normal name resolution code, as I recall, via resolveJSDocMemberName. That function is called from getSymbolAtLocation, the usual entrypoint for public users of the API. You might want to call one of those two functions.
A utility to identify tags with semantics in Typescript would be useful, and I don't think it exists yet. scanner.ts does have a regex, but that's not useful for a parse tree. (and, besides, @deprecated
should be included in the post-parser predicate, but not the scanner one.)
Please add a test either way.
src/compiler/checker.ts
Outdated
@@ -11027,6 +11027,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |||
} | |||
|
|||
if (isParameter(declaration)) { | |||
if (!declaration.symbol) { |
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 decided to include this here because it's just always possible to call checker.getTypeAtLocation
or checker.getTypeAtLocation
and those shouldn't crash or do something overly goofy - so the safest bet is to actually be on the safe side of things here + fine-tune existing call sites to those 2 if it's absolutely necessary to avoid getting errorType
back when it's obvious that it will be returned back
src/services/services.ts
Outdated
((node.parent.kind === SyntaxKind.PropertySignature && (node.parent as PropertySignature).name === node) || | ||
findAncestor(node, (n) => n.kind === SyntaxKind.Parameter)) | ||
) { | ||
// if we'd request type at those locations we'd get `errorType` that displays confusingly as `any` | ||
return false; | ||
} |
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 don't like how specific this is but I concluded that some condition like this is the best solution for #57108 . I'm not 100% sure if I'm missing some other node types that should be ignored here or not though. The worst case scenario quick info will continue to display any
when it shouldn't so I feel it's fine. If you come up with any extra situations that should be handled here I can tweak this and add more tests
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.
Note that it's still good to display any
here - and that's why I have to prevent the type request conditionally instead of just ignoring return values that are error types or smth like that
//// const test3 = { bar: '' }; | ||
//// | ||
//// type SomeObj = { bar: string; }; | ||
//// /** @type {SomeObj/*4*/} */ |
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 TS files this is just ignored, in the sense that it doesn't impact types at all. So technically we could just avoid displaying anything here. I concluded though that it would actually be less useful and that it would be confusing - usually the user expects some feedback in form of a tooltip and not returning any info here could feel like something is off, like it's broken.
Since this isn't used for type-checking there is no guarantee that it "matches" the declaration below - I think it's an acceptable tradeoff. If somebody is writing JSDoc types in TS files then it's their responsibility to make them 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.
cc @jakebailey since we were talking about this a few hours ago
//// /** @type {(a/*8*/: string) => void} */ | ||
//// function test2(a: string) {} |
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 situations like this and more, we could probably try to find the matching symbol and display that. That could be converted to a feature request - I don't think it's important enough for me to work on it though. Using both types of annotations in TS files shouldn't be common anyway.
Can you merge from main before I merge this? We've had a lot of baseline churn recently. |
We'll still not done, forgot one PR is left... |
@sandersn done |
fixes #57094
closes #57108