diff --git a/crates/oxc_linter/src/rules/eslint/no_duplicate_imports.rs b/crates/oxc_linter/src/rules/eslint/no_duplicate_imports.rs index 90dfba0a8b2d7d..af7f94c479ece1 100644 --- a/crates/oxc_linter/src/rules/eslint/no_duplicate_imports.rs +++ b/crates/oxc_linter/src/rules/eslint/no_duplicate_imports.rs @@ -7,16 +7,22 @@ use oxc_syntax::module_record::{ExportImportName, ImportImportName}; use crate::{context::LintContext, rule::Rule}; -fn no_duplicate_imports_diagnostic(module_name: &str, span: Span) -> OxcDiagnostic { +fn no_duplicate_imports_diagnostic(module_name: &str, span: Span, span2: Span) -> OxcDiagnostic { OxcDiagnostic::warn(format!("'{}' import is duplicated", module_name)) .with_help("Merge the duplicated import into a single import statement") - .with_label(span) + .with_labels([ + span.label("This import is duplicated"), + span2.label("Can be merged with this import"), + ]) } -fn no_duplicate_exports_diagnostic(module_name: &str, span: Span) -> OxcDiagnostic { +fn no_duplicate_exports_diagnostic(module_name: &str, span: Span, span2: Span) -> OxcDiagnostic { OxcDiagnostic::warn(format!("'{}' export is duplicated", module_name)) .with_help("Merge the duplicated exports into a single export statement") - .with_label(span) + .with_labels([ + span.label("This export is duplicated"), + span2.label("Can be merged with this"), + ]) } #[derive(Debug, Default, Clone)] @@ -102,7 +108,11 @@ impl Rule for NoDuplicateImports { if let Some(existing) = import_map.get(source) { let can_merge = can_merge_imports(&import_type, existing, same_statement); if can_merge { - ctx.diagnostic(no_duplicate_imports_diagnostic(source, span)); + ctx.diagnostic(no_duplicate_imports_diagnostic( + source, + span, + existing.first().unwrap().1, + )); continue; } } @@ -127,7 +137,14 @@ impl Rule for NoDuplicateImports { side_effect_import_map.iter().for_each(|(source, spans)| { if spans.len() > 1 { spans.iter().for_each(|span| { - ctx.diagnostic(no_duplicate_imports_diagnostic(source, *span)); + let i = spans.iter().position(|s| s == span).unwrap(); + if i > 0 { + ctx.diagnostic(no_duplicate_imports_diagnostic( + source, + *span, + spans.first().unwrap().clone(), + )); + } }); } }); @@ -144,12 +161,20 @@ impl Rule for NoDuplicateImports { .iter() .any(|(t, _, _)| matches!(t, ImportType::AllButDefault)) { - ctx.diagnostic(no_duplicate_exports_diagnostic(source, span)); + ctx.diagnostic(no_duplicate_exports_diagnostic( + source, + span, + existing.first().unwrap().1, + )); continue; } } - if side_effect_import_map.get(source).is_some() { - ctx.diagnostic(no_duplicate_exports_diagnostic(source, span)); + if let Some(existing) = side_effect_import_map.get(source) { + ctx.diagnostic(no_duplicate_exports_diagnostic( + source, + span, + *existing.first().unwrap(), + )); continue; } import_map.entry(source).or_default().push(( @@ -163,7 +188,11 @@ impl Rule for NoDuplicateImports { if existing.iter().any(|(t, _, _)| { matches!(t, ImportType::Named | ImportType::SideEffect) }) { - ctx.diagnostic(no_duplicate_exports_diagnostic(source, span)); + ctx.diagnostic(no_duplicate_exports_diagnostic( + source, + span, + existing.first().unwrap().1, + )); continue; } } @@ -186,7 +215,11 @@ impl Rule for NoDuplicateImports { if existing.iter().any(|(t, _, _)| { matches!(t, ImportType::Default | ImportType::Namespace) }) { - ctx.diagnostic(no_duplicate_exports_diagnostic(source, span)); + ctx.diagnostic(no_duplicate_exports_diagnostic( + source, + span, + existing.first().unwrap().1, + )); continue; } @@ -201,7 +234,11 @@ impl Rule for NoDuplicateImports { || (matches!(t, ImportType::Default) && *module_type == ModuleType::Import) }) { - ctx.diagnostic(no_duplicate_exports_diagnostic(source, span)); + ctx.diagnostic(no_duplicate_exports_diagnostic( + source, + span, + existing.first().unwrap().1, + )); continue; } } diff --git a/crates/oxc_linter/src/snapshots/no_duplicate_imports.snap b/crates/oxc_linter/src/snapshots/no_duplicate_imports.snap index 6eae291c8d1166..f00135c0848e04 100644 --- a/crates/oxc_linter/src/snapshots/no_duplicate_imports.snap +++ b/crates/oxc_linter/src/snapshots/no_duplicate_imports.snap @@ -3,107 +3,159 @@ source: crates/oxc_linter/src/tester.rs snapshot_kind: text --- ⚠ eslint(no-duplicate-imports): 'fs' import is duplicated - ╭─[no_duplicate_imports.tsx:2:9] + ╭─[no_duplicate_imports.tsx:1:8] 1 │ import "fs"; + · ──┬─ + · ╰── Can be merged with this import 2 │ import "fs" - · ─────────── + · ──┬─ + · ╰── This import is duplicated ╰──── help: Merge the duplicated import into a single import statement ⚠ eslint(no-duplicate-imports): 'lodash-es' import is duplicated - ╭─[no_duplicate_imports.tsx:2:9] + ╭─[no_duplicate_imports.tsx:1:23] 1 │ import { merge } from "lodash-es"; + · ─────┬───── + · ╰── Can be merged with this import 2 │ import { find } from "lodash-es"; - · ───────────────────────────────── + · ─────┬───── + · ╰── This import is duplicated ╰──── help: Merge the duplicated import into a single import statement ⚠ eslint(no-duplicate-imports): 'lodash-es' import is duplicated - ╭─[no_duplicate_imports.tsx:2:11] + ╭─[no_duplicate_imports.tsx:1:23] 1 │ import { merge } from "lodash-es"; + · ─────┬───── + · ╰── Can be merged with this import 2 │ import _ from "lodash-es"; - · ────────────────────────── + · ─────┬───── + · ╰── This import is duplicated ╰──── help: Merge the duplicated import into a single import statement ⚠ eslint(no-duplicate-imports): 'os' import is duplicated - ╭─[no_duplicate_imports.tsx:2:11] + ╭─[no_duplicate_imports.tsx:1:16] 1 │ import os from "os"; + · ──┬─ + · ╰── Can be merged with this import 2 │ import { something } from "os"; - · ─────────────────────────────── 3 │ import * as foobar from "os"; + · ──┬─ + · ╰── This import is duplicated ╰──── help: Merge the duplicated import into a single import statement ⚠ eslint(no-duplicate-imports): 'lodash-es' import is duplicated - ╭─[no_duplicate_imports.tsx:3:11] + ╭─[no_duplicate_imports.tsx:1:24] + 1 │ import * as modns from "lodash-es"; + · ─────┬───── + · ╰── Can be merged with this import 2 │ import { merge } from "lodash-es"; 3 │ import { baz } from "lodash-es"; - · ──────────────────────────────── + · ─────┬───── + · ╰── This import is duplicated ╰──── help: Merge the duplicated import into a single import statement ⚠ eslint(no-duplicate-imports): 'os' export is duplicated - ╭─[no_duplicate_imports.tsx:2:11] + ╭─[no_duplicate_imports.tsx:1:10] 1 │ export { os } from "os"; + · ─┬ + · ╰── Can be merged with this 2 │ export { something } from "os"; - · ─────────────────────────────── + · ────┬──── + · ╰── This export is duplicated ╰──── help: Merge the duplicated exports into a single export statement ⚠ eslint(no-duplicate-imports): 'os' export is duplicated - ╭─[no_duplicate_imports.tsx:2:11] + ╭─[no_duplicate_imports.tsx:1:16] 1 │ import os from "os"; + · ──┬─ + · ╰── Can be merged with this 2 │ export { os as foobar } from "os"; - · ────────────────────────────────── + · ──────┬───── + · ╰── This export is duplicated 3 │ export { something } from "os"; ╰──── help: Merge the duplicated exports into a single export statement ⚠ eslint(no-duplicate-imports): 'os' export is duplicated - ╭─[no_duplicate_imports.tsx:3:11] + ╭─[no_duplicate_imports.tsx:1:16] + 1 │ import os from "os"; + · ──┬─ + · ╰── Can be merged with this 2 │ export { os as foobar } from "os"; 3 │ export { something } from "os"; - · ─────────────────────────────── + · ────┬──── + · ╰── This export is duplicated ╰──── help: Merge the duplicated exports into a single export statement ⚠ eslint(no-duplicate-imports): 'os' export is duplicated - ╭─[no_duplicate_imports.tsx:2:11] + ╭─[no_duplicate_imports.tsx:1:16] 1 │ import os from "os"; + · ──┬─ + · ╰── Can be merged with this 2 │ export { something } from "os"; - · ─────────────────────────────── + · ────┬──── + · ╰── This export is duplicated ╰──── help: Merge the duplicated exports into a single export statement ⚠ eslint(no-duplicate-imports): 'os' export is duplicated - ╭─[no_duplicate_imports.tsx:2:9] + ╭─[no_duplicate_imports.tsx:1:16] 1 │ import os from "os"; + · ──┬─ + · ╰── Can be merged with this 2 │ export * as os from "os"; - · ───────────────────────── + · ────────────┬──────────── + · ╰── This export is duplicated ╰──── help: Merge the duplicated exports into a single export statement - ⚠ eslint(no-duplicate-imports): 'os' import is duplicated - ╭─[no_duplicate_imports.tsx:2:9] + ⚠ eslint(no-duplicate-imports): 'os' export is duplicated + ╭─[no_duplicate_imports.tsx:1:1] 1 │ export * as os from "os"; + · ────────────┬──────────── + · ╰── This export is duplicated 2 │ import os from "os"; - · ──────────────────── + · ──┬─ + · ╰── Can be merged with this ╰──── - help: Merge the duplicated import into a single import statement + help: Merge the duplicated exports into a single export statement + + ⚠ eslint(no-duplicate-imports): 'mod' export is duplicated + ╭─[no_duplicate_imports.tsx:1:24] + 1 │ import * as modns from "mod"; + · ──┬── + · ╰── Can be merged with this + 2 │ export * as modns from "mod"; + · ───────────────┬────────────── + · ╰── This export is duplicated + ╰──── + help: Merge the duplicated exports into a single export statement ⚠ eslint(no-duplicate-imports): 'os' export is duplicated - ╭─[no_duplicate_imports.tsx:2:9] + ╭─[no_duplicate_imports.tsx:1:1] 1 │ export * from "os"; + · ─────────┬───────── + · ╰── Can be merged with this 2 │ export * from "os"; - · ─────────────────── + · ─────────┬───────── + · ╰── This export is duplicated ╰──── help: Merge the duplicated exports into a single export statement ⚠ eslint(no-duplicate-imports): 'os' export is duplicated - ╭─[no_duplicate_imports.tsx:2:9] + ╭─[no_duplicate_imports.tsx:1:8] 1 │ import "os"; + · ──┬─ + · ╰── Can be merged with this 2 │ export * from "os"; - · ─────────────────── + · ─────────┬───────── + · ╰── This export is duplicated ╰──── help: Merge the duplicated exports into a single export statement