From 6e31de0fb612dd7464980e18061a6f984d7c896a Mon Sep 17 00:00:00 2001 From: sidlamsal <98344853+sidlamsal@users.noreply.github.com> Date: Tue, 4 Feb 2025 21:00:08 -0500 Subject: [PATCH 1/6] redundant import suggestion and test --- src/compiler/checker.ts | 11 ++++++++++ src/compiler/diagnosticMessages.json | 5 +++++ .../fourslash/redundantImportDiagnostic.ts | 21 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/cases/fourslash/redundantImportDiagnostic.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fcb93c45dc1dd..cd0d923ad8355 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -48121,6 +48121,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkAliasSymbol(node); if (node.kind === SyntaxKind.ImportSpecifier) { checkModuleExportName(node.propertyName); + + // Check for duplicate import names + const importSpecifier = node; + const namecheck = getTextOfIdentifierOrLiteral(importSpecifier.name); + const propertyNamecheck = importSpecifier.propertyName ? getTextOfIdentifierOrLiteral(importSpecifier.propertyName) : ""; + + + if(namecheck === propertyNamecheck){ + errorOrSuggestion(/*isError*/ false, importSpecifier, Diagnostics.Redundant_named_import_0, namecheck) + } + if ( moduleExportNameIsDefault(node.propertyName || node.name) && getESModuleInterop(compilerOptions) && diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 0d7e0bb39a8d4..f51ae9f7000cb 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5337,6 +5337,11 @@ "category": "Error", "code": 6189 }, + "Redundant named import '{0}'.": { + "category": "Suggestion", + "code": 6190, + "reportsUnnecessary": true + }, "Whether to keep outdated console output in watch mode instead of clearing the screen.": { "category": "Message", "code": 6191 diff --git a/tests/cases/fourslash/redundantImportDiagnostic.ts b/tests/cases/fourslash/redundantImportDiagnostic.ts new file mode 100644 index 0000000000000..993ed8d79f5e4 --- /dev/null +++ b/tests/cases/fourslash/redundantImportDiagnostic.ts @@ -0,0 +1,21 @@ +/// + +// @filename: /a.ts +//// export const bar = () => console.log('Hello world!'); + +// @filename: /b.ts +//// import { bar as bar } from "./a"; +//// bar(); + + +goTo.file("/b.ts"); + + +verify.getSuggestionDiagnostics([ + { + message: "Redundant named import 'bar'.", + code: ts.Diagnostics.Redundant_named_import_0.code, + range: {pos: 9, end: 19, fileName:"/b.ts"}, + reportsUnnecessary: true, + } +]); \ No newline at end of file From 0f1c958d845ca6981ab4369b2182c86416b62a1d Mon Sep 17 00:00:00 2001 From: sidlamsal <98344853+sidlamsal@users.noreply.github.com> Date: Mon, 10 Feb 2025 23:36:43 -0500 Subject: [PATCH 2/6] codefix rough draft --- src/services/_namespaces/ts.codefix.ts | 1 + .../codefixes/removeUnnecessaryNamedImport.ts | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/services/codefixes/removeUnnecessaryNamedImport.ts diff --git a/src/services/_namespaces/ts.codefix.ts b/src/services/_namespaces/ts.codefix.ts index 3cf05630388de..2aecc1ece0566 100644 --- a/src/services/_namespaces/ts.codefix.ts +++ b/src/services/_namespaces/ts.codefix.ts @@ -74,3 +74,4 @@ export * from "../codefixes/splitTypeOnlyImport.js"; export * from "../codefixes/convertConstToLet.js"; export * from "../codefixes/fixExpectedComma.js"; export * from "../codefixes/fixAddVoidToPromise.js"; +export * from "../codefixes/removeUnnecessaryNamedImport.js"; diff --git a/src/services/codefixes/removeUnnecessaryNamedImport.ts b/src/services/codefixes/removeUnnecessaryNamedImport.ts new file mode 100644 index 0000000000000..00276005ae67b --- /dev/null +++ b/src/services/codefixes/removeUnnecessaryNamedImport.ts @@ -0,0 +1,48 @@ +import { + createCodeFixAction, + codeFixAll, + createCombinedCodeActions, + eachDiagnostic, + registerCodeFix, +} from "../_namespaces/ts.codefix.js"; +import { + factory, + Diagnostics, + getTokenAtPosition, + isImportSpecifier, + textChanges, + tryCast, + SourceFile, + TextSpan, +} from "../_namespaces/ts.js"; + +const fixId = "removeUnnecessaryNamedImport"; +const errorCodes = [ + Diagnostics.Redundant_named_import_0.code, +]; + +registerCodeFix({ + errorCodes, + getCodeActions: function getCodeActionsToRemoveUnnecessaryNamedImport(context) { + const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span)); + if (changes.length > 0) { + return [createCodeFixAction(fixId, changes, Diagnostics.Redundant_named_import_0, fixId, Diagnostics.Redundant_named_import_0)]; + } + }, + fixIds: [fixId], + getAllCodeActions: context => { + return codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag)); + }, +}); + +function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan) { + const token = getTokenAtPosition(sourceFile, span.start); + const importSpecifier = tryCast(token.parent, isImportSpecifier); + if (!importSpecifier) { + return; + } + + if (importSpecifier.propertyName && importSpecifier.propertyName.text === importSpecifier.name.text) { + changeTracker.replaceNode(sourceFile, importSpecifier, factory.updateImportSpecifier(importSpecifier, importSpecifier.isTypeOnly, /*propertyName*/ undefined, importSpecifier.name)); + } +} \ No newline at end of file From 74d4dc8ba151faeaa1ea35805f832b4313aed1bc Mon Sep 17 00:00:00 2001 From: calebloring <112512660+calebloring@users.noreply.github.com> Date: Fri, 14 Feb 2025 13:42:06 -0500 Subject: [PATCH 3/6] Added new Diagnostics messages --- src/compiler/diagnosticMessages.json | 8 ++++++++ .../codefixes/removeUnnecessaryNamedImport.ts | 13 ++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f51ae9f7000cb..1cf1d35502746 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -8272,6 +8272,14 @@ "category": "Message", "code": 95197 }, + "Simplify redundant import '{0}'": { + "category": "Message", + "code": 95198 + }, + "Simplify all redundant imports": { + "category": "Message", + "code": 95199 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/codefixes/removeUnnecessaryNamedImport.ts b/src/services/codefixes/removeUnnecessaryNamedImport.ts index 00276005ae67b..0ad6fa9f368a6 100644 --- a/src/services/codefixes/removeUnnecessaryNamedImport.ts +++ b/src/services/codefixes/removeUnnecessaryNamedImport.ts @@ -25,8 +25,15 @@ registerCodeFix({ errorCodes, getCodeActions: function getCodeActionsToRemoveUnnecessaryNamedImport(context) { const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span)); + const token = getTokenAtPosition(context.sourceFile, context.span.start); + const importSpecifier = tryCast(token.parent, isImportSpecifier); + + if (!importSpecifier) { + return; + } + if (changes.length > 0) { - return [createCodeFixAction(fixId, changes, Diagnostics.Redundant_named_import_0, fixId, Diagnostics.Redundant_named_import_0)]; + return [createCodeFixAction(fixId, changes, [Diagnostics.Simplify_redundant_import_0,importSpecifier.name.text], fixId, Diagnostics.Simplify_all_redundant_imports)]; } }, fixIds: [fixId], @@ -45,4 +52,8 @@ function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: Source if (importSpecifier.propertyName && importSpecifier.propertyName.text === importSpecifier.name.text) { changeTracker.replaceNode(sourceFile, importSpecifier, factory.updateImportSpecifier(importSpecifier, importSpecifier.isTypeOnly, /*propertyName*/ undefined, importSpecifier.name)); } +} + +function getImportSpecifier(){ + } \ No newline at end of file From c6a735e60d57c52842cdf93e7f727ff8a9251e89 Mon Sep 17 00:00:00 2001 From: calebloring <112512660+calebloring@users.noreply.github.com> Date: Fri, 14 Feb 2025 13:44:56 -0500 Subject: [PATCH 4/6] remove excess function --- src/services/codefixes/removeUnnecessaryNamedImport.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/services/codefixes/removeUnnecessaryNamedImport.ts b/src/services/codefixes/removeUnnecessaryNamedImport.ts index 0ad6fa9f368a6..ec5b0343f9a99 100644 --- a/src/services/codefixes/removeUnnecessaryNamedImport.ts +++ b/src/services/codefixes/removeUnnecessaryNamedImport.ts @@ -53,7 +53,3 @@ function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: Source changeTracker.replaceNode(sourceFile, importSpecifier, factory.updateImportSpecifier(importSpecifier, importSpecifier.isTypeOnly, /*propertyName*/ undefined, importSpecifier.name)); } } - -function getImportSpecifier(){ - -} \ No newline at end of file From 620eb1aa02a7e63df322194aaee62444e71140a2 Mon Sep 17 00:00:00 2001 From: sidlamsal <98344853+sidlamsal@users.noreply.github.com> Date: Tue, 18 Feb 2025 12:20:07 -0500 Subject: [PATCH 5/6] code fix test --- .../codefixes/removeUnnecessaryNamedImport.ts | 2 -- .../codeFixRemoveUnnecessaryNamedImports.ts | 31 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/codeFixRemoveUnnecessaryNamedImports.ts diff --git a/src/services/codefixes/removeUnnecessaryNamedImport.ts b/src/services/codefixes/removeUnnecessaryNamedImport.ts index ec5b0343f9a99..8e4dea2b7f574 100644 --- a/src/services/codefixes/removeUnnecessaryNamedImport.ts +++ b/src/services/codefixes/removeUnnecessaryNamedImport.ts @@ -1,8 +1,6 @@ import { createCodeFixAction, codeFixAll, - createCombinedCodeActions, - eachDiagnostic, registerCodeFix, } from "../_namespaces/ts.codefix.js"; import { diff --git a/tests/cases/fourslash/codeFixRemoveUnnecessaryNamedImports.ts b/tests/cases/fourslash/codeFixRemoveUnnecessaryNamedImports.ts new file mode 100644 index 0000000000000..a8f18c080da28 --- /dev/null +++ b/tests/cases/fourslash/codeFixRemoveUnnecessaryNamedImports.ts @@ -0,0 +1,31 @@ +/// + +// @filename: /a.ts +////export const foo = () => console.log('Hello world!'); +////export const bar = () => console.log('Hello world!'); + +// @filename: /b.ts +////import { foo as foo, bar as bar } from "./a"; +////foo(); +////bar(); + +goTo.file("/b.ts"); + +verify.codeFix({ + description: 'Simplify redundant import \'foo\'', + index: 0, + newFileContent: +`import { foo, bar as bar } from "./a"; +foo(); +bar();` +}); + +verify.codeFixAll({ + fixAllDescription: ts.Diagnostics.Simplify_all_redundant_imports.message, + fixId: "removeUnnecessaryNamedImport", + newFileContent: +`import { foo, bar } from "./a"; +foo(); +bar();` +}); + \ No newline at end of file From bab49cebf1d8229b474d0e17e71778f2dec5525c Mon Sep 17 00:00:00 2001 From: sidlamsal <98344853+sidlamsal@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:08:02 -0500 Subject: [PATCH 6/6] formatting --- src/compiler/checker.ts | 5 +- .../codefixes/removeUnnecessaryNamedImport.ts | 106 +++++++++--------- 2 files changed, 55 insertions(+), 56 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cd0d923ad8355..501d7a21a1624 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -48126,10 +48126,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const importSpecifier = node; const namecheck = getTextOfIdentifierOrLiteral(importSpecifier.name); const propertyNamecheck = importSpecifier.propertyName ? getTextOfIdentifierOrLiteral(importSpecifier.propertyName) : ""; - - if(namecheck === propertyNamecheck){ - errorOrSuggestion(/*isError*/ false, importSpecifier, Diagnostics.Redundant_named_import_0, namecheck) + if (namecheck === propertyNamecheck) { + errorOrSuggestion(/*isError*/ false, importSpecifier, Diagnostics.Redundant_named_import_0, namecheck); } if ( diff --git a/src/services/codefixes/removeUnnecessaryNamedImport.ts b/src/services/codefixes/removeUnnecessaryNamedImport.ts index 8e4dea2b7f574..7c711b4927259 100644 --- a/src/services/codefixes/removeUnnecessaryNamedImport.ts +++ b/src/services/codefixes/removeUnnecessaryNamedImport.ts @@ -1,53 +1,53 @@ -import { - createCodeFixAction, - codeFixAll, - registerCodeFix, -} from "../_namespaces/ts.codefix.js"; -import { - factory, - Diagnostics, - getTokenAtPosition, - isImportSpecifier, - textChanges, - tryCast, - SourceFile, - TextSpan, -} from "../_namespaces/ts.js"; - -const fixId = "removeUnnecessaryNamedImport"; -const errorCodes = [ - Diagnostics.Redundant_named_import_0.code, -]; - -registerCodeFix({ - errorCodes, - getCodeActions: function getCodeActionsToRemoveUnnecessaryNamedImport(context) { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span)); - const token = getTokenAtPosition(context.sourceFile, context.span.start); - const importSpecifier = tryCast(token.parent, isImportSpecifier); - - if (!importSpecifier) { - return; - } - - if (changes.length > 0) { - return [createCodeFixAction(fixId, changes, [Diagnostics.Simplify_redundant_import_0,importSpecifier.name.text], fixId, Diagnostics.Simplify_all_redundant_imports)]; - } - }, - fixIds: [fixId], - getAllCodeActions: context => { - return codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag)); - }, -}); - -function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan) { - const token = getTokenAtPosition(sourceFile, span.start); - const importSpecifier = tryCast(token.parent, isImportSpecifier); - if (!importSpecifier) { - return; - } - - if (importSpecifier.propertyName && importSpecifier.propertyName.text === importSpecifier.name.text) { - changeTracker.replaceNode(sourceFile, importSpecifier, factory.updateImportSpecifier(importSpecifier, importSpecifier.isTypeOnly, /*propertyName*/ undefined, importSpecifier.name)); - } -} +import { + codeFixAll, + createCodeFixAction, + registerCodeFix, +} from "../_namespaces/ts.codefix.js"; +import { + Diagnostics, + factory, + getTokenAtPosition, + isImportSpecifier, + SourceFile, + textChanges, + TextSpan, + tryCast, +} from "../_namespaces/ts.js"; + +const fixId = "removeUnnecessaryNamedImport"; +const errorCodes = [ + Diagnostics.Redundant_named_import_0.code, +]; + +registerCodeFix({ + errorCodes, + getCodeActions: function getCodeActionsToRemoveUnnecessaryNamedImport(context) { + const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span)); + const token = getTokenAtPosition(context.sourceFile, context.span.start); + const importSpecifier = tryCast(token.parent, isImportSpecifier); + + if (!importSpecifier) { + return; + } + + if (changes.length > 0) { + return [createCodeFixAction(fixId, changes, [Diagnostics.Simplify_redundant_import_0, importSpecifier.name.text], fixId, Diagnostics.Simplify_all_redundant_imports)]; + } + }, + fixIds: [fixId], + getAllCodeActions: context => { + return codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag)); + }, +}); + +function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan) { + const token = getTokenAtPosition(sourceFile, span.start); + const importSpecifier = tryCast(token.parent, isImportSpecifier); + if (!importSpecifier) { + return; + } + + if (importSpecifier.propertyName && importSpecifier.propertyName.text === importSpecifier.name.text) { + changeTracker.replaceNode(sourceFile, importSpecifier, factory.updateImportSpecifier(importSpecifier, importSpecifier.isTypeOnly, /*propertyName*/ undefined, importSpecifier.name)); + } +}