fix(32297): add quick-fix action to delete parameter destructuring elements (#38764)

This commit is contained in:
Alexander T
2020-06-30 11:44:47 -07:00
committed by GitHub
parent a812a7449d
commit 8b6a88700e
13 changed files with 86 additions and 47 deletions
+8 -4
View File
@@ -5155,10 +5155,6 @@
"category": "Message",
"code": 90008
},
"Remove destructuring": {
"category": "Message",
"code": 90009
},
"Remove variable statement": {
"category": "Message",
"code": 90010
@@ -5275,6 +5271,14 @@
"category": "Message",
"code": 90038
},
"Remove unused destructuring declaration": {
"category": "Message",
"code": 90039
},
"Remove unused declarations for: '{0}'": {
"category": "Message",
"code": 90041
},
"Declare a private field named '{0}'.": {
"category": "Message",
"code": 90053
+47 -29
View File
@@ -34,18 +34,33 @@ namespace ts.codefix {
const changes = textChanges.ChangeTracker.with(context, t => t.delete(sourceFile, importDecl));
return [createDeleteFix(changes, [Diagnostics.Remove_import_from_0, showModuleSpecifier(importDecl)])];
}
const delDestructure = textChanges.ChangeTracker.with(context, t =>
tryDeleteFullDestructure(token, t, sourceFile, checker, sourceFiles, /*isFixAll*/ false));
if (delDestructure.length) {
return [createDeleteFix(delDestructure, Diagnostics.Remove_destructuring)];
if (isObjectBindingPattern(token.parent)) {
if (isParameter(token.parent.parent)) {
const elements = token.parent.elements;
const diagnostic: [DiagnosticMessage, string] = [
elements.length > 1 ? Diagnostics.Remove_unused_declarations_for_Colon_0 : Diagnostics.Remove_unused_declaration_for_Colon_0,
map(elements, e => e.getText(sourceFile)).join(", ")
];
return [
createDeleteFix(textChanges.ChangeTracker.with(context, t =>
deleteDestructuringElements(t, sourceFile, <ObjectBindingPattern>token.parent)), diagnostic)
];
}
return [
createDeleteFix(textChanges.ChangeTracker.with(context, t =>
t.delete(sourceFile, token.parent.parent)), Diagnostics.Remove_unused_destructuring_declaration)
];
}
const delVar = textChanges.ChangeTracker.with(context, t => tryDeleteFullVariableStatement(sourceFile, token, t));
if (delVar.length) {
return [createDeleteFix(delVar, Diagnostics.Remove_variable_statement)];
if (canDeleteEntireVariableStatement(sourceFile, token)) {
return [
createDeleteFix(textChanges.ChangeTracker.with(context, t =>
deleteEntireVariableStatement(t, sourceFile, <VariableDeclarationList>token.parent)), Diagnostics.Remove_variable_statement)
];
}
const result: CodeFixAction[] = [];
if (token.kind === SyntaxKind.InferKeyword) {
const changes = textChanges.ChangeTracker.with(context, t => changeInferToUnknown(t, sourceFile, token));
const name = cast(token.parent, isInferTypeNode).typeParameter.name.text;
@@ -79,7 +94,9 @@ namespace ts.codefix {
tryPrefixDeclaration(changes, diag.code, sourceFile, token);
break;
case fixIdDelete: {
if (token.kind === SyntaxKind.InferKeyword) break; // Can't delete
if (token.kind === SyntaxKind.InferKeyword) {
break; // Can't delete
}
const importDecl = tryGetFullImport(token);
if (importDecl) {
changes.delete(sourceFile, importDecl);
@@ -90,8 +107,18 @@ namespace ts.codefix {
else if (token.kind === SyntaxKind.LessThanToken) {
deleteTypeParameters(changes, sourceFile, token);
}
else if (!tryDeleteFullDestructure(token, changes, sourceFile, checker, sourceFiles, /*isFixAll*/ true) &&
!tryDeleteFullVariableStatement(sourceFile, token, changes)) {
else if (isObjectBindingPattern(token.parent)) {
if (isParameter(token.parent.parent)) {
deleteDestructuringElements(changes, sourceFile, token.parent);
}
else {
changes.delete(sourceFile, token.parent.parent);
}
}
else if (canDeleteEntireVariableStatement(sourceFile, token)) {
deleteEntireVariableStatement(changes, sourceFile, <VariableDeclarationList>token.parent);
}
else {
tryDeleteDeclaration(sourceFile, token, changes, checker, sourceFiles, /*isFixAll*/ true);
}
break;
@@ -125,25 +152,16 @@ namespace ts.codefix {
return token.kind === SyntaxKind.ImportKeyword ? tryCast(token.parent, isImportDeclaration) : undefined;
}
function tryDeleteFullDestructure(token: Node, changes: textChanges.ChangeTracker, sourceFile: SourceFile, checker: TypeChecker, sourceFiles: readonly SourceFile[], isFixAll: boolean): boolean {
if (token.kind !== SyntaxKind.OpenBraceToken || !isObjectBindingPattern(token.parent)) return false;
const decl = token.parent.parent;
if (decl.kind === SyntaxKind.Parameter) {
tryDeleteParameter(changes, sourceFile, decl, checker, sourceFiles, isFixAll);
}
else {
changes.delete(sourceFile, decl);
}
return true;
function canDeleteEntireVariableStatement(sourceFile: SourceFile, token: Node): boolean {
return isVariableDeclarationList(token.parent) && first(token.parent.getChildren(sourceFile)) === token;
}
function tryDeleteFullVariableStatement(sourceFile: SourceFile, token: Node, changes: textChanges.ChangeTracker): boolean {
const declarationList = tryCast(token.parent, isVariableDeclarationList);
if (declarationList && declarationList.getChildren(sourceFile)[0] === token) {
changes.delete(sourceFile, declarationList.parent.kind === SyntaxKind.VariableStatement ? declarationList.parent : declarationList);
return true;
}
return false;
function deleteEntireVariableStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: VariableDeclarationList) {
changes.delete(sourceFile, node.parent.kind === SyntaxKind.VariableStatement ? node.parent : node);
}
function deleteDestructuringElements(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: ObjectBindingPattern) {
forEach(node.elements, n => changes.delete(sourceFile, n));
}
function tryPrefixDeclaration(changes: textChanges.ChangeTracker, errorCode: number, sourceFile: SourceFile, token: Node): void {
@@ -205,7 +223,7 @@ namespace ts.codefix {
}
}
function tryDeleteParameter(changes: textChanges.ChangeTracker, sourceFile: SourceFile, p: ParameterDeclaration, checker: TypeChecker, sourceFiles: readonly SourceFile[], isFixAll: boolean): void {
function tryDeleteParameter(changes: textChanges.ChangeTracker, sourceFile: SourceFile, p: ParameterDeclaration, checker: TypeChecker, sourceFiles: readonly SourceFile[], isFixAll = false): void {
if (mayDeleteParameter(p, checker, isFixAll)) {
if (p.modifiers && p.modifiers.length > 0
&& (!isIdentifier(p.name) || FindAllReferences.Core.isSymbolReferencedInFile(p.name, checker, sourceFile))) {
@@ -50,7 +50,7 @@
verify.codeFixAll({
fixId: "unusedIdentifier_delete",
fixAllDescription: "Delete all unused declarations",
fixAllDescription: ts.Diagnostics.Delete_all_unused_declarations.message,
newFileContent:
`import { used1 } from "foo";
import { used2 } from "foo";
@@ -38,7 +38,7 @@
verify.codeFixAll({
fixId: "unusedIdentifier_delete",
fixAllDescription: "Delete all unused declarations",
fixAllDescription: ts.Diagnostics.Delete_all_unused_declarations.message,
newFileContent:
`/** Parameter doc comment */
function f() {}
@@ -5,6 +5,6 @@
verify.codeFixAll({
fixId: "unusedIdentifier_delete",
fixAllDescription: "Delete all unused declarations",
fixAllDescription: ts.Diagnostics.Delete_all_unused_declarations.message,
newFileContent: "export {};\n",
});
@@ -14,7 +14,7 @@
verify.codeFixAll({
fixId: "unusedIdentifier_delete",
fixAllDescription: "Delete all unused declarations",
fixAllDescription: ts.Diagnostics.Delete_all_unused_declarations.message,
newFileContent:
`
export class C {
@@ -7,7 +7,7 @@
////const { x, y } = o;
verify.codeFix({
description: "Remove destructuring",
description: ts.Diagnostics.Remove_unused_destructuring_declaration.message,
newFileContent:
`export {};
`,
@@ -12,11 +12,11 @@
verify.codeFixAll({
fixId: "unusedIdentifier_delete",
fixAllDescription: "Delete all unused declarations",
fixAllDescription: ts.Diagnostics.Delete_all_unused_declarations.message,
newFileContent:
`const { a } = o;
a;
export function f({ a }) {
export function f({ a }, { }) {
a;
}`,
});
@@ -6,7 +6,7 @@
////for (const { x } of o) {}
verify.codeFix({
description: "Remove destructuring",
description: ts.Diagnostics.Remove_unused_destructuring_declaration.message,
newFileContent:
`for (const {} of o) {}`,
});
@@ -7,7 +7,7 @@
////const { x: { a, b } } = o;
verify.codeFix({
description: "Remove destructuring",
description: ts.Diagnostics.Remove_unused_destructuring_declaration.message,
newFileContent:
`export {};
const { } = o;`,
@@ -38,7 +38,7 @@
verify.codeFixAll({
fixId: "unusedIdentifier_delete",
fixAllDescription: "Delete all unused declarations",
fixAllDescription: ts.Diagnostics.Delete_all_unused_declarations.message,
newFileContent:
`{
const { x } = o;
@@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
// @noUnusedLocals: true
// @noUnusedParameters: true
////export function f({ x, y }, a) {
//// a;
////}
verify.codeFix({
description: [ts.Diagnostics.Remove_unused_declarations_for_Colon_0.message, "x, y"],
index: 0,
newFileContent:
`export function f({ }, a) {
a;
}`,
});
@@ -4,7 +4,7 @@
// @noUnusedParameters: true
////function f(a, b, { x, y }) { b; }
////f(0, 1, 2);
////f(0, 1, { x: 1, y: 1 });
////
////class C {
//// m(a, b, c) { b; }
@@ -23,10 +23,10 @@
verify.codeFixAll({
fixId: "unusedIdentifier_delete",
fixAllDescription: "Delete all unused declarations",
fixAllDescription: ts.Diagnostics.Delete_all_unused_declarations.message,
newFileContent:
`function f(b) { b; }
f(1);
`function f(b, { }) { b; }
f(1, { x: 1, y: 1 });
class C {
m(b) { b; }