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));
+ }
+}