mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 01:04:49 -05:00
Unused Identifier codefix better understands constructors and methods (#41555)
* Unuse Identifier codefix understands constructors Previously, it did not look for `super()` and `new this()` calls when determining whether a constructor parameter could be deleted. * better names, fix off-by-1 bug * Codefix understands super methods now too This unifies the code, changing it considerably.
This commit is contained in:
committed by
GitHub
parent
6643d97385
commit
d3abd35428
@@ -18,7 +18,7 @@ namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions(context) {
|
||||
const { errorCode, sourceFile, program } = context;
|
||||
const { errorCode, sourceFile, program, cancellationToken } = context;
|
||||
const checker = program.getTypeChecker();
|
||||
const sourceFiles = program.getSourceFiles();
|
||||
const token = getTokenAtPosition(sourceFile, context.span.start);
|
||||
@@ -36,7 +36,7 @@ namespace ts.codefix {
|
||||
return [createCodeFixAction(fixName, changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)], fixIdDeleteImports, Diagnostics.Delete_all_unused_imports)];
|
||||
}
|
||||
else if (isImport(token)) {
|
||||
const deletion = textChanges.ChangeTracker.with(context, t => tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, /*isFixAll*/ false));
|
||||
const deletion = textChanges.ChangeTracker.with(context, t => tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, program, cancellationToken, /*isFixAll*/ false));
|
||||
if (deletion.length) {
|
||||
return [createCodeFixAction(fixName, deletion, [Diagnostics.Remove_unused_declaration_for_Colon_0, token.getText(sourceFile)], fixIdDeleteImports, Diagnostics.Delete_all_unused_imports)];
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace ts.codefix {
|
||||
}
|
||||
else {
|
||||
const deletion = textChanges.ChangeTracker.with(context, t =>
|
||||
tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, /*isFixAll*/ false));
|
||||
tryDeleteDeclaration(sourceFile, token, t, checker, sourceFiles, program, cancellationToken, /*isFixAll*/ false));
|
||||
if (deletion.length) {
|
||||
const name = isComputedPropertyName(token.parent) ? token.parent : token;
|
||||
result.push(createDeleteFix(deletion, [Diagnostics.Remove_unused_declaration_for_Colon_0, name.getText(sourceFile)]));
|
||||
@@ -91,7 +91,7 @@ namespace ts.codefix {
|
||||
},
|
||||
fixIds: [fixIdPrefix, fixIdDelete, fixIdDeleteImports, fixIdInfer],
|
||||
getAllCodeActions: context => {
|
||||
const { sourceFile, program } = context;
|
||||
const { sourceFile, program, cancellationToken } = context;
|
||||
const checker = program.getTypeChecker();
|
||||
const sourceFiles = program.getSourceFiles();
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
@@ -106,7 +106,7 @@ namespace ts.codefix {
|
||||
changes.delete(sourceFile, importDecl);
|
||||
}
|
||||
else if (isImport(token)) {
|
||||
tryDeleteDeclaration(sourceFile, token, changes, checker, sourceFiles, /*isFixAll*/ true);
|
||||
tryDeleteDeclaration(sourceFile, token, changes, checker, sourceFiles, program, cancellationToken, /*isFixAll*/ true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -132,7 +132,7 @@ namespace ts.codefix {
|
||||
deleteEntireVariableStatement(changes, sourceFile, <VariableDeclarationList>token.parent);
|
||||
}
|
||||
else {
|
||||
tryDeleteDeclaration(sourceFile, token, changes, checker, sourceFiles, /*isFixAll*/ true);
|
||||
tryDeleteDeclaration(sourceFile, token, changes, checker, sourceFiles, program, cancellationToken, /*isFixAll*/ true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -217,8 +217,8 @@ namespace ts.codefix {
|
||||
return false;
|
||||
}
|
||||
|
||||
function tryDeleteDeclaration(sourceFile: SourceFile, token: Node, changes: textChanges.ChangeTracker, checker: TypeChecker, sourceFiles: readonly SourceFile[], isFixAll: boolean) {
|
||||
tryDeleteDeclarationWorker(token, changes, sourceFile, checker, sourceFiles, isFixAll);
|
||||
function tryDeleteDeclaration(sourceFile: SourceFile, token: Node, changes: textChanges.ChangeTracker, checker: TypeChecker, sourceFiles: readonly SourceFile[], program: Program, cancellationToken: CancellationToken, isFixAll: boolean) {
|
||||
tryDeleteDeclarationWorker(token, changes, sourceFile, checker, sourceFiles, program, cancellationToken, isFixAll);
|
||||
if (isIdentifier(token)) deleteAssignments(changes, sourceFile, token, checker);
|
||||
}
|
||||
|
||||
@@ -231,18 +231,18 @@ namespace ts.codefix {
|
||||
});
|
||||
}
|
||||
|
||||
function tryDeleteDeclarationWorker(token: Node, changes: textChanges.ChangeTracker, sourceFile: SourceFile, checker: TypeChecker, sourceFiles: readonly SourceFile[], isFixAll: boolean): void {
|
||||
function tryDeleteDeclarationWorker(token: Node, changes: textChanges.ChangeTracker, sourceFile: SourceFile, checker: TypeChecker, sourceFiles: readonly SourceFile[], program: Program, cancellationToken: CancellationToken, isFixAll: boolean): void {
|
||||
const { parent } = token;
|
||||
if (isParameter(parent)) {
|
||||
tryDeleteParameter(changes, sourceFile, parent, checker, sourceFiles, isFixAll);
|
||||
tryDeleteParameter(changes, sourceFile, parent, checker, sourceFiles, program, cancellationToken, isFixAll);
|
||||
}
|
||||
else {
|
||||
changes.delete(sourceFile, isImportClause(parent) ? token : isComputedPropertyName(parent) ? parent.parent : parent);
|
||||
}
|
||||
}
|
||||
|
||||
function tryDeleteParameter(changes: textChanges.ChangeTracker, sourceFile: SourceFile, p: ParameterDeclaration, checker: TypeChecker, sourceFiles: readonly SourceFile[], isFixAll = false): void {
|
||||
if (mayDeleteParameter(checker, sourceFile, p, isFixAll)) {
|
||||
function tryDeleteParameter(changes: textChanges.ChangeTracker, sourceFile: SourceFile, p: ParameterDeclaration, checker: TypeChecker, sourceFiles: readonly SourceFile[], program: Program, cancellationToken: CancellationToken, isFixAll = false): void {
|
||||
if (mayDeleteParameter(checker, sourceFile, p, sourceFiles, program, cancellationToken, isFixAll)) {
|
||||
if (p.modifiers && p.modifiers.length > 0 &&
|
||||
(!isIdentifier(p.name) || FindAllReferences.Core.isSymbolReferencedInFile(p.name, checker, sourceFile))) {
|
||||
p.modifiers.forEach(modifier => changes.deleteModifier(sourceFile, modifier));
|
||||
@@ -254,15 +254,36 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
function mayDeleteParameter(checker: TypeChecker, sourceFile: SourceFile, parameter: ParameterDeclaration, isFixAll: boolean): boolean {
|
||||
function mayDeleteParameter(checker: TypeChecker, sourceFile: SourceFile, parameter: ParameterDeclaration, sourceFiles: readonly SourceFile[], program: Program, cancellationToken: CancellationToken, isFixAll: boolean): boolean {
|
||||
const { parent } = parameter;
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
// Don't remove a parameter if this overrides something.
|
||||
const symbol = checker.getSymbolAtLocation(parent.name)!;
|
||||
if (isMemberSymbolInBaseType(symbol, checker)) return false;
|
||||
// falls through
|
||||
case SyntaxKind.Constructor:
|
||||
const index = parent.parameters.indexOf(parameter);
|
||||
const referent = isMethodDeclaration(parent) ? parent.name : parent;
|
||||
const entries = FindAllReferences.Core.getReferencedSymbolsForNode(parent.pos, referent, program, sourceFiles, cancellationToken);
|
||||
if (entries) {
|
||||
for (const entry of entries) {
|
||||
for (const reference of entry.references) {
|
||||
if (reference.kind === FindAllReferences.EntryKind.Node) {
|
||||
// argument in super(...)
|
||||
const isSuperCall = isSuperKeyword(reference.node)
|
||||
&& isCallExpression(reference.node.parent)
|
||||
&& reference.node.parent.arguments.length > index;
|
||||
// argument in super.m(...)
|
||||
const isSuperMethodCall = isPropertyAccessExpression(reference.node.parent)
|
||||
&& isSuperKeyword(reference.node.parent.expression)
|
||||
&& isCallExpression(reference.node.parent.parent)
|
||||
&& reference.node.parent.parent.arguments.length > index;
|
||||
// parameter in overridden or overriding method
|
||||
const isOverriddenMethod = (isMethodDeclaration(reference.node.parent) || isMethodSignature(reference.node.parent))
|
||||
&& reference.node.parent !== parameter.parent
|
||||
&& reference.node.parent.parameters.length > index;
|
||||
if (isSuperCall || isSuperMethodCall || isOverriddenMethod) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case SyntaxKind.FunctionDeclaration: {
|
||||
if (parent.name && isCallbackLike(checker, sourceFile, parent.name)) {
|
||||
|
||||
Reference in New Issue
Block a user