@@ -13,21 +13,22 @@ namespace ts.codefix {
13
13
registerCodeFix ( {
14
14
errorCodes,
15
15
getCodeActions ( context ) {
16
- const { errorCode, sourceFile } = context ;
16
+ const { errorCode, sourceFile, program } = context ;
17
+ const checker = program . getTypeChecker ( ) ;
17
18
const importDecl = tryGetFullImport ( sourceFile , context . span . start ) ;
18
19
if ( importDecl ) {
19
20
const changes = textChanges . ChangeTracker . with ( context , t => t . deleteNode ( sourceFile , importDecl ) ) ;
20
21
return [ createCodeFixAction ( fixName , changes , [ Diagnostics . Remove_import_from_0 , showModuleSpecifier ( importDecl ) ] , fixIdDelete , Diagnostics . Delete_all_unused_declarations ) ] ;
21
22
}
22
- const delDestructure = textChanges . ChangeTracker . with ( context , t => tryDeleteFullDestructure ( t , sourceFile , context . span . start , /*deleted*/ undefined ) ) ;
23
+ const delDestructure = textChanges . ChangeTracker . with ( context , t => tryDeleteFullDestructure ( t , sourceFile , context . span . start , /*deleted*/ undefined , checker ) ) ;
23
24
if ( delDestructure . length ) {
24
25
return [ createCodeFixAction ( fixName , delDestructure , Diagnostics . Remove_destructuring , fixIdDelete , Diagnostics . Delete_all_unused_declarations ) ] ;
25
26
}
26
27
27
28
const token = getToken ( sourceFile , textSpanEnd ( context . span ) ) ;
28
29
const result : CodeFixAction [ ] = [ ] ;
29
30
30
- const deletion = textChanges . ChangeTracker . with ( context , t => tryDeleteDeclaration ( t , sourceFile , token , /*deleted*/ undefined ) ) ;
31
+ const deletion = textChanges . ChangeTracker . with ( context , t => tryDeleteDeclaration ( t , sourceFile , token , /*deleted*/ undefined , checker ) ) ;
31
32
if ( deletion . length ) {
32
33
result . push ( createCodeFixAction ( fixName , deletion , [ Diagnostics . Remove_declaration_for_Colon_0 , token . getText ( sourceFile ) ] , fixIdDelete , Diagnostics . Delete_all_unused_declarations ) ) ;
33
34
}
@@ -43,8 +44,9 @@ namespace ts.codefix {
43
44
getAllCodeActions : context => {
44
45
// Track a set of deleted nodes that may be ancestors of other marked for deletion -- only delete the ancestors.
45
46
const deleted = new NodeSet ( ) ;
47
+ const { sourceFile, program } = context ;
48
+ const checker = program . getTypeChecker ( ) ;
46
49
return codeFixAll ( context , errorCodes , ( changes , diag ) => {
47
- const { sourceFile } = context ;
48
50
const token = findPrecedingToken ( textSpanEnd ( diag ) , diag . file ! ) ;
49
51
switch ( context . fixId ) {
50
52
case fixIdPrefix :
@@ -61,8 +63,8 @@ namespace ts.codefix {
61
63
changes . deleteNode ( sourceFile , importDecl ) ;
62
64
}
63
65
else {
64
- if ( ! tryDeleteFullDestructure ( changes , sourceFile , diag . start ! , deleted ) ) {
65
- tryDeleteDeclaration ( changes , sourceFile , token , deleted ) ;
66
+ if ( ! tryDeleteFullDestructure ( changes , sourceFile , diag . start ! , deleted , checker ) ) {
67
+ tryDeleteDeclaration ( changes , sourceFile , token , deleted , checker ) ;
66
68
}
67
69
}
68
70
break ;
@@ -79,7 +81,7 @@ namespace ts.codefix {
79
81
return startToken . kind === SyntaxKind . ImportKeyword ? tryCast ( startToken . parent , isImportDeclaration ) : undefined ;
80
82
}
81
83
82
- function tryDeleteFullDestructure ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , pos : number , deletedAncestors : NodeSet | undefined ) : boolean {
84
+ function tryDeleteFullDestructure ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , pos : number , deletedAncestors : NodeSet | undefined , checker : TypeChecker ) : boolean {
83
85
const startToken = getTokenAtPosition ( sourceFile , pos , /*includeJsDocComment*/ false ) ;
84
86
if ( startToken . kind !== SyntaxKind . OpenBraceToken || ! isObjectBindingPattern ( startToken . parent ) ) return false ;
85
87
const decl = startToken . parent . parent ;
@@ -88,6 +90,7 @@ namespace ts.codefix {
88
90
tryDeleteVariableDeclaration ( changes , sourceFile , decl , deletedAncestors ) ;
89
91
break ;
90
92
case SyntaxKind . Parameter :
93
+ if ( ! mayDeleteParameter ( decl , checker ) ) break ;
91
94
if ( deletedAncestors ) deletedAncestors . add ( decl ) ;
92
95
changes . deleteNodeInList ( sourceFile , decl ) ;
93
96
break ;
@@ -130,10 +133,10 @@ namespace ts.codefix {
130
133
return false ;
131
134
}
132
135
133
- function tryDeleteDeclaration ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , token : Node , deletedAncestors : NodeSet | undefined ) : void {
136
+ function tryDeleteDeclaration ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , token : Node , deletedAncestors : NodeSet | undefined , checker : TypeChecker ) : void {
134
137
switch ( token . kind ) {
135
138
case SyntaxKind . Identifier :
136
- tryDeleteIdentifier ( changes , sourceFile , < Identifier > token , deletedAncestors ) ;
139
+ tryDeleteIdentifier ( changes , sourceFile , < Identifier > token , deletedAncestors , checker ) ;
137
140
break ;
138
141
case SyntaxKind . PropertyDeclaration :
139
142
case SyntaxKind . NamespaceImport :
@@ -156,7 +159,7 @@ namespace ts.codefix {
156
159
}
157
160
}
158
161
159
- function tryDeleteIdentifier ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , identifier : Identifier , deletedAncestors : NodeSet | undefined ) : void {
162
+ function tryDeleteIdentifier ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , identifier : Identifier , deletedAncestors : NodeSet | undefined , checker : TypeChecker ) : void {
160
163
const parent = identifier . parent ;
161
164
switch ( parent . kind ) {
162
165
case SyntaxKind . VariableDeclaration :
@@ -180,11 +183,8 @@ namespace ts.codefix {
180
183
break ;
181
184
182
185
case SyntaxKind . Parameter :
186
+ if ( ! mayDeleteParameter ( parent as ParameterDeclaration , checker ) ) break ;
183
187
const oldFunction = parent . parent ;
184
- if ( isSetAccessor ( oldFunction ) ) {
185
- // Setter must have a parameter
186
- break ;
187
- }
188
188
189
189
if ( isArrowFunction ( oldFunction ) && oldFunction . parameters . length === 1 ) {
190
190
// Lambdas with exactly one parameter are special because, after removal, there
@@ -334,6 +334,33 @@ namespace ts.codefix {
334
334
}
335
335
}
336
336
337
+ function mayDeleteParameter ( p : ParameterDeclaration , checker : TypeChecker ) {
338
+ const parent = p . parent ;
339
+ switch ( parent . kind ) {
340
+ case SyntaxKind . FunctionDeclaration :
341
+ return true ;
342
+
343
+ case SyntaxKind . FunctionExpression :
344
+ case SyntaxKind . ArrowFunction :
345
+ // Can't remove a non-last parameter in a callback. (Can if future parameters are also unused.)
346
+ const index = parent . parameters . indexOf ( p ) ;
347
+ Debug . assert ( index !== - 1 ) ;
348
+ return parent . parameters . slice ( index ) . every ( p => p . name . kind === SyntaxKind . Identifier && ! p . symbol . isReferenced ) || ! checker . getContextualType ( parent ) ;
349
+
350
+ case SyntaxKind . SetAccessor :
351
+ // Setter must have a parameter
352
+ return false ;
353
+
354
+ case SyntaxKind . MethodDeclaration :
355
+ // Don't remove a parameter if this overrides something
356
+ const symbol = checker . getSymbolAtLocation ( parent . name ) ;
357
+ return ! isMemberSymbolInBaseType ( symbol , checker ) ;
358
+
359
+ default :
360
+ return Debug . failBadSyntaxKind ( parent ) ;
361
+ }
362
+ }
363
+
337
364
class NodeSet {
338
365
private map = createMap < Node > ( ) ;
339
366
0 commit comments