Skip to content
This repository was archived by the owner on Aug 31, 2023. It is now read-only.

Commit 0bff894

Browse files
committed
feat(rome_js_analyze): noDuplicateCase, no-duplicate-case #3969
1 parent 7ba67ce commit 0bff894

File tree

15 files changed

+1186
-19
lines changed

15 files changed

+1186
-19
lines changed

crates/rome_diagnostics_categories/src/categories.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ define_dategories! {
4848
"lint/nursery/noConstEnum": "https://docs.rome.tools/lint/rules/noConstEnum",
4949
"lint/nursery/noConstructorReturn": "https://docs.rome.tools/lint/rules/noConstructorReturn",
5050
"lint/nursery/noDistractingElements": "https://docs.rome.tools/lint/rules/noDistractingElements",
51+
"lint/nursery/noDuplicateCase": "https://docs.rome.tools/lint/rules/noDuplicateCase",
5152
"lint/nursery/noDuplicateObjectKeys":"https://docs.rome.tools/lint/rules/noDuplicateObjectKeys",
5253
"lint/nursery/noEmptyInterface": "https://docs.rome.tools/lint/rules/noEmptyInterface",
5354
"lint/nursery/noExtraNonNullAssertion":"https://docs.rome.tools/lint/rules/noExtraNonNullAssertion",

crates/rome_js_analyze/src/analyzers/nursery.rs

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
use rome_analyze::context::RuleContext;
2+
use rome_analyze::{declare_rule, Ast, Rule, RuleDiagnostic};
3+
use rome_js_syntax::{AnyJsSwitchClause, JsCaseClause, JsSwitchStatement};
4+
use rome_rowan::AstNode;
5+
use std::collections::hash_map::Entry;
6+
use std::collections::HashMap;
7+
8+
declare_rule! {
9+
/// Disallow duplicate case labels.
10+
/// If a switch statement has duplicate test expressions in case clauses, it is likely that a programmer copied a case clause but forgot to change the test expression.
11+
///
12+
/// Source: https://eslint.org/docs/latest/rules/no-duplicate-case
13+
///
14+
/// ## Examples
15+
///
16+
/// ### Invalid
17+
///
18+
/// ```ts,expect_diagnostic
19+
/// switch (a) {
20+
/// case 1:
21+
/// break;
22+
/// case 1:
23+
/// break;
24+
/// default:
25+
/// break;
26+
/// }
27+
/// ```
28+
///
29+
/// ```ts,expect_diagnostic
30+
/// switch (a) {
31+
/// case one:
32+
/// break;
33+
/// case one:
34+
/// break;
35+
/// default:
36+
/// break;
37+
/// }
38+
/// ```
39+
///
40+
/// ```ts,expect_diagnostic
41+
/// switch (a) {
42+
/// case "1":
43+
/// break;
44+
/// case "1":
45+
/// break;
46+
/// default:
47+
/// break;
48+
/// }
49+
/// ```
50+
///
51+
/// ### Valid
52+
///
53+
/// ```ts
54+
/// switch (a) {
55+
/// case 1:
56+
/// break;
57+
/// case 2:
58+
/// break;
59+
/// default:
60+
/// break;
61+
/// }
62+
/// ```
63+
///
64+
/// ```ts
65+
/// switch (a) {
66+
/// case one:
67+
/// break;
68+
/// case two:
69+
/// break;
70+
/// default:
71+
/// break;
72+
/// }
73+
/// ```
74+
///
75+
/// ```ts
76+
/// switch (a) {
77+
/// case "1":
78+
/// break;
79+
/// case "2":
80+
/// break;
81+
/// default:
82+
/// break;
83+
/// }
84+
/// ```
85+
pub(crate) NoDuplicateCase {
86+
version: "11.0.0",
87+
name: "noDuplicateCase",
88+
recommended: true,
89+
}
90+
}
91+
92+
impl Rule for NoDuplicateCase {
93+
type Query = Ast<JsSwitchStatement>;
94+
type State = JsCaseClause;
95+
type Signals = Vec<Self::State>;
96+
type Options = ();
97+
98+
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
99+
let node = ctx.query();
100+
101+
let mut defined_cases: HashMap<String, JsCaseClause> = HashMap::new();
102+
let mut signals = Vec::new();
103+
104+
for case in node.cases() {
105+
if let AnyJsSwitchClause::JsCaseClause(case) = case {
106+
if let Ok(test) = case.test() {
107+
let text = test.text();
108+
109+
match defined_cases.entry(text) {
110+
Entry::Occupied(_) => {
111+
signals.push(case);
112+
}
113+
Entry::Vacant(entry) => {
114+
entry.insert(case);
115+
}
116+
}
117+
}
118+
}
119+
}
120+
121+
signals
122+
}
123+
124+
fn diagnostic(_: &RuleContext<Self>, case: &Self::State) -> Option<RuleDiagnostic> {
125+
case.test().ok().map(|test| {
126+
RuleDiagnostic::new(rule_category!(), test.range(), "Duplicate case label.")
127+
})
128+
}
129+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
var a = 1;
2+
switch (a) {
3+
case 1:
4+
break;
5+
case 1:
6+
break;
7+
case 2:
8+
break;
9+
default:
10+
break;
11+
}
12+
var a = "1";
13+
switch (a) {
14+
case "1":
15+
break;
16+
case "1":
17+
break;
18+
case "2":
19+
break;
20+
default:
21+
break;
22+
}
23+
var a = 1,
24+
one = 1;
25+
switch (a) {
26+
case one:
27+
break;
28+
case one:
29+
break;
30+
case 2:
31+
break;
32+
default:
33+
break;
34+
}
35+
var a = 1,
36+
p = { p: { p1: 1, p2: 1 } };
37+
switch (a) {
38+
case p.p.p1:
39+
break;
40+
case p.p.p1:
41+
break;
42+
default:
43+
break;
44+
}
45+
var a = 1,
46+
f = function (b) {
47+
return b ? { p1: 1 } : { p1: 2 };
48+
};
49+
switch (a) {
50+
case f(true).p1:
51+
break;
52+
case f(true).p1:
53+
break;
54+
default:
55+
break;
56+
}
57+
var a = 1,
58+
f = function (s) {
59+
return { p1: s };
60+
};
61+
switch (a) {
62+
case f(a + 1).p1:
63+
break;
64+
case f(a + 1).p1:
65+
break;
66+
default:
67+
break;
68+
}
69+
var a = 1,
70+
f = function (s) {
71+
return { p1: s };
72+
};
73+
switch (a) {
74+
case f(a === 1 ? 2 : 3).p1:
75+
break;
76+
case f(a === 1 ? 2 : 3).p1:
77+
break;
78+
default:
79+
break;
80+
}
81+
var a = 1,
82+
f1 = function () {
83+
return { p1: 1 };
84+
};
85+
switch (a) {
86+
case f1().p1:
87+
break;
88+
case f1().p1:
89+
break;
90+
default:
91+
break;
92+
}
93+
var a = [1, 2];
94+
switch (a.toString()) {
95+
case [1, 2].toString():
96+
break;
97+
case [1, 2].toString():
98+
break;
99+
default:
100+
break;
101+
}
102+
switch (a) {
103+
case a:
104+
case a:
105+
}
106+
switch (a) {
107+
case a:
108+
break;
109+
case b:
110+
break;
111+
case a:
112+
break;
113+
case c:
114+
break;
115+
case a:
116+
break;
117+
}
118+
var a = 1,
119+
p = { p: { p1: 1, p2: 1 } };
120+
switch (a) {
121+
case p.p.p1:
122+
break;
123+
case p.p.p1: // comment\n
124+
break;
125+
default:
126+
break;
127+
}
128+
var a = 1,
129+
p = { p: { p1: 1, p2: 1 } };
130+
switch (a) {
131+
case /* comment */
132+
p.p.p1:
133+
break;
134+
case p.p.p1:
135+
break;
136+
default:
137+
break;
138+
}
139+
var a = 1,
140+
p = { p: { p1: 1, p2: 1 } };
141+
switch (a) {
142+
case p.p /* comment */.p1:
143+
break;
144+
case p.p.p1: // comment
145+
break;
146+
default:
147+
break;
148+
}
149+
var a = 1,
150+
p = { p: { p1: 1, p2: 1 } };
151+
switch (a) {
152+
case p.p.p1:
153+
break;
154+
case p.p.p1: // comment
155+
break;
156+
case /* comment */
157+
p.p.p1:
158+
break;
159+
default:
160+
break;
161+
}
162+
var a = 1,
163+
f = function (s) {
164+
return { p1: s };
165+
};
166+
switch (a) {
167+
case f(a + 1).p1:
168+
break;
169+
case f(a + 1).p1:
170+
break;
171+
default:
172+
break;
173+
}
174+
var a = 1,
175+
f = function (s) {
176+
return { p1: s };
177+
};
178+
switch (a) {
179+
case f(
180+
a + 1 // comment
181+
).p1:
182+
break;
183+
case f(a + 1).p1:
184+
break;
185+
default:
186+
break;
187+
}

0 commit comments

Comments
 (0)