feat(52569): fixUnusedIdentifier: Unwanted side-effects when removing an unused destructuring declaration containing a function call (#52951)

This commit is contained in:
Oleksandr T
2023-03-07 01:41:13 +02:00
committed by GitHub
parent 7e4592ea09
commit 5240f060b8
3 changed files with 73 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ import {
CancellationToken,
cast,
CodeFixAction,
CodeFixContext,
Debug,
DiagnosticAndArguments,
DiagnosticMessage,
@@ -14,12 +15,15 @@ import {
forEach,
FunctionLikeDeclaration,
getJSDocParameterTags,
getNewLineOrDefaultFromHost,
getPrecedingNonSpaceCharacterPosition,
getTokenAtPosition,
Identifier,
ImportDeclaration,
isArrayBindingPattern,
isBinaryExpression,
isCallExpression,
isCallLikeExpression,
isComputedPropertyName,
isDeclarationWithTypeParameterChildren,
isExpressionStatement,
@@ -37,11 +41,14 @@ import {
isPrefixUnaryExpression,
isPropertyAccessExpression,
isSuperKeyword,
isVariableDeclaration,
isVariableDeclarationList,
length,
map,
Node,
ObjectBindingPattern,
ParameterDeclaration,
probablyUsesSemicolons,
Program,
showModuleSpecifier,
SourceFile,
@@ -114,7 +121,7 @@ registerCodeFix({
}
return [
createDeleteFix(textChanges.ChangeTracker.with(context, t =>
t.delete(sourceFile, token.parent.parent)), Diagnostics.Remove_unused_destructuring_declaration)
deleteDestructuring(context, t, sourceFile, token.parent as ObjectBindingPattern | ArrayBindingPattern)), Diagnostics.Remove_unused_destructuring_declaration),
];
}
@@ -243,6 +250,27 @@ function deleteDestructuringElements(changes: textChanges.ChangeTracker, sourceF
forEach(node.elements, n => changes.delete(sourceFile, n));
}
function deleteDestructuring(context: CodeFixContext, changes: textChanges.ChangeTracker, sourceFile: SourceFile, { parent }: ObjectBindingPattern | ArrayBindingPattern) {
if (isVariableDeclaration(parent) && parent.initializer && isCallLikeExpression(parent.initializer)) {
if (isVariableDeclarationList(parent.parent) && length(parent.parent.declarations) > 1) {
const varStatement = parent.parent.parent;
const pos = varStatement.getStart(sourceFile);
const end = varStatement.end;
changes.delete(sourceFile, parent);
changes.insertNodeAt(sourceFile, end, parent.initializer, {
prefix: getNewLineOrDefaultFromHost(context.host, context.formatContext.options) + sourceFile.text.slice(getPrecedingNonSpaceCharacterPosition(sourceFile.text, pos - 1), pos),
suffix: probablyUsesSemicolons(sourceFile) ? ";" : "",
});
}
else {
changes.replaceNode(sourceFile, parent.parent, parent.initializer);
}
}
else {
changes.delete(sourceFile, parent);
}
}
function tryPrefixDeclaration(changes: textChanges.ChangeTracker, errorCode: number, sourceFile: SourceFile, token: Node): void {
// Don't offer to prefix a property.
if (errorCode === Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code) return;

View File

@@ -0,0 +1,20 @@
/// <reference path="fourslash.ts" />
// @noUnusedLocals: true
// @noUnusedParameters: true
////function foo() {
//// return { a: 1 };
////}
////[|function bar() {
//// const { a } = foo();
////}|]
verify.codeFix({
index: 0,
description: ts.Diagnostics.Remove_unused_destructuring_declaration.message,
newRangeContent:
`function bar() {
foo();
}`,
});

View File

@@ -0,0 +1,24 @@
/// <reference path="fourslash.ts" />
// @noUnusedLocals: true
// @noUnusedParameters: true
////function foo() {
//// return { a: 1 };
////}
////[|function bar() {
//// const { a } = foo(),
//// b = 1;
//// return b;
////}|]
verify.codeFix({
index: 0,
description: ts.Diagnostics.Remove_unused_destructuring_declaration.message,
newRangeContent:
`function bar() {
const b = 1;
foo();
return b;
}`,
});