From 7dcf11f13985be927886ebea353d282a9b3418e0 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Tue, 4 Oct 2022 01:57:32 +0300 Subject: [PATCH 1/2] fix(50750): Object type literal with string literal property in contextual typing position causes language service error on all literal type references (#50757) * fix(50750): skip unbound symbols from JSDoc tags in typescript * skip contextual type checking in JsDoc for TypeScript files --- src/services/utilities.ts | 2 + ...findAllRefsForStringLiteral.baseline.jsonc | 52 +++++++++++++++++++ .../reference/renameForStringLiteral.baseline | 11 ++++ .../fourslash/findAllRefsForStringLiteral.ts | 14 +++++ .../cases/fourslash/renameForStringLiteral.ts | 14 +++++ 5 files changed, 93 insertions(+) create mode 100644 tests/baselines/reference/findAllRefsForStringLiteral.baseline.jsonc create mode 100644 tests/baselines/reference/renameForStringLiteral.baseline create mode 100644 tests/cases/fourslash/findAllRefsForStringLiteral.ts create mode 100644 tests/cases/fourslash/renameForStringLiteral.ts diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 4d24e57d051..58d1b1cbca1 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -790,6 +790,8 @@ namespace ts { } export function getContextualTypeFromParentOrAncestorTypeNode(node: Expression, checker: TypeChecker): Type | undefined { + if (node.flags & (NodeFlags.JSDoc & ~NodeFlags.JavaScriptFile)) return undefined; + const contextualType = getContextualTypeFromParent(node, checker); if (contextualType) return contextualType; diff --git a/tests/baselines/reference/findAllRefsForStringLiteral.baseline.jsonc b/tests/baselines/reference/findAllRefsForStringLiteral.baseline.jsonc new file mode 100644 index 00000000000..5cfaa3f509c --- /dev/null +++ b/tests/baselines/reference/findAllRefsForStringLiteral.baseline.jsonc @@ -0,0 +1,52 @@ +// === /a.ts === +// interface Foo { +// property: /*FIND ALL REFS*/"[|foo|]"; +// } +// /** +// * @type {{ property: "foo"}} +// */ +// const obj: Foo = { +// property: "[|foo|]", +// } + +[ + { + "definition": { + "containerKind": "", + "containerName": "", + "fileName": "/a.ts", + "kind": "var", + "name": "foo", + "textSpan": { + "start": 31, + "length": 3 + }, + "displayParts": [ + { + "text": "\"foo\"", + "kind": "stringLiteral" + } + ] + }, + "references": [ + { + "textSpan": { + "start": 31, + "length": 3 + }, + "fileName": "/a.ts", + "isWriteAccess": false, + "isInString": true + }, + { + "textSpan": { + "start": 111, + "length": 3 + }, + "fileName": "/a.ts", + "isWriteAccess": false, + "isInString": true + } + ] + } +] \ No newline at end of file diff --git a/tests/baselines/reference/renameForStringLiteral.baseline b/tests/baselines/reference/renameForStringLiteral.baseline new file mode 100644 index 00000000000..9e915526960 --- /dev/null +++ b/tests/baselines/reference/renameForStringLiteral.baseline @@ -0,0 +1,11 @@ +/*====== /a.ts ======*/ + +interface Foo { + property: "RENAME"; +} +/** + * @type {{ property: "foo"}} + */ +const obj: Foo = { + property: "RENAME", +} diff --git a/tests/cases/fourslash/findAllRefsForStringLiteral.ts b/tests/cases/fourslash/findAllRefsForStringLiteral.ts new file mode 100644 index 00000000000..d7a3e92943d --- /dev/null +++ b/tests/cases/fourslash/findAllRefsForStringLiteral.ts @@ -0,0 +1,14 @@ +/// + +// @filename: /a.ts +////interface Foo { +//// property: /**/"foo"; +////} +/////** +//// * @type {{ property: "foo"}} +//// */ +////const obj: Foo = { +//// property: "foo", +////} + +verify.baselineFindAllReferences(""); diff --git a/tests/cases/fourslash/renameForStringLiteral.ts b/tests/cases/fourslash/renameForStringLiteral.ts new file mode 100644 index 00000000000..5d35ded522a --- /dev/null +++ b/tests/cases/fourslash/renameForStringLiteral.ts @@ -0,0 +1,14 @@ +/// + +// @filename: /a.ts +////interface Foo { +//// property: /**/"foo"; +////} +/////** +//// * @type {{ property: "foo"}} +//// */ +////const obj: Foo = { +//// property: "foo", +////} + +verify.baselineRename("", {}); From 299745cb217c2fc061f75b3735f8420d78b8360a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 3 Oct 2022 16:07:57 -0700 Subject: [PATCH 2/2] Fix crash in goto-def on `@override` (#51016) * Fix crash in goto-def on `@override` When the base type is not defined, getDefinitionFromOverriddenMember will have its type as errorType, which has no symbol. The error handling previously only handled the case of no baseType at all -- which I'm not sure ever actually happens. * Improve checking 1. getTypeAtLocation never returns undefined, only errorType, so check for that. 2. Return directly after missing baseTypeNode instead of continuing to return later. * Experiment with making goto-def on `override` more consistent * Unify static/instance node->symbol->type path * Make getSymbolAtLocation support class expressions and parenthesized expressions * Revert "Make getSymbolAtLocation support class expressions" This reverts commit 4c1b03135576c9e5d146ce6f38e691c804cbb0dd. * fix semicolon lint --- src/services/goToDefinition.ts | 10 ++++++---- .../goToDefinitionOverriddenMember16.ts | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 tests/cases/fourslash/goToDefinitionOverriddenMember16.ts diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 40cac0a916d..866588c51b9 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -171,13 +171,15 @@ namespace ts.GoToDefinition { if (!baseDeclaration) return; const baseTypeNode = getEffectiveBaseTypeNode(baseDeclaration); - const baseType = baseTypeNode ? typeChecker.getTypeAtLocation(baseTypeNode) : undefined; - if (!baseType) return; + if (!baseTypeNode) return; + const expression = skipParentheses(baseTypeNode.expression); + const base = isClassExpression(expression) ? expression.symbol : typeChecker.getSymbolAtLocation(expression); + if (!base) return; const name = unescapeLeadingUnderscores(getTextOfPropertyName(classElement.name)); const symbol = hasStaticModifier(classElement) - ? typeChecker.getPropertyOfType(typeChecker.getTypeOfSymbolAtLocation(baseType.symbol, baseDeclaration), name) - : typeChecker.getPropertyOfType(baseType, name); + ? typeChecker.getPropertyOfType(typeChecker.getTypeOfSymbol(base), name) + : typeChecker.getPropertyOfType(typeChecker.getDeclaredTypeOfSymbol(base), name); if (!symbol) return; return getDefinitionFromSymbol(typeChecker, symbol, node); diff --git a/tests/cases/fourslash/goToDefinitionOverriddenMember16.ts b/tests/cases/fourslash/goToDefinitionOverriddenMember16.ts new file mode 100644 index 00000000000..96f1467a344 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionOverriddenMember16.ts @@ -0,0 +1,16 @@ +/// +// @Filename: goToDefinitionOverrideJsdoc.ts +// @allowJs: true +// @checkJs: true + +//// export class C extends CompletelyUndefined { +//// /** +//// * @override/*1*/ +//// * @returns {{}} +//// */ +//// static foo() { +//// return {} +//// } +//// } + +verify.goToDefinition(['1'], [])