From fa3f70e9c68bf70c2e4aeeaea2165aebe7becf63 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 24 Feb 2023 13:58:53 +0200 Subject: [PATCH] feat(52569): disallow the deletion of function calls used by a unused destructuring declaration --- src/services/codefixes/fixUnusedIdentifier.ts | 30 ++++++++++++++++++- ...nusedIdentifier_destructuring_elements7.ts | 20 +++++++++++++ ...nusedIdentifier_destructuring_elements8.ts | 24 +++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/codeFixUnusedIdentifier_destructuring_elements7.ts create mode 100644 tests/cases/fourslash/codeFixUnusedIdentifier_destructuring_elements8.ts diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index e9b0f2fc7ed12..881ce7478604b 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -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; diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructuring_elements7.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructuring_elements7.ts new file mode 100644 index 0000000000000..e4905ac4445af --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructuring_elements7.ts @@ -0,0 +1,20 @@ +/// + +// @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(); +}`, +}); diff --git a/tests/cases/fourslash/codeFixUnusedIdentifier_destructuring_elements8.ts b/tests/cases/fourslash/codeFixUnusedIdentifier_destructuring_elements8.ts new file mode 100644 index 0000000000000..235e5b1b31ef8 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnusedIdentifier_destructuring_elements8.ts @@ -0,0 +1,24 @@ +/// + +// @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; +}`, +});