Fixed crashes when looking up symbols of jsdoc nodes in TS files (#57110)

Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
Mateusz Burzyński 2024-04-05 00:45:35 +02:00 committed by GitHub
parent f2bd592838
commit 69e7e57b15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 362 additions and 1 deletions

View File

@ -11272,6 +11272,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
if (isParameter(declaration)) {
if (!declaration.symbol) {
// parameters of function types defined in JSDoc in TS files don't have symbols
return;
}
const func = declaration.parent as FunctionLikeDeclaration;
// For a parameter of a set accessor, use the type of the get accessor if one is present
if (func.kind === SyntaxKind.SetAccessor && hasBindableName(func)) {

View File

@ -151,6 +151,7 @@ import {
isIdentifier,
isImportMeta,
isInComment,
isInJSFile,
isInsideJsxElement,
isInsideJsxElementOrAttribute,
isInString,
@ -245,6 +246,7 @@ import {
PrivateIdentifier,
Program,
PropertyName,
PropertySignature,
QuickInfo,
refactor,
RefactorContext,
@ -2067,7 +2069,6 @@ export function createLanguageService(
const typeChecker = program.getTypeChecker();
const nodeForQuickInfo = getNodeForQuickInfo(node);
const symbol = getSymbolAtLocationForQuickInfo(nodeForQuickInfo, typeChecker);
if (!symbol || typeChecker.isUnknownSymbol(symbol)) {
const type = shouldGetType(sourceFile, nodeForQuickInfo, position) ? typeChecker.getTypeAtLocation(nodeForQuickInfo) : undefined;
return type && {
@ -2110,6 +2111,14 @@ export function createLanguageService(
function shouldGetType(sourceFile: SourceFile, node: Node, position: number): boolean {
switch (node.kind) {
case SyntaxKind.Identifier:
if (
node.flags & NodeFlags.JSDoc && !isInJSFile(node) &&
((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;
}
return !isLabelName(node) && !isTagName(node) && !isConstTypeReference(node.parent);
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.QualifiedName:

View File

@ -0,0 +1,45 @@
// === findAllReferences ===
// === /tests/cases/fourslash/src/index.ts ===
// import { useQuery } from "use-query";
// <|const { /*FIND ALL REFS*/[|{| isWriteAccess: true, isDefinition: true |}data|] } = useQuery();|>
// === Definitions ===
// === /tests/cases/fourslash/src/index.ts ===
// import { useQuery } from "use-query";
// <|const { /*FIND ALL REFS*/[|data|] } = useQuery();|>
// === Details ===
[
{
"containerKind": "",
"containerName": "",
"kind": "const",
"name": "const data: any",
"displayParts": [
{
"text": "const",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "data",
"kind": "localName"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "any",
"kind": "keyword"
}
]
}
]

View File

@ -0,0 +1,45 @@
// === findAllReferences ===
// === /tests/cases/fourslash/src/index.ts ===
// import { useQuery } from "use-query";
// <|const { /*FIND ALL REFS*/[|{| isWriteAccess: true, isDefinition: true |}data|] } = useQuery();|>
// === Definitions ===
// === /tests/cases/fourslash/src/index.ts ===
// import { useQuery } from "use-query";
// <|const { /*FIND ALL REFS*/[|data|] } = useQuery();|>
// === Details ===
[
{
"containerKind": "",
"containerName": "",
"kind": "const",
"name": "const data: any",
"displayParts": [
{
"text": "const",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "data",
"kind": "localName"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "any",
"kind": "keyword"
}
]
}
]

View File

@ -0,0 +1,111 @@
// === findAllReferences ===
// === /tests/cases/fourslash/findReferencesSeeTagInTs.ts ===
// <|function [|{| isWriteAccess: true, isDefinition: true |}doStuffWithStuff|]/*FIND ALL REFS*/(stuff: { quantity: number }) {}|>
//
// declare const stuff: { quantity: number };
// /** @see {[|doStuffWithStuff|]} */
// if (stuff.quantity) {}
// === Definitions ===
// === /tests/cases/fourslash/findReferencesSeeTagInTs.ts ===
// <|function [|doStuffWithStuff|]/*FIND ALL REFS*/(stuff: { quantity: number }) {}|>
//
// declare const stuff: { quantity: number };
// /** @see {doStuffWithStuff} */
// if (stuff.quantity) {}
// === Details ===
[
{
"containerKind": "",
"containerName": "",
"kind": "function",
"name": "function doStuffWithStuff(stuff: {\n quantity: number;\n}): void",
"displayParts": [
{
"text": "function",
"kind": "keyword"
},
{
"text": " ",
"kind": "space"
},
{
"text": "doStuffWithStuff",
"kind": "functionName"
},
{
"text": "(",
"kind": "punctuation"
},
{
"text": "stuff",
"kind": "parameterName"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "{",
"kind": "punctuation"
},
{
"text": "\n",
"kind": "lineBreak"
},
{
"text": " ",
"kind": "space"
},
{
"text": "quantity",
"kind": "propertyName"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "number",
"kind": "keyword"
},
{
"text": ";",
"kind": "punctuation"
},
{
"text": "\n",
"kind": "lineBreak"
},
{
"text": "}",
"kind": "punctuation"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "void",
"kind": "keyword"
}
]
}
]

View File

@ -0,0 +1,33 @@
/// <reference path='fourslash.ts' />
// @moduleResolution: node
// @Filename: node_modules/use-query/package.json
////{
//// "name": "use-query",
//// "types": "index.d.ts"
////}
// @Filename: node_modules/use-query/index.d.ts
////declare function useQuery(): {
//// data: string[];
////};
// @Filename: node_modules/other/package.json
////{
//// "name": "other",
//// "types": "index.d.ts"
////}
// @Filename: node_modules/other/index.d.ts
////interface BottomSheetModalProps {
//// /**
//// * A scrollable node or normal view.
//// * @type {({ data: any }?) => any}
//// */
//// children: ({ data: any }?) => any;
////}
// @Filename: src/index.ts
////import { useQuery } from "use-query";
////const { /*1*/data } = useQuery();
verify.baselineFindAllReferences('1');

View File

@ -0,0 +1,33 @@
/// <reference path='fourslash.ts' />
// @moduleResolution: node
// @Filename: node_modules/use-query/package.json
////{
//// "name": "use-query",
//// "types": "index.d.ts"
////}
// @Filename: node_modules/use-query/index.d.ts
////declare function useQuery(): {
//// data: string[];
////};
// @Filename: node_modules/use-query/package.json
////{
//// "name": "other",
//// "types": "index.d.ts"
////}
// @Filename: node_modules/other/index.d.ts
////interface BottomSheetModalProps {
//// /**
//// * A scrollable node or normal view.
//// * @type null | (({ data: any }?) => any)
//// */
//// children: null | (({ data: any }?) => any);
////}
// @Filename: src/index.ts
////import { useQuery } from "use-query";
////const { /*1*/data } = useQuery();
verify.baselineFindAllReferences('1');

View File

@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
//// function doStuffWithStuff/*1*/(stuff: { quantity: number }) {}
////
//// declare const stuff: { quantity: number };
//// /** @see {doStuffWithStuff} */
//// if (stuff.quantity) {}
verify.baselineFindAllReferences("1");

View File

@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />
/////** @type {({ /*1*/data: any }?) => { data: string[] }} */
////function useQuery({ data }): { data: string[] } {
//// return {
//// data,
//// };
////}
verify.quickInfoAt("1", "");

View File

@ -0,0 +1,62 @@
/// <reference path='fourslash.ts' />
//// /** @type {() => { /*1*/data: string[] }} */
//// function test(): { data: string[] } {
//// return {
//// data: [],
//// };
//// }
////
//// /** @returns {{ /*2*/data: string[] }} */
//// function test2(): { data: string[] } {
//// return {
//// data: [],
//// };
//// }
////
//// /** @type {{ /*3*/bar: string; }} */
//// const test3 = { bar: '' };
////
//// type SomeObj = { bar: string; };
//// /** @type {SomeObj/*4*/} */
//// const test4 = { bar: '' }
////
//// /**
//// * @param/*5*/ stuff/*6*/ Stuff to do stuff with
//// */
//// function doStuffWithStuff(stuff: { quantity: number }) {}
////
//// declare const stuff: { quantity: number };
//// /** @see {doStuffWithStuff/*7*/} */
//// if (stuff.quantity) {}
////
//// /** @type {(a/*8*/: string) => void} */
//// function test2(a: string) {}
verify.quickInfoAt("1", "");
verify.quickInfoAt("2", "");
verify.quickInfoAt("3", "");
verify.quickInfoAt("4", `type SomeObj = {
bar: string;
}`);
verify.quickInfoAt(
"5",
`(parameter) stuff: {
quantity: number;
}`,
"Stuff to do stuff with"
);
verify.quickInfoAt(
"6",
`(parameter) stuff: {
quantity: number;
}`,
"Stuff to do stuff with"
);
verify.quickInfoAt(
"7",
`function doStuffWithStuff(stuff: {
quantity: number;
}): void`,
);
verify.quickInfoAt("8", "");