diff --git a/crates/oxc_linter/src/rules/eslint/no_dupe_keys.rs b/crates/oxc_linter/src/rules/eslint/no_dupe_keys.rs index 134e87f10..7fe1eb4c3 100644 --- a/crates/oxc_linter/src/rules/eslint/no_dupe_keys.rs +++ b/crates/oxc_linter/src/rules/eslint/no_dupe_keys.rs @@ -1,5 +1,5 @@ use oxc_ast::{ - ast::{ObjectPropertyKind, PropertyKind}, + ast::{ObjectPropertyKind, PropertyKey, PropertyKind}, AstKind, }; use oxc_diagnostics::OxcDiagnostic; @@ -9,10 +9,13 @@ use rustc_hash::{FxBuildHasher, FxHashMap}; use crate::{context::LintContext, rule::Rule, AstNode}; -fn no_dupe_keys_diagnostic(span0: Span, span1: Span) -> OxcDiagnostic { - OxcDiagnostic::warn("Disallow duplicate keys in object literals") +fn no_dupe_keys_diagnostic(first: Span, second: Span, key: &str) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("Duplicate key '{key}'")) .with_help("Consider removing the duplicated key") - .with_labels([span0, span1]) + .with_labels([ + first.label("Key is first defined here"), + second.label("and duplicated here"), + ]) } #[derive(Debug, Default, Clone)] @@ -25,14 +28,38 @@ declare_oxc_lint!( /// /// ### Why is this bad? /// - /// Multiple properties with the same key in object literals can cause unexpected behavior in your application. + /// Multiple properties with the same key in object literals can cause + /// unexpected behavior in your application. + /// + /// It is safe to disable this rule when using TypeScript because + /// TypeScript's compiler enforces this check. /// /// ### Example - /// ```javascript + /// + /// Examples of **incorrect** code for this rule: + /// ```js /// var foo = { /// bar: "baz", /// bar: "qux" - /// } + /// }; + /// + /// var foo = { + /// "bar": "baz", + /// bar: "qux" + /// }; + /// + /// var foo = { + /// 0x1: "baz", + /// 1: "qux" + /// }; + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// var foo = { + /// bar: "baz", + /// qux: "qux" + /// }; /// ``` NoDupeKeys, correctness @@ -60,13 +87,24 @@ impl Rule for NoDupeKeys { || prop.kind == PropertyKind::Init || prev_kind == prop.kind { - ctx.diagnostic(no_dupe_keys_diagnostic(prev_span, prop.key.span())); + let name = prop_key_name(&prop.key, ctx); + ctx.diagnostic(no_dupe_keys_diagnostic(prev_span, prop.key.span(), name)); } } } } } +fn prop_key_name<'a>(key: &PropertyKey<'a>, ctx: &LintContext<'a>) -> &'a str { + match key { + PropertyKey::Identifier(ident) => ident.name.as_str(), + PropertyKey::StaticIdentifier(ident) => ident.name.as_str(), + PropertyKey::PrivateIdentifier(ident) => ident.name.as_str(), + PropertyKey::StringLiteral(lit) => lit.value.as_str(), + PropertyKey::NumericLiteral(lit) => lit.raw, + _ => ctx.source_range(key.span()), + } +} #[test] fn test() { use crate::tester::Tester; diff --git a/crates/oxc_linter/src/snapshots/no_dupe_keys.snap b/crates/oxc_linter/src/snapshots/no_dupe_keys.snap index de382c4e9..e8940bd09 100644 --- a/crates/oxc_linter/src/snapshots/no_dupe_keys.snap +++ b/crates/oxc_linter/src/snapshots/no_dupe_keys.snap @@ -1,111 +1,141 @@ --- source: crates/oxc_linter/src/tester.rs --- - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key 'a' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { a: b, ['a']: b }; - · ─ ─── + · ┬ ─┬─ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key 'y' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { y: 1, y: 2 }; - · ─ ─ + · ┬ ┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key '' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { '': 1, '': 2 }; - · ── ── + · ─┬ ─┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key '``' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { '': 1, [``]: 2 }; - · ── ── + · ─┬ ─┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key '1' ╭─[no_dupe_keys.tsx:1:13] 1 │ var foo = { 0x1: 1, 1: 2}; - · ─── ─ + · ─┬─ ┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key '10' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { 012: 1, 10: 2 }; - · ─── ── + · ─┬─ ─┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key '1' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { 0b1: 1, 1: 2 }; - · ─── ─ + · ─┬─ ┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key '1' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { 0o1: 1, 1: 2 }; - · ─── ─ + · ─┬─ ┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key '10' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { 1_0: 1, 10: 2 }; - · ─── ── + · ─┬─ ─┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key 'z' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { "z": 1, z: 2 }; - · ─── ─ + · ─┬─ ┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key 'bar' ╭─[no_dupe_keys.tsx:2:3] 1 │ var foo = { 2 │ bar: 1, - · ─── + · ─┬─ + · ╰── Key is first defined here 3 │ bar: 1, - · ─── + · ─┬─ + · ╰── and duplicated here 4 │ } ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key 'a' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { a: 1, get a() {} }; - · ─ ─ + · ┬ ┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key 'a' ╭─[no_dupe_keys.tsx:1:11] 1 │ var x = { a: 1, set a(value) {} }; - · ─ ─ + · ┬ ┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key 'b' ╭─[no_dupe_keys.tsx:1:17] 1 │ var x = { a: 1, b: { a: 2 }, get b() {} }; - · ─ ─ + · ┬ ┬ + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key - ⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals + ⚠ eslint(no-dupe-keys): Duplicate key '/(?0)/' ╭─[no_dupe_keys.tsx:1:12] 1 │ var x = ({ '/(?0)/': 1, [/(?0)/]: 2 }) - · ────────────── ──────────── + · ───────┬────── ──────┬───── + · │ ╰── and duplicated here + · ╰── Key is first defined here ╰──── help: Consider removing the duplicated key