Skip to content

Commit c70fa51

Browse files
authored
Don't include completions for current and later parameters (#52690)
1 parent 0c5be02 commit c70fa51

16 files changed

+96
-34
lines changed

src/services/completions.ts

+35-8
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ import {
168168
isImportKeyword,
169169
isImportSpecifier,
170170
isInComment,
171+
isIndexSignatureDeclaration,
172+
isInferTypeNode,
171173
isInitializedProperty,
172174
isInJSFile,
173175
isInRightSideOfInternalImportEqualsDeclaration,
@@ -235,6 +237,7 @@ import {
235237
isTypeOfExpression,
236238
isTypeOnlyImportDeclaration,
237239
isTypeOnlyImportOrExportDeclaration,
240+
isTypeParameterDeclaration,
238241
isTypeReferenceType,
239242
isValidTypeOnlyAliasUseSite,
240243
isVariableDeclaration,
@@ -292,6 +295,7 @@ import {
292295
ObjectType,
293296
ObjectTypeDeclaration,
294297
or,
298+
ParameterDeclaration,
295299
ParenthesizedTypeNode,
296300
positionBelongsToNode,
297301
positionIsASICandidate,
@@ -360,6 +364,7 @@ import {
360364
TypeLiteralNode,
361365
TypeNode,
362366
TypeOnlyImportDeclaration,
367+
TypeParameterDeclaration,
363368
TypeQueryNode,
364369
TypeReferenceNode,
365370
unescapeLeadingUnderscores,
@@ -2389,7 +2394,7 @@ export function getCompletionEntriesFromSymbols(
23892394
includeSymbol = false
23902395
): UniqueNameSet {
23912396
const start = timestamp();
2392-
const variableDeclaration = getVariableDeclaration(location);
2397+
const variableOrParameterDeclaration = getVariableOrParameterDeclaration(contextToken);
23932398
const useSemicolons = probablyUsesSemicolons(sourceFile);
23942399
const typeChecker = program.getTypeChecker();
23952400
// Tracks unique names.
@@ -2463,10 +2468,27 @@ export function getCompletionEntriesFromSymbols(
24632468
}
24642469
// Filter out variables from their own initializers
24652470
// `const a = /* no 'a' here */`
2466-
if (variableDeclaration && symbol.valueDeclaration === variableDeclaration) {
2471+
if (tryCast(variableOrParameterDeclaration, isVariableDeclaration) && symbol.valueDeclaration === variableOrParameterDeclaration) {
24672472
return false;
24682473
}
24692474

2475+
// Filter out parameters from their own initializers
2476+
// `function f(a = /* no 'a' and 'b' here */, b) { }` or
2477+
// `function f<T = /* no 'T' here */>(a: T) { }`
2478+
const symbolDeclaration = symbol.valueDeclaration ?? symbol.declarations?.[0];
2479+
if (variableOrParameterDeclaration && symbolDeclaration && (
2480+
(isTypeParameterDeclaration(variableOrParameterDeclaration) && isTypeParameterDeclaration(symbolDeclaration)) ||
2481+
(isParameter(variableOrParameterDeclaration) && isParameter(symbolDeclaration))
2482+
)) {
2483+
const symbolDeclarationPos = symbolDeclaration.pos;
2484+
const parameters = isParameter(variableOrParameterDeclaration) ? variableOrParameterDeclaration.parent.parameters :
2485+
isInferTypeNode(variableOrParameterDeclaration.parent) ? undefined :
2486+
variableOrParameterDeclaration.parent.typeParameters;
2487+
if (symbolDeclarationPos >= variableOrParameterDeclaration.pos && parameters && symbolDeclarationPos < parameters.end) {
2488+
return false;
2489+
}
2490+
}
2491+
24702492
// External modules can have global export declarations that will be
24712493
// available as global keywords in all scopes. But if the external module
24722494
// already has an explicit export and user only wants to user explicit
@@ -5442,17 +5464,22 @@ function isModuleSpecifierMissingOrEmpty(specifier: ModuleReference | Expression
54425464
return !tryCast(isExternalModuleReference(specifier) ? specifier.expression : specifier, isStringLiteralLike)?.text;
54435465
}
54445466

5445-
function getVariableDeclaration(property: Node): VariableDeclaration | undefined {
5446-
const variableDeclaration = findAncestor(property, node =>
5467+
function getVariableOrParameterDeclaration(contextToken: Node | undefined) {
5468+
if (!contextToken) return;
5469+
5470+
const declaration = findAncestor(contextToken, node =>
54475471
isFunctionBlock(node) || isArrowFunctionBody(node) || isBindingPattern(node)
54485472
? "quit"
5449-
: isVariableDeclaration(node));
5450-
5451-
return variableDeclaration as VariableDeclaration | undefined;
5473+
: isVariableDeclaration(node) || ((isParameter(node) || isTypeParameterDeclaration(node)) && !isIndexSignatureDeclaration(node.parent)));
5474+
return declaration as ParameterDeclaration | TypeParameterDeclaration | VariableDeclaration | undefined;
54525475
}
54535476

54545477
function isArrowFunctionBody(node: Node) {
5455-
return node.parent && isArrowFunction(node.parent) && node.parent.body === node;
5478+
return node.parent && isArrowFunction(node.parent) &&
5479+
(node.parent.body === node ||
5480+
// const a = () => /**/;
5481+
node.kind === SyntaxKind.EqualsGreaterThanToken
5482+
);
54565483
}
54575484

54585485
/** True if symbol is a type or a module containing at least one type. */

tests/cases/fourslash/completionListInClosedFunction02.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@
77

88
verify.completions({
99
marker: "1",
10-
// Note: `c: typeof c` would be a compile error
11-
includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"],
10+
includes: ["foo", "x", "y", "z", "bar", "a", "b"],
1211
});

tests/cases/fourslash/completionListInClosedFunction03.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
verify.completions({
1010
marker: "1",
1111
// Note: `c = c` would be a compile error
12-
includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"],
12+
includes: ["foo", "x", "y", "z", "bar", "a", "b"],
1313
});

tests/cases/fourslash/completionListInClosedFunction04.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
verify.completions({
1010
marker: "1",
1111
// Note: `b = b` or `b = c` would be a compile error
12-
includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"],
12+
includes: ["foo", "x", "y", "z", "bar", "a"],
1313
});

tests/cases/fourslash/completionListInClosedFunction05.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
verify.completions({
1010
marker: "1",
11-
// Note: `v = v` would be a compile error
12-
includes: ["foo", "x", "y", "z", "bar", "a", "b", "c", "v"],
11+
includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"],
1312
isNewIdentifierLocation: true,
1413
});

tests/cases/fourslash/completionListInTypeParameterOfClassExpression1.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
////var C4 = class D<T extends /*4*/>{}
88

99
verify.completions({ marker: ["0", "1", "2", "3"], exact: undefined });
10-
verify.completions({ marker: "4", exact: completion.globalTypesPlus(["D", "T"]) });
10+
verify.completions({ marker: "4", exact: completion.globalTypesPlus(["D"]) });

tests/cases/fourslash/completionListInUnclosedFunction02.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
////function foo(x: string, y: number, z: boolean) {
44
//// function bar(a: number, b: string, c: typeof /*1*/
55

6-
// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet.
7-
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]})
6+
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]})

tests/cases/fourslash/completionListInUnclosedFunction03.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44
//// function bar(a: number, b: string, c: typeof /*1*/
55
////}
66

7-
// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet.
8-
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]})
7+
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]})

tests/cases/fourslash/completionListInUnclosedFunction04.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
////function foo(x: string, y: number, z: boolean) {
44
//// function bar(a: number, b: string, c: typeof x = /*1*/
55

6-
// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet.
7-
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]})
6+
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]})

tests/cases/fourslash/completionListInUnclosedFunction05.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44
//// function bar(a: number, b: string, c: typeof x = /*1*/
55
////}
66

7-
// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet.
8-
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]})
7+
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b"]})

tests/cases/fourslash/completionListInUnclosedFunction06.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44
//// function bar(a: number, b: string = /*1*/, c: typeof x = "hello"
55
////
66

7-
// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet.
8-
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]})
7+
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a"]})

tests/cases/fourslash/completionListInUnclosedFunction07.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44
//// function bar(a: number, b: string = /*1*/, c: typeof x = "hello"
55
////}
66

7-
// Note: Ideally `c` wouldn't be included since it hasn't been initialized yet.
8-
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"]})
7+
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a"]})

tests/cases/fourslash/completionListInUnclosedFunction08.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44
//// function bar(a: number, b: string = "hello", c: typeof x = "hello") {
55
//// var v = /*1*/
66

7-
// Note: "v" questionable since we're in its initializer
8-
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c", "v"], isNewIdentifierLocation: true });
7+
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"], isNewIdentifierLocation: true });

tests/cases/fourslash/completionListInUnclosedFunction09.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@
55
//// var v = /*1*/
66
////}
77

8-
// Note: "v" questionable since we're in its initializer
9-
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c", "v"], isNewIdentifierLocation: true });
8+
verify.completions({ marker: "1", includes: ["foo", "x", "y", "z", "bar", "a", "b", "c"], isNewIdentifierLocation: true });

tests/cases/fourslash/completionListWithoutVariableinitializer.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ verify.completions({
4646

4747
verify.completions({
4848
marker: ["7"],
49-
includes: ["a", "b", "c", "d", "e"],
50-
excludes: ["fn"],
49+
includes: ["a", "b", "c", "d", "e", "fn"],
5150
});
5251

5352
verify.completions({
@@ -59,4 +58,3 @@ verify.completions({
5958
marker: ["9"],
6059
includes: ["a", "b", "c", "d", "e", "fn"],
6160
});
62-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
//// function f1(a = /*1*/, b) { }
4+
//// function f2(a = a/*2*/, b) { }
5+
//// function f3(a = a + /*3*/, b = a/*4*/, c = /*5*/) { }
6+
//// function f3(a) {
7+
//// function f4(b = /*6*/, c) { }
8+
//// }
9+
//// const f5 = (a, b = (c = /*7*/, e) => { }, d = b) => { }
10+
////
11+
//// type A1<K = /*T1*/, L> = K
12+
//// type A2<K extends /*T2*/, L> = K
13+
14+
verify.completions({
15+
marker: ["1", "2"],
16+
excludes: ["a", "b"],
17+
})
18+
verify.completions({
19+
marker: ["3"],
20+
excludes: ["a", "b"],
21+
})
22+
23+
verify.completions({
24+
marker: ["4"],
25+
includes: ["a"],
26+
})
27+
28+
verify.completions({
29+
marker: ["5"],
30+
includes: ["a", "b"],
31+
})
32+
33+
verify.completions({
34+
marker: ["6"],
35+
excludes: ["b", "c"],
36+
includes: ["a"],
37+
})
38+
39+
verify.completions({
40+
marker: ["7"],
41+
includes: ["a", "b", "d"],
42+
})
43+
44+
verify.completions({
45+
marker: ["T1", "T2"],
46+
excludes: ["K", "L"],
47+
})

0 commit comments

Comments
 (0)