diff --git a/src/services/completions.ts b/src/services/completions.ts index c55783b060a72..77c12920f2f41 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -160,6 +160,8 @@ import { isImportKeyword, isImportSpecifier, isInComment, + isIndexSignatureDeclaration, + isInferTypeNode, isInitializedProperty, isInJSFile, isInRightSideOfInternalImportEqualsDeclaration, @@ -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, @@ -2048,7 +2053,7 @@ export function getCompletionEntriesFromSymbols( isRightOfOpenTag?: boolean, ): UniqueNameSet { const start = timestamp(); - const variableDeclaration = getVariableDeclaration(location); + const variableOrParameterDeclaration = getVariableOrParameterDeclaration(contextToken); const useSemicolons = probablyUsesSemicolons(sourceFile); const typeChecker = program.getTypeChecker(); // Tracks unique names. @@ -2121,10 +2126,27 @@ 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) { }` 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 = isParameter(variableOrParameterDeclaration) ? variableOrParameterDeclaration.parent.parameters : + isInferTypeNode(variableOrParameterDeclaration.parent) ? undefined : + variableOrParameterDeclaration.parent.typeParameters; + if (symbolDeclarationPos >= variableOrParameterDeclaration.pos && parameters && symbolDeclarationPos < parameters.end) { + 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 @@ -5084,17 +5106,22 @@ 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 => +function getVariableOrParameterDeclaration(contextToken: Node | undefined) { + if (!contextToken) return; + + const declaration = findAncestor(contextToken, node => isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node) ? "quit" - : isVariableDeclaration(node)); - - return variableDeclaration as 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; + return node.parent && isArrowFunction(node.parent) && + (node.parent.body === node || + // const a = () => /**/; + node.kind === SyntaxKind.EqualsGreaterThanToken + ); } /** True if symbol is a type or a module containing at least one type. */ 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/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/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/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..dffb285559b9a 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"]}) 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/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"], }); - diff --git a/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts b/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts new file mode 100644 index 0000000000000..6ed833c16f428 --- /dev/null +++ b/tests/cases/fourslash/noCompletionsForCurrentOrLaterParameters.ts @@ -0,0 +1,47 @@ +/// + +//// 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) { } +//// } +//// const f5 = (a, b = (c = /*7*/, e) => { }, d = b) => { } +//// +//// type A1 = K +//// type A2 = K + +verify.completions({ + marker: ["1", "2"], + excludes: ["a", "b"], +}) +verify.completions({ + marker: ["3"], + excludes: ["a", "b"], +}) + +verify.completions({ + marker: ["4"], + includes: ["a"], +}) + +verify.completions({ + marker: ["5"], + includes: ["a", "b"], +}) + +verify.completions({ + marker: ["6"], + excludes: ["b", "c"], + includes: ["a"], +}) + +verify.completions({ + marker: ["7"], + includes: ["a", "b", "d"], +}) + +verify.completions({ + marker: ["T1", "T2"], + excludes: ["K", "L"], +})