1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
use crate::rule_prelude::*;
use ast::SwitchStmt;

declare_lint! {
    /**
    Disallow duplicate test cases in `switch` statements.

    `switch` statement clauses can freely have duplicate tests, however this is almost always a mistake, because
    the second case is unreachable. It is likely that the programmer copied a case clause but did not change the test for it.

    ## Invalid Code Examples

    ```js
    switch (a) {
        case 1:
            break;
        case 2:
            break;
        case 1:
            break;
        default:
            break;
    }
    ```

    ```js
    switch (a) {
        case foo.bar:
            break;

        case foo . bar:
            break;
    }
    ```
    */
    #[derive(Default)]
    NoDuplicateCases,
    errors,
    "no-duplicate-cases"
}

#[typetag::serde]
impl CstRule for NoDuplicateCases {
    fn check_node(&self, node: &SyntaxNode, ctx: &mut RuleCtx) -> Option<()> {
        if let Some(switch) = node.try_to::<SwitchStmt>() {
            let mut seen: Vec<SyntaxNode> = vec![];
            for case in switch.cases().filter_map(|case| case.into_case()) {
                if let Some(expr) = case.test() {
                    if let Some(old) = seen.iter().find(|clause| clause.lexical_eq(expr.syntax())) {
                        let err = ctx
                            .err(
                                self.name(),
                                format!("duplicate switch statement test `{}`", old.trimmed_text()),
                            )
                            .secondary(
                                old,
                                format!("`{}` is first tested for here", old.trimmed_text()),
                            )
                            .primary(
                                expr.syntax(),
                                format!(
                                    "`{}` is then tested for again here",
                                    expr.syntax().trimmed_text()
                                ),
                            );

                        ctx.add_err(err)
                    } else {
                        seen.push(expr.syntax().clone());
                    }
                }
            }
        }
        None
    }
}

rule_tests! {
    NoDuplicateCases::default(),
    err: {
        "
        switch (foo) {
            case foo. bar:
            break;

            case foo.bar:
            break;
        }
        ",
        "
        switch foo {
            case 5:
            break;

            case 6:
            break;

            case 5:
            break;
        }
        "
    },
    ok: {}
}