-
Notifications
You must be signed in to change notification settings - Fork 649
feat(rome_js_analyze): noDuplicateCase, no-duplicate-case #3969 #4039
Changes from 1 commit
74aca10
44f7070
55f7b79
6e8e3f2
5330fa3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| use rome_analyze::context::RuleContext; | ||
| use rome_analyze::{declare_rule, Ast, Rule, RuleDiagnostic}; | ||
| use rome_js_syntax::{AnyJsSwitchClause, JsCaseClause, JsSwitchStatement}; | ||
| use rome_rowan::AstNode; | ||
| use std::collections::hash_map::Entry; | ||
| use std::collections::HashMap; | ||
|
|
||
| declare_rule! { | ||
| /// Disallow duplicate case labels. | ||
| /// 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. | ||
| /// | ||
| /// Source: https://eslint.org/docs/latest/rules/no-duplicate-case | ||
| /// | ||
| /// ## Examples | ||
| /// | ||
| /// ### Invalid | ||
| /// | ||
| /// ```ts,expect_diagnostic | ||
| /// switch (a) { | ||
| /// case 1: | ||
| /// break; | ||
| /// case 1: | ||
| /// break; | ||
| /// default: | ||
| /// break; | ||
| /// } | ||
| /// ``` | ||
| /// | ||
| /// ```ts,expect_diagnostic | ||
| /// switch (a) { | ||
| /// case one: | ||
| /// break; | ||
| /// case one: | ||
| /// break; | ||
| /// default: | ||
| /// break; | ||
| /// } | ||
| /// ``` | ||
| /// | ||
| /// ```ts,expect_diagnostic | ||
| /// switch (a) { | ||
| /// case "1": | ||
| /// break; | ||
| /// case "1": | ||
| /// break; | ||
| /// default: | ||
| /// break; | ||
| /// } | ||
| /// ``` | ||
| /// | ||
| /// ### Valid | ||
| /// | ||
| /// ```ts | ||
| /// switch (a) { | ||
| /// case 1: | ||
| /// break; | ||
| /// case 2: | ||
| /// break; | ||
| /// default: | ||
| /// break; | ||
| /// } | ||
| /// ``` | ||
| /// | ||
| /// ```ts | ||
| /// switch (a) { | ||
| /// case one: | ||
| /// break; | ||
| /// case two: | ||
| /// break; | ||
| /// default: | ||
| /// break; | ||
| /// } | ||
| /// ``` | ||
| /// | ||
| /// ```ts | ||
| /// switch (a) { | ||
| /// case "1": | ||
| /// break; | ||
| /// case "2": | ||
| /// break; | ||
| /// default: | ||
| /// break; | ||
| /// } | ||
| /// ``` | ||
| pub(crate) NoDuplicateCase { | ||
| version: "11.0.0", | ||
denbezrukov marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| name: "noDuplicateCase", | ||
| recommended: true, | ||
| } | ||
| } | ||
|
|
||
| impl Rule for NoDuplicateCase { | ||
| type Query = Ast<JsSwitchStatement>; | ||
| type State = JsCaseClause; | ||
| type Signals = Vec<Self::State>; | ||
| type Options = (); | ||
|
|
||
| fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
| let node = ctx.query(); | ||
|
|
||
| let mut defined_cases: HashMap<String, JsCaseClause> = HashMap::new(); | ||
| let mut signals = Vec::new(); | ||
|
|
||
| for case in node.cases() { | ||
| if let AnyJsSwitchClause::JsCaseClause(case) = case { | ||
| if let Ok(test) = case.test() { | ||
| let text = test.text(); | ||
|
||
|
|
||
| match defined_cases.entry(text) { | ||
| Entry::Occupied(_) => { | ||
| signals.push(case); | ||
| } | ||
| Entry::Vacant(entry) => { | ||
| entry.insert(case); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| signals | ||
| } | ||
|
|
||
| fn diagnostic(_: &RuleContext<Self>, case: &Self::State) -> Option<RuleDiagnostic> { | ||
| case.test().ok().map(|test| { | ||
| RuleDiagnostic::new(rule_category!(), test.range(), "Duplicate case label.") | ||
denbezrukov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }) | ||
| } | ||
denbezrukov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| var a = 1; | ||
| switch (a) { | ||
| case 1: | ||
| break; | ||
| case 1: | ||
| break; | ||
| case 2: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = "1"; | ||
| switch (a) { | ||
| case "1": | ||
| break; | ||
| case "1": | ||
| break; | ||
| case "2": | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| one = 1; | ||
| switch (a) { | ||
| case one: | ||
| break; | ||
| case one: | ||
| break; | ||
| case 2: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| p = { p: { p1: 1, p2: 1 } }; | ||
| switch (a) { | ||
| case p.p.p1: | ||
| break; | ||
| case p.p.p1: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| f = function (b) { | ||
| return b ? { p1: 1 } : { p1: 2 }; | ||
| }; | ||
| switch (a) { | ||
| case f(true).p1: | ||
| break; | ||
| case f(true).p1: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| f = function (s) { | ||
| return { p1: s }; | ||
| }; | ||
| switch (a) { | ||
| case f(a + 1).p1: | ||
| break; | ||
| case f(a + 1).p1: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| f = function (s) { | ||
| return { p1: s }; | ||
| }; | ||
| switch (a) { | ||
| case f(a === 1 ? 2 : 3).p1: | ||
| break; | ||
| case f(a === 1 ? 2 : 3).p1: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| f1 = function () { | ||
| return { p1: 1 }; | ||
| }; | ||
| switch (a) { | ||
| case f1().p1: | ||
| break; | ||
| case f1().p1: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = [1, 2]; | ||
| switch (a.toString()) { | ||
| case [1, 2].toString(): | ||
| break; | ||
| case [1, 2].toString(): | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| switch (a) { | ||
| case a: | ||
| case a: | ||
| } | ||
| switch (a) { | ||
| case a: | ||
| break; | ||
| case b: | ||
| break; | ||
| case a: | ||
| break; | ||
| case c: | ||
| break; | ||
| case a: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| p = { p: { p1: 1, p2: 1 } }; | ||
| switch (a) { | ||
| case p.p.p1: | ||
| break; | ||
| case p.p.p1: // comment\n | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| p = { p: { p1: 1, p2: 1 } }; | ||
| switch (a) { | ||
| case /* comment */ | ||
| p.p.p1: | ||
| break; | ||
| case p.p.p1: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| p = { p: { p1: 1, p2: 1 } }; | ||
| switch (a) { | ||
| case p.p /* comment */.p1: | ||
| break; | ||
| case p.p.p1: // comment | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| p = { p: { p1: 1, p2: 1 } }; | ||
| switch (a) { | ||
| case p.p.p1: | ||
| break; | ||
| case p.p.p1: // comment | ||
| break; | ||
| case /* comment */ | ||
| p.p.p1: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| f = function (s) { | ||
| return { p1: s }; | ||
| }; | ||
| switch (a) { | ||
| case f(a + 1).p1: | ||
| break; | ||
| case f(a + 1).p1: | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| var a = 1, | ||
| f = function (s) { | ||
| return { p1: s }; | ||
| }; | ||
| switch (a) { | ||
| case f( | ||
| a + 1 // comment | ||
| ).p1: | ||
| break; | ||
| case f(a + 1).p1: | ||
| break; | ||
| default: | ||
| break; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.