Codefix for removing Unused Identifiers #11546
Conversation
| const start = context.span.start; | ||
| const token = getTokenAtPosition(sourceFile, start); | ||
|
|
||
| if (token.kind === ts.SyntaxKind.Identifier) { |
There was a problem hiding this comment.
please refactor this into a switch statement.
| return createCodeFix("{}", token.parent.pos, token.parent.end - token.parent.pos); | ||
| } | ||
|
|
||
| if (token.parent.kind === SyntaxKind.ImportEqualsDeclaration) { |
There was a problem hiding this comment.
what does this mean. this is import m = require("mod");, you want to remove the whole declaration, and not just the identifier.
There was a problem hiding this comment.
Also, please add a test for this.
There was a problem hiding this comment.
This is import a = A
There was a problem hiding this comment.
Tests are here, changed this to remove the whole line.
| } | ||
|
|
||
| if (token.kind === SyntaxKind.AsteriskToken && token.parent.kind === SyntaxKind.NamespaceImport) { | ||
| return createCodeFix("{}", token.parent.pos, token.parent.end - token.parent.pos); |
There was a problem hiding this comment.
do not think this is correct either. you need to check if this is the only import for this module, remove it whole sale, if not, remove whole import declaration.
also please add tests for the deffrent combinations of import:
import * as ns from "mod";
import d from "mod"
import {a, b} from "mod" // where both are unused
import {a, b} from "mod" // where only one is unused
import d, * as ns from "mod"; // where both are unused
import d, * as ns from "mod"; // where one of them is unsued, but the other is
import d, {a, b} from "mod"; // where one of a,b, or d is unused, but other are not
import d, {a, b} from "mod"; // where both a and b are unused, but d is not
import d, {a, b} from "mod"; // where only d is unsued
import d, {a, b} from "mod"; // where all are unused
import m = require("mod");
import m = n.a;There was a problem hiding this comment.
Since the cases where more than 1 import are unused, will result in 2 errors, and 2 fixes; so those would be identical to the single unused import.
All other cases have test cases.
| //// [|constructor(private p1: string, public p2: boolean, public p3: any, p5)|] { p5; } | ||
| //// } | ||
|
|
||
| verify.codeFixAtPosition("constructor(public p2: boolean, public p3: any, p5)"); No newline at end of file |
There was a problem hiding this comment.
please add another test with private readonly and another one with private is not in the first position.
| const elements = namedImports.elements; | ||
| if (elements.length === 1) { | ||
| // Only 1 import and it is unused. So the entire line could be removed. | ||
| return createCodeFix("", token.parent.pos, token.parent.end - token.parent.pos); |
There was a problem hiding this comment.
this is not removing the whole line? do you mean namedImports.parent? I think you need to handle all cases. i have listed them below, also please add tests for each combination.
| } | ||
|
|
||
| if (token.parent.kind === SyntaxKind.TypeParameter) { | ||
| const typeParameters = (<ClassDeclaration>token.parent.parent).typeParameters; |
There was a problem hiding this comment.
why ClassDeclaraton? that can be function, interface declaration, method, type alias, arrow function, etc.. please add tests for these.
There was a problem hiding this comment.
Used DeclWIthTypeParam, tests were already there.
| } | ||
|
|
||
| if (token.parent.kind === ts.SyntaxKind.Parameter) { | ||
| const functionDeclaration = <FunctionDeclaration>token.parent.parent; |
There was a problem hiding this comment.
this is the same logic as above, please merge these two branches.
| token.parent.kind === SyntaxKind.MethodDeclaration || | ||
| token.parent.kind === SyntaxKind.ModuleDeclaration || | ||
| token.parent.kind === SyntaxKind.PropertyDeclaration || | ||
| token.parent.kind === SyntaxKind.ArrowFunction) { |
There was a problem hiding this comment.
how can an arrowFunction have an identifier as a name?
consider changing this to:
if (isDeclaration(token.parent) && (<Declaration>parent).name === token) {
return createCodeFix("", token.parent.pos, token.parent.end - token.parent.pos);
}There was a problem hiding this comment.
Also please add handeling for computed properties with non-identifier names:
class C {
private ["string"] (){}
private [Symbol.Iterator]() {}
}| else if (token.parent.parent.parent.kind === SyntaxKind.ForInStatement) { | ||
| const forInStatement = <ForInStatement>token.parent.parent.parent; | ||
| const initializer = <VariableDeclarationList>forInStatement.initializer; | ||
| return createCodeFix("{}", initializer.declarations[0].pos, initializer.declarations[0].end - initializer.declarations[0].pos); |
There was a problem hiding this comment.
this is not valid for..in statement. we should switch it x to _x and add special casing for _x to be not show the error. alternatively do nothing here, which would be my recommendation.
There was a problem hiding this comment.
Doing nothing, modified test to make sure.
| } | ||
| else { | ||
| const variableStatement = <VariableStatement>token.parent.parent.parent; | ||
| if (variableStatement.declarationList.declarations.length === 1) { |
There was a problem hiding this comment.
consider sharing the logic here with the other list removal cases.
| if (token.parent.parent.parent.kind === SyntaxKind.ForStatement) { | ||
| const forStatement = <ForStatement>token.parent.parent.parent; | ||
| const initializer = <VariableDeclarationList>forStatement.initializer; | ||
| if (initializer.declarations.length === 1) { |
There was a problem hiding this comment.
consider merging this into a list removal logic for all of the cases below.
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
what about binding patterns? can you add tests for these cases. both for parameters and variable declarations.
| token.parent.kind === SyntaxKind.MethodDeclaration || | ||
| token.parent.kind === SyntaxKind.ModuleDeclaration || | ||
| token.parent.kind === SyntaxKind.PropertyDeclaration || | ||
| token.parent.kind === SyntaxKind.ArrowFunction) { |
There was a problem hiding this comment.
Also please add handeling for computed properties with non-identifier names:
class C {
private ["string"] (){}
private [Symbol.Iterator]() {}
}| //// x++; | ||
| ////} | ||
|
|
||
| verify.codeFixAtPosition("var x;", 6133); |
There was a problem hiding this comment.
No error codes in fourslash tests
| break; | ||
|
|
||
| case SyntaxKind.ForInStatement: | ||
| // There is no valid fix in the case of: |
There was a problem hiding this comment.
rename the variable to start with _.
There was a problem hiding this comment.
Out of scope, will add for 2nd iteration of this fix
| } | ||
| } | ||
|
|
||
| case SyntaxKind.FunctionDeclaration: |
There was a problem hiding this comment.
namespace, type alais, and enum as well
There was a problem hiding this comment.
i would move this to the default clause and use isDeclarationName
| case SyntaxKind.ImportSpecifier: | ||
| const namedImports = <NamedImports>token.parent.parent; | ||
| const elements = namedImports.elements; | ||
| if (elements.length === 1) { |
There was a problem hiding this comment.
if no other imports in this list we need to remove the whole module import.
| } | ||
| break; | ||
|
|
||
| case SyntaxKind.PrivateKeyword: |
| const token = getTokenAtPosition(sourceFile, start); | ||
|
|
||
| switch (token.kind) { | ||
| case ts.SyntaxKind.Identifier: |
There was a problem hiding this comment.
binding patterns are not handled anywhere:
function f({a, b}) {}There was a problem hiding this comment.
outside of scope, I think we can do a better job when we add the change signature refactoring
| const token = getTokenAtPosition(sourceFile, start); | ||
|
|
||
| switch (token.kind) { | ||
| case ts.SyntaxKind.Identifier: |
There was a problem hiding this comment.
Also please add handeling for computed properties with non-identifier names:
class C {
private ["string"] (){}
private [Symbol.Iterator]() {}
}|
|
||
| // @noUnusedLocals: true | ||
| // @Filename: file2.ts | ||
| //// [| import f1, * as s from "./file1"; |] |
There was a problem hiding this comment.
Please add a test with both unused.
There was a problem hiding this comment.
That generates 2 errors and 2 fixes. So not applicable
| case SyntaxKind.PropertyDeclaration: | ||
| return createCodeFix("", token.parent.pos, token.parent.end - token.parent.pos); | ||
|
|
||
| case SyntaxKind.AsteriskToken: |
Quick fix for removing unused variables and unused locals.
Current known limitations:
for ... inloop