mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
Separate delete-all-imports from other delete-all (#41105)
This fixes the first part of #32196
This commit is contained in:
parent
53bc006752
commit
ae81add083
@ -5959,6 +5959,10 @@
|
||||
"category": "Message",
|
||||
"code": 95144
|
||||
},
|
||||
"Delete all unused imports": {
|
||||
"category": "Message",
|
||||
"code": 95145
|
||||
},
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
|
||||
@ -3,6 +3,7 @@ namespace ts.codefix {
|
||||
const fixName = "unusedIdentifier";
|
||||
const fixIdPrefix = "unusedIdentifier_prefix";
|
||||
const fixIdDelete = "unusedIdentifier_delete";
|
||||
const fixIdDeleteImports = "unusedIdentifier_deleteImports";
|
||||
const fixIdInfer = "unusedIdentifier_infer";
|
||||
const errorCodes = [
|
||||
Diagnostics._0_is_declared_but_its_value_is_never_read.code,
|
||||
@ -32,7 +33,13 @@ namespace ts.codefix {
|
||||
const importDecl = tryGetFullImport(token);
|
||||
if (importDecl) {
|
||||
const changes = textChanges.ChangeTracker.with(context, t => t.delete(sourceFile, importDecl));
|
||||
return [createDeleteFix(changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)])];
|
||||
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));
|
||||
if (deletion.length) {
|
||||
return [createCodeFixAction(fixName, deletion, [Diagnostics.Remove_unused_declaration_for_Colon_0, token.getText(sourceFile)], fixIdDeleteImports, Diagnostics.Delete_all_unused_imports)];
|
||||
}
|
||||
}
|
||||
|
||||
if (isObjectBindingPattern(token.parent)) {
|
||||
@ -82,7 +89,7 @@ namespace ts.codefix {
|
||||
|
||||
return result;
|
||||
},
|
||||
fixIds: [fixIdPrefix, fixIdDelete, fixIdInfer],
|
||||
fixIds: [fixIdPrefix, fixIdDelete, fixIdDeleteImports, fixIdInfer],
|
||||
getAllCodeActions: context => {
|
||||
const { sourceFile, program } = context;
|
||||
const checker = program.getTypeChecker();
|
||||
@ -93,14 +100,20 @@ namespace ts.codefix {
|
||||
case fixIdPrefix:
|
||||
tryPrefixDeclaration(changes, diag.code, sourceFile, token);
|
||||
break;
|
||||
case fixIdDelete: {
|
||||
if (token.kind === SyntaxKind.InferKeyword) {
|
||||
break; // Can't delete
|
||||
}
|
||||
case fixIdDeleteImports: {
|
||||
const importDecl = tryGetFullImport(token);
|
||||
if (importDecl) {
|
||||
changes.delete(sourceFile, importDecl);
|
||||
}
|
||||
else if (isImport(token)) {
|
||||
tryDeleteDeclaration(sourceFile, token, changes, checker, sourceFiles, /*isFixAll*/ true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case fixIdDelete: {
|
||||
if (token.kind === SyntaxKind.InferKeyword || isImport(token)) {
|
||||
break; // Can't delete
|
||||
}
|
||||
else if (isJSDocTemplateTag(token)) {
|
||||
changes.delete(sourceFile, token);
|
||||
}
|
||||
@ -147,6 +160,11 @@ namespace ts.codefix {
|
||||
changes.delete(sourceFile, Debug.checkDefined(cast(token.parent, isDeclarationWithTypeParameterChildren).typeParameters, "The type parameter to delete should exist"));
|
||||
}
|
||||
|
||||
function isImport(token: Node) {
|
||||
return token.kind === SyntaxKind.ImportKeyword
|
||||
|| token.kind === SyntaxKind.Identifier && (token.parent.kind === SyntaxKind.ImportSpecifier || token.parent.kind === SyntaxKind.ImportClause);
|
||||
}
|
||||
|
||||
// Sometimes the diagnostic span is an entire ImportDeclaration, so we should remove the whole thing.
|
||||
function tryGetFullImport(token: Node): ImportDeclaration | undefined {
|
||||
return token.kind === SyntaxKind.ImportKeyword ? tryCast(token.parent, isImportDeclaration) : undefined;
|
||||
|
||||
@ -61,8 +61,10 @@ verify.codeFixAll({
|
||||
fixId: "unusedIdentifier_delete",
|
||||
fixAllDescription: ts.Diagnostics.Delete_all_unused_declarations.message,
|
||||
newFileContent:
|
||||
`import { used1 } from "foo";
|
||||
import { used2 } from "foo";
|
||||
`import d from "foo";
|
||||
import d2, { used1 } from "foo";
|
||||
import { x } from "foo";
|
||||
import { x2, used2 } from "foo";
|
||||
used1; used2;
|
||||
|
||||
function f() {
|
||||
|
||||
@ -0,0 +1,115 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @noUnusedLocals: true
|
||||
// @noUnusedParameters: true
|
||||
|
||||
////import d from "foo";
|
||||
////import d2, { used1 } from "foo";
|
||||
////import { x } from "foo";
|
||||
////import { x2, used2 } from "foo";
|
||||
////used1; used2;
|
||||
////
|
||||
////function f(a, b) {
|
||||
//// const x = 0;
|
||||
////}
|
||||
////function g(a, b, c) { return a; }
|
||||
////f; g;
|
||||
////
|
||||
////interface I {
|
||||
//// m(x: number): void;
|
||||
////}
|
||||
////
|
||||
////class C implements I {
|
||||
//// m(x: number): void {} // Does not remove 'x', which is inherited
|
||||
//// n(x: number): void {}
|
||||
//// private ["o"](): void {}
|
||||
////}
|
||||
////C;
|
||||
////
|
||||
////declare function takesCb(cb: (x: number, y: string) => void): void;
|
||||
////takesCb((x, y) => {});
|
||||
////takesCb((x, y) => { x; });
|
||||
////takesCb((x, y) => { y; });
|
||||
////
|
||||
////function fn1(x: number, y: string): void {}
|
||||
////takesCb(fn1);
|
||||
////
|
||||
////function fn2(x: number, y: string): void { x; }
|
||||
////takesCb(fn2);
|
||||
////
|
||||
////function fn3(x: number, y: string): void { y; }
|
||||
////takesCb(fn3);
|
||||
////
|
||||
////x => {
|
||||
//// const y = 0;
|
||||
////};
|
||||
////
|
||||
////{
|
||||
//// let a, b;
|
||||
////}
|
||||
////for (let i = 0, j = 0; ;) {}
|
||||
////for (const x of []) {}
|
||||
////for (const y in {}) {}
|
||||
////
|
||||
////export type First<T, U> = T;
|
||||
////export interface ISecond<T, U> { u: U; }
|
||||
////export const cls = class<T, U> { u: U; };
|
||||
////export class Ctu<T, U> {}
|
||||
////export type Length<T> = T extends ArrayLike<infer U> ? number : never; // Not affected, can't delete
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "unusedIdentifier_deleteImports",
|
||||
fixAllDescription: ts.Diagnostics.Delete_all_unused_imports.message,
|
||||
newFileContent:
|
||||
`import { used1 } from "foo";
|
||||
import { used2 } from "foo";
|
||||
used1; used2;
|
||||
|
||||
function f(a, b) {
|
||||
const x = 0;
|
||||
}
|
||||
function g(a, b, c) { return a; }
|
||||
f; g;
|
||||
|
||||
interface I {
|
||||
m(x: number): void;
|
||||
}
|
||||
|
||||
class C implements I {
|
||||
m(x: number): void {} // Does not remove 'x', which is inherited
|
||||
n(x: number): void {}
|
||||
private ["o"](): void {}
|
||||
}
|
||||
C;
|
||||
|
||||
declare function takesCb(cb: (x: number, y: string) => void): void;
|
||||
takesCb((x, y) => {});
|
||||
takesCb((x, y) => { x; });
|
||||
takesCb((x, y) => { y; });
|
||||
|
||||
function fn1(x: number, y: string): void {}
|
||||
takesCb(fn1);
|
||||
|
||||
function fn2(x: number, y: string): void { x; }
|
||||
takesCb(fn2);
|
||||
|
||||
function fn3(x: number, y: string): void { y; }
|
||||
takesCb(fn3);
|
||||
|
||||
x => {
|
||||
const y = 0;
|
||||
};
|
||||
|
||||
{
|
||||
let a, b;
|
||||
}
|
||||
for (let i = 0, j = 0; ;) {}
|
||||
for (const x of []) {}
|
||||
for (const y in {}) {}
|
||||
|
||||
export type First<T, U> = T;
|
||||
export interface ISecond<T, U> { u: U; }
|
||||
export const cls = class<T, U> { u: U; };
|
||||
export class Ctu<T, U> {}
|
||||
export type Length<T> = T extends ArrayLike<infer U> ? number : never; // Not affected, can't delete`,
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user