Skip to content

Commit 4584d6d

Browse files
authored
fix(39676): skip removing unused parameters for functions used as callback references (microsoft#40299)
1 parent 2b0278a commit 4584d6d

File tree

4 files changed

+58
-19
lines changed

4 files changed

+58
-19
lines changed

src/services/codefixes/fixUnusedIdentifier.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,10 @@ namespace ts.codefix {
224224
}
225225

226226
function tryDeleteParameter(changes: textChanges.ChangeTracker, sourceFile: SourceFile, p: ParameterDeclaration, checker: TypeChecker, sourceFiles: readonly SourceFile[], isFixAll = false): void {
227-
if (mayDeleteParameter(p, checker, isFixAll)) {
228-
if (p.modifiers && p.modifiers.length > 0
229-
&& (!isIdentifier(p.name) || FindAllReferences.Core.isSymbolReferencedInFile(p.name, checker, sourceFile))) {
230-
p.modifiers.forEach(modifier => {
231-
changes.deleteModifier(sourceFile, modifier);
232-
});
227+
if (mayDeleteParameter(checker, sourceFile, p, isFixAll)) {
228+
if (p.modifiers && p.modifiers.length > 0 &&
229+
(!isIdentifier(p.name) || FindAllReferences.Core.isSymbolReferencedInFile(p.name, checker, sourceFile))) {
230+
p.modifiers.forEach(modifier => changes.deleteModifier(sourceFile, modifier));
233231
}
234232
else {
235233
changes.delete(sourceFile, p);
@@ -238,29 +236,26 @@ namespace ts.codefix {
238236
}
239237
}
240238

241-
function mayDeleteParameter(p: ParameterDeclaration, checker: TypeChecker, isFixAll: boolean): boolean {
242-
const { parent } = p;
239+
function mayDeleteParameter(checker: TypeChecker, sourceFile: SourceFile, parameter: ParameterDeclaration, isFixAll: boolean): boolean {
240+
const { parent } = parameter;
243241
switch (parent.kind) {
244242
case SyntaxKind.MethodDeclaration:
245243
// Don't remove a parameter if this overrides something.
246244
const symbol = checker.getSymbolAtLocation(parent.name)!;
247245
if (isMemberSymbolInBaseType(symbol, checker)) return false;
248246
// falls through
249-
250247
case SyntaxKind.Constructor:
251-
case SyntaxKind.FunctionDeclaration:
252248
return true;
253-
249+
case SyntaxKind.FunctionDeclaration: {
250+
if (parent.name && isCallbackLike(checker, sourceFile, parent.name)) {
251+
return isLastParameter(parent, parameter, isFixAll);
252+
}
253+
return true;
254+
}
254255
case SyntaxKind.FunctionExpression:
255-
case SyntaxKind.ArrowFunction: {
256+
case SyntaxKind.ArrowFunction:
256257
// Can't remove a non-last parameter in a callback. Can remove a parameter in code-fix-all if future parameters are also unused.
257-
const { parameters } = parent;
258-
const index = parameters.indexOf(p);
259-
Debug.assert(index !== -1, "The parameter should already be in the list");
260-
return isFixAll
261-
? parameters.slice(index + 1).every(p => p.name.kind === SyntaxKind.Identifier && !p.symbol.isReferenced)
262-
: index === parameters.length - 1;
263-
}
258+
return isLastParameter(parent, parameter, isFixAll);
264259

265260
case SyntaxKind.SetAccessor:
266261
// Setter must have a parameter
@@ -279,4 +274,18 @@ namespace ts.codefix {
279274
}
280275
});
281276
}
277+
278+
function isCallbackLike(checker: TypeChecker, sourceFile: SourceFile, name: Identifier): boolean {
279+
return !!FindAllReferences.Core.eachSymbolReferenceInFile(name, checker, sourceFile, reference =>
280+
isIdentifier(reference) && isCallExpression(reference.parent) && reference.parent.arguments.indexOf(reference) >= 0);
281+
}
282+
283+
function isLastParameter(func: FunctionLikeDeclaration, parameter: ParameterDeclaration, isFixAll: boolean): boolean {
284+
const parameters = func.parameters;
285+
const index = parameters.indexOf(parameter);
286+
Debug.assert(index !== -1, "The parameter should already be in the list");
287+
return isFixAll ?
288+
parameters.slice(index + 1).every(p => isIdentifier(p.name) && !p.symbol.isReferenced) :
289+
index === parameters.length - 1;
290+
}
282291
}

tests/cases/fourslash/codeFixUnusedIdentifier_all_delete.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@
3131
////takesCb((x, y) => { x; });
3232
////takesCb((x, y) => { y; });
3333
////
34+
////function fn1(x: number, y: string): void {}
35+
////takesCb(fn1);
36+
////
37+
////function fn2(x: number, y: string): void { x; }
38+
////takesCb(fn2);
39+
////
40+
////function fn3(x: number, y: string): void { y; }
41+
////takesCb(fn3);
42+
////
3443
////x => {
3544
//// const y = 0;
3645
////};
@@ -76,6 +85,15 @@ takesCb(() => {});
7685
takesCb((x) => { x; });
7786
takesCb((x, y) => { y; });
7887
88+
function fn1(): void {}
89+
takesCb(fn1);
90+
91+
function fn2(x: number): void { x; }
92+
takesCb(fn2);
93+
94+
function fn3(x: number, y: string): void { y; }
95+
takesCb(fn3);
96+
7997
() => {
8098
};
8199
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @noUnusedParameters: true
4+
5+
////declare function foo(cb: (x: number, y: string) => void): void;
6+
////function fn(x: number, y: number): any {
7+
//// return y;
8+
////}
9+
////foo(fn);
10+
11+
// No codefix to remove a non-last parameter in a callback
12+
verify.codeFixAvailable([{ description: "Prefix 'x' with an underscore" }]);

0 commit comments

Comments
 (0)