From f259b1461c058f54f7f5f7ce116bd916b24854f5 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 03:03:51 +0300 Subject: [PATCH 01/19] add impl to ensure its not lost --- src/services/completions.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/services/completions.ts b/src/services/completions.ts index c55783b060a72..2fb43a31b4e9f 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -122,6 +122,7 @@ import { isBinaryExpression, isBindingElement, isBindingPattern, + isBlock, isBreakOrContinueStatement, isCallExpression, isCaseBlock, @@ -133,6 +134,7 @@ import { isClassMemberModifier, isClassOrTypeElement, isClassStaticBlockDeclaration, + isCommaToken, isComputedPropertyName, isConstructorDeclaration, isContextualKeyword, @@ -226,6 +228,7 @@ import { isTypeOfExpression, isTypeOnlyImportDeclaration, isTypeOnlyImportOrExportDeclaration, + isTypeParameterDeclaration, isTypeReferenceType, isValidTypeOnlyAliasUseSite, isVariableDeclaration, @@ -283,6 +286,7 @@ import { ObjectType, ObjectTypeDeclaration, or, + ParameterDeclaration, ParenthesizedTypeNode, positionBelongsToNode, positionIsASICandidate, @@ -350,6 +354,7 @@ import { TypeLiteralNode, TypeNode, TypeOnlyImportDeclaration, + TypeParameterDeclaration, TypeQueryNode, TypeReferenceNode, unescapeLeadingUnderscores, @@ -2049,6 +2054,7 @@ export function getCompletionEntriesFromSymbols( ): UniqueNameSet { const start = timestamp(); const variableDeclaration = getVariableDeclaration(location); + const parameterDeclaration = getParameterDeclaration(contextToken); const useSemicolons = probablyUsesSemicolons(sourceFile); const typeChecker = program.getTypeChecker(); // Tracks unique names. @@ -2125,6 +2131,21 @@ export function getCompletionEntriesFromSymbols( return false; } + if (parameterDeclaration) { + // Filter out parameters from their own initializers + // `function f(a = /* no 'a' here */) { }` + if (symbol.valueDeclaration === parameterDeclaration) { + return false; + } + // Filter out parameters from other parameters' initializers + // `function f(a = /* no 'b' here */, b) { }` + const parameters = parameterDeclaration.parent.parameters; + const currentParamIdx = parameters.indexOf(parameterDeclaration); + if (parameters.slice(currentParamIdx).some((p) => symbol.valueDeclaration === p)) { + return false; + } + } + // External modules can have global export declarations that will be // available as global keywords in all scopes. But if the external module // already has an explicit export and user only wants to user explicit @@ -5093,6 +5114,13 @@ function getVariableDeclaration(property: Node): VariableDeclaration | undefined return variableDeclaration as VariableDeclaration | undefined; } +function getParameterDeclaration(contextToken: Node | undefined): ParameterDeclaration | undefined { + if (!contextToken) return; + const parameter = findAncestor(contextToken, node => isParameter(node)); + + return parameter as ParameterDeclaration | undefined; +} + function isArrowFunctionBody(node: Node) { return node.parent && isArrowFunction(node.parent) && node.parent.body === node; } From 7c1fdfb2b3a4415c32eab2e859484d41f037fde7 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 03:15:15 +0300 Subject: [PATCH 02/19] add test that seems okay --- .../fourslash/noIncorrectParamaterCompletions.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/cases/fourslash/noIncorrectParamaterCompletions.ts diff --git a/tests/cases/fourslash/noIncorrectParamaterCompletions.ts b/tests/cases/fourslash/noIncorrectParamaterCompletions.ts new file mode 100644 index 0000000000000..1681fc36d74dd --- /dev/null +++ b/tests/cases/fourslash/noIncorrectParamaterCompletions.ts @@ -0,0 +1,16 @@ +/// + +//// f(a = /*1*/, b) { } +//// f(a = a/*2*/, b) { } +//// f(a = a + /*3*/, b) { } + +verify.completions({ + marker: ["1", "2"], + excludes: ["a", "b"], + isNewIdentifierLocation: true, +}) +verify.completions({ + marker: ["3"], + excludes: ["a", "b"], + isNewIdentifierLocation: false, +}) From f69d9d7a3cabba3f585651babe6144ca5e450767 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 03:19:58 +0300 Subject: [PATCH 03/19] hope to fix lint --- src/services/completions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 2fb43a31b4e9f..c6404602f0c78 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -122,7 +122,6 @@ import { isBinaryExpression, isBindingElement, isBindingPattern, - isBlock, isBreakOrContinueStatement, isCallExpression, isCaseBlock, @@ -134,7 +133,6 @@ import { isClassMemberModifier, isClassOrTypeElement, isClassStaticBlockDeclaration, - isCommaToken, isComputedPropertyName, isConstructorDeclaration, isContextualKeyword, From 47497130b7b81a015b0f76ccefdf276c22bea1ab Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 03:33:34 +0300 Subject: [PATCH 04/19] fix lint (more chances) --- src/services/completions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index c6404602f0c78..419093fc65c3f 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -226,7 +226,6 @@ import { isTypeOfExpression, isTypeOnlyImportDeclaration, isTypeOnlyImportOrExportDeclaration, - isTypeParameterDeclaration, isTypeReferenceType, isValidTypeOnlyAliasUseSite, isVariableDeclaration, @@ -352,7 +351,6 @@ import { TypeLiteralNode, TypeNode, TypeOnlyImportDeclaration, - TypeParameterDeclaration, TypeQueryNode, TypeReferenceNode, unescapeLeadingUnderscores, From 09caed3b49dbab4d0e60655c64365653fe6728cb Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 04:46:34 +0300 Subject: [PATCH 05/19] be surprised with the number of tests to fix --- tests/cases/fourslash/completionListInClosedFunction02.ts | 3 +-- tests/cases/fourslash/completionListInClosedFunction03.ts | 2 +- tests/cases/fourslash/completionListInClosedFunction04.ts | 2 +- tests/cases/fourslash/completionListInUnclosedFunction02.ts | 3 +-- tests/cases/fourslash/completionListInUnclosedFunction03.ts | 3 +-- tests/cases/fourslash/completionListInUnclosedFunction04.ts | 3 +-- tests/cases/fourslash/completionListInUnclosedFunction05.ts | 3 +-- tests/cases/fourslash/completionListInUnclosedFunction06.ts | 3 +-- tests/cases/fourslash/completionListInUnclosedFunction07.ts | 3 +-- 9 files changed, 9 insertions(+), 16 deletions(-) diff --git a/tests/cases/fourslash/completionListInClosedFunction02.ts b/tests/cases/fourslash/completionListInClosedFunction02.ts index bfda6646fd736..77522ca225914 100644 --- a/tests/cases/fourslash/completionListInClosedFunction02.ts +++ b/tests/cases/fourslash/completionListInClosedFunction02.ts @@ -7,6 +7,5 @@ verify.completions({ marker: "1", - // Note: `c: typeof c` would be a compile error - includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"], + includes: ["foo", "x", "y", "z", "bar", "a", "b"], }); diff --git a/tests/cases/fourslash/completionListInClosedFunction03.ts b/tests/cases/fourslash/completionListInClosedFunction03.ts index 3ee7cd43d2496..85a0bc3d8a6ec 100644 --- a/tests/cases/fourslash/completionListInClosedFunction03.ts +++ b/tests/cases/fourslash/completionListInClosedFunction03.ts @@ -9,5 +9,5 @@ verify.completions({ marker: "1", // Note: `c = c` would be a compile error - includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"], + includes: ["foo", "x", "y", "z", "bar", "a", "b"], }); diff --git a/tests/cases/fourslash/completionListInClosedFunction04.ts b/tests/cases/fourslash/completionListInClosedFunction04.ts index 225cbbb1a2062..5dfbc5347f380 100644 --- a/tests/cases/fourslash/completionListInClosedFunction04.ts +++ b/tests/cases/fourslash/completionListInClosedFunction04.ts @@ -9,5 +9,5 @@ verify.completions({ marker: "1", // Note: `b = b` or `b = c` would be a compile error - includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"], + includes: ["foo", "x", "y", "z", "bar", "a"], }); diff --git a/tests/cases/fourslash/completionListInUnclosedFunction02.ts b/tests/cases/fourslash/completionListInUnclosedFunction02.ts index b46d51d2f69f6..d7183b3f694da 100644 --- a/tests/cases/fourslash/completionListInUnclosedFunction02.ts +++ b/tests/cases/fourslash/completionListInUnclosedFunction02.ts @@ -3,5 +3,4 @@ ////function foo(x: string, y: number, z: boolean) { //// function bar(a: number, b: string, c: typeof /*1*/ -// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet. -verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]}) +verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]}) diff --git a/tests/cases/fourslash/completionListInUnclosedFunction03.ts b/tests/cases/fourslash/completionListInUnclosedFunction03.ts index b094fe2e725d9..1aec54cf35269 100644 --- a/tests/cases/fourslash/completionListInUnclosedFunction03.ts +++ b/tests/cases/fourslash/completionListInUnclosedFunction03.ts @@ -4,5 +4,4 @@ //// function bar(a: number, b: string, c: typeof /*1*/ ////} -// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet. -verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]}) +verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]}) diff --git a/tests/cases/fourslash/completionListInUnclosedFunction04.ts b/tests/cases/fourslash/completionListInUnclosedFunction04.ts index 543b1bd321386..20a9b4efd7af8 100644 --- a/tests/cases/fourslash/completionListInUnclosedFunction04.ts +++ b/tests/cases/fourslash/completionListInUnclosedFunction04.ts @@ -3,5 +3,4 @@ ////function foo(x: string, y: number, z: boolean) { //// function bar(a: number, b: string, c: typeof x = /*1*/ -// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet. -verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]}) +verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]}) diff --git a/tests/cases/fourslash/completionListInUnclosedFunction05.ts b/tests/cases/fourslash/completionListInUnclosedFunction05.ts index 82d3611001d35..c077c5eb1e2da 100644 --- a/tests/cases/fourslash/completionListInUnclosedFunction05.ts +++ b/tests/cases/fourslash/completionListInUnclosedFunction05.ts @@ -4,5 +4,4 @@ //// function bar(a: number, b: string, c: typeof x = /*1*/ ////} -// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet. -verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]}) +verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]}) diff --git a/tests/cases/fourslash/completionListInUnclosedFunction06.ts b/tests/cases/fourslash/completionListInUnclosedFunction06.ts index 21b1349a60762..712ae4bae051c 100644 --- a/tests/cases/fourslash/completionListInUnclosedFunction06.ts +++ b/tests/cases/fourslash/completionListInUnclosedFunction06.ts @@ -4,5 +4,4 @@ //// function bar(a: number, b: string = /*1*/, c: typeof x = "hello" //// -// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet. -verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]}) +verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a"]}) diff --git a/tests/cases/fourslash/completionListInUnclosedFunction07.ts b/tests/cases/fourslash/completionListInUnclosedFunction07.ts index 7863d9dd1f36c..ca0b21719ca79 100644 --- a/tests/cases/fourslash/completionListInUnclosedFunction07.ts +++ b/tests/cases/fourslash/completionListInUnclosedFunction07.ts @@ -4,5 +4,4 @@ //// function bar(a: number, b: string = /*1*/, c: typeof x = "hello" ////} -// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet. -verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]}) +verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]}) From d520a3106767a1a6352c5e5184e9caf6d5ff3456 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 04:47:57 +0300 Subject: [PATCH 06/19] fix don't include index signature --- src/services/completions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 419093fc65c3f..c666051694418 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -160,6 +160,7 @@ import { isImportKeyword, isImportSpecifier, isInComment, + isIndexSignatureDeclaration, isInitializedProperty, isInJSFile, isInRightSideOfInternalImportEqualsDeclaration, @@ -5112,7 +5113,7 @@ function getVariableDeclaration(property: Node): VariableDeclaration | undefined function getParameterDeclaration(contextToken: Node | undefined): ParameterDeclaration | undefined { if (!contextToken) return; - const parameter = findAncestor(contextToken, node => isParameter(node)); + const parameter = findAncestor(contextToken, node => isParameter(node) && !isIndexSignatureDeclaration(node.parent)); return parameter as ParameterDeclaration | undefined; } From e27faedb32860da950454a2733e56963049ed592 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 04:49:21 +0300 Subject: [PATCH 07/19] fix my tests --- tests/cases/fourslash/noIncorrectParamaterCompletions.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/cases/fourslash/noIncorrectParamaterCompletions.ts b/tests/cases/fourslash/noIncorrectParamaterCompletions.ts index 1681fc36d74dd..78ab21585b95d 100644 --- a/tests/cases/fourslash/noIncorrectParamaterCompletions.ts +++ b/tests/cases/fourslash/noIncorrectParamaterCompletions.ts @@ -1,16 +1,14 @@ /// -//// f(a = /*1*/, b) { } -//// f(a = a/*2*/, b) { } -//// f(a = a + /*3*/, b) { } +//// function f1(a = /*1*/, b) { } +//// function f2(a = a/*2*/, b) { } +//// function f3(a = a + /*3*/, b) { } verify.completions({ marker: ["1", "2"], excludes: ["a", "b"], - isNewIdentifierLocation: true, }) verify.completions({ marker: ["3"], excludes: ["a", "b"], - isNewIdentifierLocation: false, }) From fee4e96bd77c32321f7c2633c2760504f127b77e Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 04:51:23 +0300 Subject: [PATCH 08/19] add positive testcases --- .../fourslash/noIncorrectParamaterCompletions.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/cases/fourslash/noIncorrectParamaterCompletions.ts b/tests/cases/fourslash/noIncorrectParamaterCompletions.ts index 78ab21585b95d..d34640e52d52f 100644 --- a/tests/cases/fourslash/noIncorrectParamaterCompletions.ts +++ b/tests/cases/fourslash/noIncorrectParamaterCompletions.ts @@ -2,7 +2,7 @@ //// function f1(a = /*1*/, b) { } //// function f2(a = a/*2*/, b) { } -//// function f3(a = a + /*3*/, b) { } +//// function f3(a = a + /*3*/, b = a/*4*/, c = /*5*/) { } verify.completions({ marker: ["1", "2"], @@ -12,3 +12,13 @@ verify.completions({ marker: ["3"], excludes: ["a", "b"], }) + +verify.completions({ + marker: ["4"], + includes: ["a"], +}) + +verify.completions({ + marker: ["5"], + includes: ["a", "b"], +}) From f3154b05a8e7f4d7ed61a497ead337a8addd1850 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 05:18:23 +0300 Subject: [PATCH 09/19] simplify implementation --- src/services/completions.ts | 18 +++++++----------- .../noIncorrectParamaterCompletions.ts | 9 +++++++++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index c666051694418..c67bb7a7bf98f 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -2128,17 +2128,13 @@ export function getCompletionEntriesFromSymbols( return false; } - if (parameterDeclaration) { - // Filter out parameters from their own initializers - // `function f(a = /* no 'a' here */) { }` - if (symbol.valueDeclaration === parameterDeclaration) { - return false; - } - // Filter out parameters from other parameters' initializers - // `function f(a = /* no 'b' here */, b) { }` - const parameters = parameterDeclaration.parent.parameters; - const currentParamIdx = parameters.indexOf(parameterDeclaration); - if (parameters.slice(currentParamIdx).some((p) => symbol.valueDeclaration === p)) { + // Filter out parameters from their own initializers + // `function f(a = /* no 'a' and 'b' here */, b) { }` + const symbolDeclaration = symbol.valueDeclaration; + if (parameterDeclaration && tryCast(symbolDeclaration, isParameter)) { + const symbolDeclarationPos = (symbolDeclaration as ParameterDeclaration).pos; + const { parameters } = parameterDeclaration.parent; + if (symbolDeclarationPos >= parameterDeclaration.pos && symbolDeclarationPos < parameters.end) { return false; } } diff --git a/tests/cases/fourslash/noIncorrectParamaterCompletions.ts b/tests/cases/fourslash/noIncorrectParamaterCompletions.ts index d34640e52d52f..088aaf3bd40b3 100644 --- a/tests/cases/fourslash/noIncorrectParamaterCompletions.ts +++ b/tests/cases/fourslash/noIncorrectParamaterCompletions.ts @@ -3,6 +3,9 @@ //// function f1(a = /*1*/, b) { } //// function f2(a = a/*2*/, b) { } //// function f3(a = a + /*3*/, b = a/*4*/, c = /*5*/) { } +//// function f3(a) { +//// function f4(b = /*6*/, c) { } +////} verify.completions({ marker: ["1", "2"], @@ -22,3 +25,9 @@ verify.completions({ marker: ["5"], includes: ["a", "b"], }) + +verify.completions({ + marker: ["6"], + excludes: ["b", "c"], + includes: ["a"], +}) From 36ed7b743b7f2e3e1887819d229cb1862899a94f Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 05:32:31 +0300 Subject: [PATCH 10/19] refactor to `getVariableOrParameterDeclaration` (simplify) --- src/services/completions.ts | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index c67bb7a7bf98f..16adc40532221 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -2050,8 +2050,7 @@ export function getCompletionEntriesFromSymbols( isRightOfOpenTag?: boolean, ): UniqueNameSet { const start = timestamp(); - const variableDeclaration = getVariableDeclaration(location); - const parameterDeclaration = getParameterDeclaration(contextToken); + const variableOrParameterDeclaration = getVariableOrParameterDeclaration(contextToken); const useSemicolons = probablyUsesSemicolons(sourceFile); const typeChecker = program.getTypeChecker(); // Tracks unique names. @@ -2124,17 +2123,17 @@ export function getCompletionEntriesFromSymbols( } // Filter out variables from their own initializers // `const a = /* no 'a' here */` - if (variableDeclaration && symbol.valueDeclaration === variableDeclaration) { + if (tryCast(variableOrParameterDeclaration, isVariableDeclaration) && symbol.valueDeclaration === variableOrParameterDeclaration) { return false; } // Filter out parameters from their own initializers // `function f(a = /* no 'a' and 'b' here */, b) { }` const symbolDeclaration = symbol.valueDeclaration; - if (parameterDeclaration && tryCast(symbolDeclaration, isParameter)) { + if (tryCast(variableOrParameterDeclaration, isParameter) && tryCast(symbolDeclaration, isParameter)) { const symbolDeclarationPos = (symbolDeclaration as ParameterDeclaration).pos; - const { parameters } = parameterDeclaration.parent; - if (symbolDeclarationPos >= parameterDeclaration.pos && symbolDeclarationPos < parameters.end) { + const { parameters } = (variableOrParameterDeclaration as ParameterDeclaration).parent; + if (symbolDeclarationPos >= variableOrParameterDeclaration!.pos && symbolDeclarationPos < parameters.end) { return false; } } @@ -5098,20 +5097,14 @@ function isModuleSpecifierMissingOrEmpty(specifier: ModuleReference | Expression return !tryCast(isExternalModuleReference(specifier) ? specifier.expression : specifier, isStringLiteralLike)?.text; } -function getVariableDeclaration(property: Node): VariableDeclaration | undefined { - const variableDeclaration = findAncestor(property, node => - isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node) - ? "quit" - : isVariableDeclaration(node)); - - return variableDeclaration as VariableDeclaration | undefined; -} - -function getParameterDeclaration(contextToken: Node | undefined): ParameterDeclaration | undefined { +function getVariableOrParameterDeclaration(contextToken: Node | undefined) { if (!contextToken) return; - const parameter = findAncestor(contextToken, node => isParameter(node) && !isIndexSignatureDeclaration(node.parent)); - return parameter as ParameterDeclaration | undefined; + return findAncestor(contextToken, node => + (isParameter(node) && !isIndexSignatureDeclaration(node.parent)) || + (isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node) + ? "quit" + : isVariableDeclaration(node))) as ParameterDeclaration | VariableDeclaration | undefined; } function isArrowFunctionBody(node: Node) { From c1d02f2fe17819c431a8c23b0e4ca024bd52844b Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 05:33:39 +0300 Subject: [PATCH 11/19] fix remaining test? --- tests/cases/fourslash/completionListInUnclosedFunction07.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cases/fourslash/completionListInUnclosedFunction07.ts b/tests/cases/fourslash/completionListInUnclosedFunction07.ts index ca0b21719ca79..dffb285559b9a 100644 --- a/tests/cases/fourslash/completionListInUnclosedFunction07.ts +++ b/tests/cases/fourslash/completionListInUnclosedFunction07.ts @@ -4,4 +4,4 @@ //// function bar(a: number, b: string = /*1*/, c: typeof x = "hello" ////} -verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]}) +verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a"]}) From b204b1d2b8e1cae8eff4fb88dc90c008c341c604 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 05:35:50 +0300 Subject: [PATCH 12/19] rename test file --- ...Completions.ts => noCompletionsForCurrentOrLaterParameters.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/cases/fourslash/{noIncorrectParamaterCompletions.ts => noCompletionsForCurrentOrLaterParameters.ts} (100%) diff --git a/tests/cases/fourslash/noIncorrectParamaterCompletions.ts b/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts similarity index 100% rename from tests/cases/fourslash/noIncorrectParamaterCompletions.ts rename to tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts From b6eafdddc87db03690f950f2038d35d645b2f0a2 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 21:25:06 +0300 Subject: [PATCH 13/19] again fix bunch of tests (imporve quality) --- tests/cases/fourslash/completionListInClosedFunction05.ts | 3 +-- tests/cases/fourslash/completionListInUnclosedFunction08.ts | 3 +-- tests/cases/fourslash/completionListInUnclosedFunction09.ts | 3 +-- tests/cases/fourslash/completionListIsGlobalCompletion.ts | 2 +- .../fourslash/completionListWithoutVariableinitializer.ts | 4 +--- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/cases/fourslash/completionListInClosedFunction05.ts b/tests/cases/fourslash/completionListInClosedFunction05.ts index 11c1600934983..161c6ed3bda62 100644 --- a/tests/cases/fourslash/completionListInClosedFunction05.ts +++ b/tests/cases/fourslash/completionListInClosedFunction05.ts @@ -8,7 +8,6 @@ verify.completions({ marker: "1", - // Note: `v = v` would be a compile error - includes: ["foo", "x", "y", "z", "bar", "a", "b", "c", "v"], + includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"], isNewIdentifierLocation: true, }); diff --git a/tests/cases/fourslash/completionListInUnclosedFunction08.ts b/tests/cases/fourslash/completionListInUnclosedFunction08.ts index c0616881bd6a9..090ef27227410 100644 --- a/tests/cases/fourslash/completionListInUnclosedFunction08.ts +++ b/tests/cases/fourslash/completionListInUnclosedFunction08.ts @@ -4,5 +4,4 @@ //// function bar(a: number, b: string = "hello", c: typeof x = "hello") { //// var v = /*1*/ -// Note: "v" questionable since we're in its initializer -verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c", "v"], isNewIdentifierLocation: true }); +verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"], isNewIdentifierLocation: true }); diff --git a/tests/cases/fourslash/completionListInUnclosedFunction09.ts b/tests/cases/fourslash/completionListInUnclosedFunction09.ts index 14827b1a891cd..b87a43eece085 100644 --- a/tests/cases/fourslash/completionListInUnclosedFunction09.ts +++ b/tests/cases/fourslash/completionListInUnclosedFunction09.ts @@ -5,5 +5,4 @@ //// var v = /*1*/ ////} -// Note: "v" questionable since we're in its initializer -verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c", "v"], isNewIdentifierLocation: true }); +verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"], isNewIdentifierLocation: true }); diff --git a/tests/cases/fourslash/completionListIsGlobalCompletion.ts b/tests/cases/fourslash/completionListIsGlobalCompletion.ts index 107c64adadb2a..8b773c3bef61a 100644 --- a/tests/cases/fourslash/completionListIsGlobalCompletion.ts +++ b/tests/cases/fourslash/completionListIsGlobalCompletion.ts @@ -48,7 +48,7 @@ verify.completions( { marker: "7", exact: completion.globalsInsideFunction(x), isGlobalCompletion: true }, { marker: "9", exact: ["x", "y"], isGlobalCompletion: false }, { marker: "10", exact: completion.classElementKeywords, isGlobalCompletion: false, isNewIdentifierLocation: true }, - { marker: "13", exact: globals, isGlobalCompletion: false }, + { marker: "13", exact: globals.filter(name => name !== 'z'), isGlobalCompletion: false }, { marker: "15", exact: globals.filter(name => name !== 'x'), isGlobalCompletion: true, isNewIdentifierLocation: true }, { marker: "16", unsorted: [...x, completion.globalThisEntry, ...completion.globalsVars, completion.undefinedVarEntry].filter(name => name !== 'user'), isGlobalCompletion: false }, { marker: "17", exact: completion.globalKeywords, isGlobalCompletion: false }, diff --git a/tests/cases/fourslash/completionListWithoutVariableinitializer.ts b/tests/cases/fourslash/completionListWithoutVariableinitializer.ts index 7ef57ca66e196..5d2a95b743cca 100644 --- a/tests/cases/fourslash/completionListWithoutVariableinitializer.ts +++ b/tests/cases/fourslash/completionListWithoutVariableinitializer.ts @@ -46,8 +46,7 @@ verify.completions({ verify.completions({ marker: ["7"], - includes: ["a", "b", "c", "d", "e"], - excludes: ["fn"], + includes: ["a", "b", "c", "d", "e", "fn"], }); verify.completions({ @@ -59,4 +58,3 @@ verify.completions({ marker: ["9"], includes: ["a", "b", "c", "d", "e", "fn"], }); - From 038e9536ed37713f0f92422e4cb89f4620281a7d Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 21:26:22 +0300 Subject: [PATCH 14/19] fix `const a = () => a` though it was enough to do most top contextToken only, but lets be consistent in code --- src/services/completions.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 16adc40532221..964930454714f 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -5108,7 +5108,11 @@ function getVariableOrParameterDeclaration(contextToken: Node | undefined) { } function isArrowFunctionBody(node: Node) { - return node.parent && isArrowFunction(node.parent) && node.parent.body === node; + return node.parent && isArrowFunction(node.parent) && + (node.parent.body === node || + // const a = () => /**/; + (node.kind === SyntaxKind.EqualsGreaterThanToken && node.parent.equalsGreaterThanToken === node) + ); } /** True if symbol is a type or a module containing at least one type. */ From a0aea6222a53103e6797ad09a24da162eedae3a2 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Thu, 9 Feb 2023 21:26:36 +0300 Subject: [PATCH 15/19] add more complex test case --- .../fourslash/noCompletionsForCurrentOrLaterParameters.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts b/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts index 088aaf3bd40b3..266ce7d4bb1d8 100644 --- a/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts +++ b/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts @@ -5,7 +5,8 @@ //// function f3(a = a + /*3*/, b = a/*4*/, c = /*5*/) { } //// function f3(a) { //// function f4(b = /*6*/, c) { } -////} +//// } +//// const f5 = (a, b = (c = /*7*/, e) => { }, d = b) => { } verify.completions({ marker: ["1", "2"], @@ -31,3 +32,8 @@ verify.completions({ excludes: ["b", "c"], includes: ["a"], }) + +verify.completions({ + marker: ["7"], + includes: ["a", "b", "d"], +}) From 85e35e0ba9019c02c813303627e35a26630efdea Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Fri, 17 Feb 2023 22:09:25 +0300 Subject: [PATCH 16/19] avoid casting --- src/services/completions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 964930454714f..89600256c0ab9 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -2130,10 +2130,10 @@ export function getCompletionEntriesFromSymbols( // Filter out parameters from their own initializers // `function f(a = /* no 'a' and 'b' here */, b) { }` const symbolDeclaration = symbol.valueDeclaration; - if (tryCast(variableOrParameterDeclaration, isParameter) && tryCast(symbolDeclaration, isParameter)) { - const symbolDeclarationPos = (symbolDeclaration as ParameterDeclaration).pos; - const { parameters } = (variableOrParameterDeclaration as ParameterDeclaration).parent; - if (symbolDeclarationPos >= variableOrParameterDeclaration!.pos && symbolDeclarationPos < parameters.end) { + if (variableOrParameterDeclaration && isParameter(variableOrParameterDeclaration) && symbolDeclaration && isParameter(symbolDeclaration)) { + const symbolDeclarationPos = symbolDeclaration.pos; + const { parameters } = variableOrParameterDeclaration.parent; + if (symbolDeclarationPos >= variableOrParameterDeclaration.pos && symbolDeclarationPos < parameters.end) { return false; } } From 0b281e21ce455da44dc4132b0c600ebd50af8c2a Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 19 Feb 2023 04:40:38 +0300 Subject: [PATCH 17/19] do the same for type parameters pick changes from https://github.com/zardoy/TypeScript/commit/3f2a0ffda9a286f9fd817f8492e273900c624aa4 --- src/services/completions.ts | 25 +++++++++++++------ ...oCompletionsForCurrentOrLaterParameters.ts | 8 ++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 89600256c0ab9..09fd9bf8b3182 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -161,6 +161,7 @@ import { isImportSpecifier, isInComment, isIndexSignatureDeclaration, + isInferTypeNode, isInitializedProperty, isInJSFile, isInRightSideOfInternalImportEqualsDeclaration, @@ -227,6 +228,7 @@ import { isTypeOfExpression, isTypeOnlyImportDeclaration, isTypeOnlyImportOrExportDeclaration, + isTypeParameterDeclaration, isTypeReferenceType, isValidTypeOnlyAliasUseSite, isVariableDeclaration, @@ -352,6 +354,7 @@ import { TypeLiteralNode, TypeNode, TypeOnlyImportDeclaration, + TypeParameterDeclaration, TypeQueryNode, TypeReferenceNode, unescapeLeadingUnderscores, @@ -2128,12 +2131,20 @@ export function getCompletionEntriesFromSymbols( } // Filter out parameters from their own initializers - // `function f(a = /* no 'a' and 'b' here */, b) { }` - const symbolDeclaration = symbol.valueDeclaration; - if (variableOrParameterDeclaration && isParameter(variableOrParameterDeclaration) && symbolDeclaration && isParameter(symbolDeclaration)) { + // `function f(a = /* no 'a' and 'b' here */, b) { }` or + // `function f(a: T) { }` + const symbolDeclaration = symbol.valueDeclaration ?? symbol.declarations?.[0]; + if (variableOrParameterDeclaration && symbolDeclaration && ( + (isTypeParameterDeclaration(variableOrParameterDeclaration) && isTypeParameterDeclaration(symbolDeclaration)) || + (isParameter(variableOrParameterDeclaration) && isParameter(symbolDeclaration)) + )) { const symbolDeclarationPos = symbolDeclaration.pos; - const { parameters } = variableOrParameterDeclaration.parent; - if (symbolDeclarationPos >= variableOrParameterDeclaration.pos && symbolDeclarationPos < parameters.end) { + const parameters = isParameter(variableOrParameterDeclaration) ? + variableOrParameterDeclaration.parent.parameters : + isInferTypeNode(variableOrParameterDeclaration.parent) ? + undefined : + variableOrParameterDeclaration.parent.typeParameters; + if (symbolDeclarationPos >= variableOrParameterDeclaration.pos && parameters && symbolDeclarationPos < parameters.end) { return false; } } @@ -5101,10 +5112,10 @@ function getVariableOrParameterDeclaration(contextToken: Node | undefined) { if (!contextToken) return; return findAncestor(contextToken, node => - (isParameter(node) && !isIndexSignatureDeclaration(node.parent)) || + (isParameter(node) || isTypeParameterDeclaration(node) && !isIndexSignatureDeclaration(node.parent)) || (isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node) ? "quit" - : isVariableDeclaration(node))) as ParameterDeclaration | VariableDeclaration | undefined; + : isVariableDeclaration(node))) as ParameterDeclaration | TypeParameterDeclaration | VariableDeclaration | undefined; } function isArrowFunctionBody(node: Node) { diff --git a/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts b/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts index 266ce7d4bb1d8..6ed833c16f428 100644 --- a/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts +++ b/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts @@ -7,6 +7,9 @@ //// function f4(b = /*6*/, c) { } //// } //// const f5 = (a, b = (c = /*7*/, e) => { }, d = b) => { } +//// +//// type A1 = K +//// type A2 = K verify.completions({ marker: ["1", "2"], @@ -37,3 +40,8 @@ verify.completions({ marker: ["7"], includes: ["a", "b", "d"], }) + +verify.completions({ + marker: ["T1", "T2"], + excludes: ["K", "L"], +}) From dced6bc2899a15db10484fead989884939f4fcd5 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Sun, 19 Feb 2023 05:18:14 +0300 Subject: [PATCH 18/19] fix tests --- src/services/completions.ts | 2 +- .../completionListInTypeParameterOfClassExpression1.ts | 2 +- tests/cases/fourslash/completionListIsGlobalCompletion.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 09fd9bf8b3182..6e54aac328dd9 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -5112,7 +5112,7 @@ function getVariableOrParameterDeclaration(contextToken: Node | undefined) { if (!contextToken) return; return findAncestor(contextToken, node => - (isParameter(node) || isTypeParameterDeclaration(node) && !isIndexSignatureDeclaration(node.parent)) || + ((isParameter(node) || isTypeParameterDeclaration(node)) && !isIndexSignatureDeclaration(node.parent)) || (isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node) ? "quit" : isVariableDeclaration(node))) as ParameterDeclaration | TypeParameterDeclaration | VariableDeclaration | undefined; diff --git a/tests/cases/fourslash/completionListInTypeParameterOfClassExpression1.ts b/tests/cases/fourslash/completionListInTypeParameterOfClassExpression1.ts index 7435289eb5246..15f150cef62c1 100644 --- a/tests/cases/fourslash/completionListInTypeParameterOfClassExpression1.ts +++ b/tests/cases/fourslash/completionListInTypeParameterOfClassExpression1.ts @@ -7,4 +7,4 @@ ////var C4 = class D{} verify.completions({ marker: ["0", "1", "2", "3"], exact: undefined }); -verify.completions({ marker: "4", exact: completion.globalTypesPlus(["D", "T"]) }); +verify.completions({ marker: "4", exact: completion.globalTypesPlus(["D"]) }); diff --git a/tests/cases/fourslash/completionListIsGlobalCompletion.ts b/tests/cases/fourslash/completionListIsGlobalCompletion.ts index 8b773c3bef61a..107c64adadb2a 100644 --- a/tests/cases/fourslash/completionListIsGlobalCompletion.ts +++ b/tests/cases/fourslash/completionListIsGlobalCompletion.ts @@ -48,7 +48,7 @@ verify.completions( { marker: "7", exact: completion.globalsInsideFunction(x), isGlobalCompletion: true }, { marker: "9", exact: ["x", "y"], isGlobalCompletion: false }, { marker: "10", exact: completion.classElementKeywords, isGlobalCompletion: false, isNewIdentifierLocation: true }, - { marker: "13", exact: globals.filter(name => name !== 'z'), isGlobalCompletion: false }, + { marker: "13", exact: globals, isGlobalCompletion: false }, { marker: "15", exact: globals.filter(name => name !== 'x'), isGlobalCompletion: true, isNewIdentifierLocation: true }, { marker: "16", unsorted: [...x, completion.globalThisEntry, ...completion.globalsVars, completion.undefinedVarEntry].filter(name => name !== 'user'), isGlobalCompletion: false }, { marker: "17", exact: completion.globalKeywords, isGlobalCompletion: false }, From d1666e86cc427cc55664e93e8d65ccb586eaffd3 Mon Sep 17 00:00:00 2001 From: Vitaly Turovsky Date: Tue, 7 Mar 2023 21:58:11 +0300 Subject: [PATCH 19/19] proposed changes --- src/services/completions.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 6e54aac328dd9..77c12920f2f41 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -2139,11 +2139,9 @@ export function getCompletionEntriesFromSymbols( (isParameter(variableOrParameterDeclaration) && isParameter(symbolDeclaration)) )) { const symbolDeclarationPos = symbolDeclaration.pos; - const parameters = isParameter(variableOrParameterDeclaration) ? - variableOrParameterDeclaration.parent.parameters : - isInferTypeNode(variableOrParameterDeclaration.parent) ? - undefined : - variableOrParameterDeclaration.parent.typeParameters; + const parameters = isParameter(variableOrParameterDeclaration) ? variableOrParameterDeclaration.parent.parameters : + isInferTypeNode(variableOrParameterDeclaration.parent) ? undefined : + variableOrParameterDeclaration.parent.typeParameters; if (symbolDeclarationPos >= variableOrParameterDeclaration.pos && parameters && symbolDeclarationPos < parameters.end) { return false; } @@ -5111,18 +5109,18 @@ function isModuleSpecifierMissingOrEmpty(specifier: ModuleReference | Expression function getVariableOrParameterDeclaration(contextToken: Node | undefined) { if (!contextToken) return; - return findAncestor(contextToken, node => - ((isParameter(node) || isTypeParameterDeclaration(node)) && !isIndexSignatureDeclaration(node.parent)) || - (isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node) + const declaration = findAncestor(contextToken, node => + isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node) ? "quit" - : isVariableDeclaration(node))) as ParameterDeclaration | TypeParameterDeclaration | VariableDeclaration | undefined; + : isVariableDeclaration(node) || ((isParameter(node) || isTypeParameterDeclaration(node)) && !isIndexSignatureDeclaration(node.parent))); + return declaration as ParameterDeclaration | TypeParameterDeclaration | VariableDeclaration | undefined; } function isArrowFunctionBody(node: Node) { return node.parent && isArrowFunction(node.parent) && (node.parent.body === node || // const a = () => /**/; - (node.kind === SyntaxKind.EqualsGreaterThanToken && node.parent.equalsGreaterThanToken === node) + node.kind === SyntaxKind.EqualsGreaterThanToken ); }