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
use crate::rule_prelude::*;
use SyntaxKind::*;

declare_lint! {
    /**
    Disallow unneeded semicolons.

    Unneeded semicolons are often caused by typing mistakes, while this is not an error, it
    can cause confusion when reading the code. This rule disallows empty statements (extra semicolons).

    ## Invalid Code Examples

    ```js
    if (foo) {
        ;
    }
    ```

    ```js
    class Foo {
        constructor() {};
    }
    ```
    */
    #[derive(Default)]
    NoExtraSemi,
    errors,
    "no-extra-semi"
}

const ALLOWED: [SyntaxKind; 8] = [
    FOR_STMT,
    FOR_IN_STMT,
    FOR_OF_STMT,
    WHILE_STMT,
    DO_WHILE_STMT,
    IF_STMT,
    LABELLED_STMT,
    WITH_STMT,
];

#[typetag::serde]
impl CstRule for NoExtraSemi {
    fn check_node(&self, node: &SyntaxNode, ctx: &mut RuleCtx) -> Option<()> {
        if node.kind() == SyntaxKind::EMPTY_STMT
            && node
                .parent()
                .map_or(true, |parent| !ALLOWED.contains(&parent.kind()))
        {
            let err = ctx
                .err(self.name(), "Unnecessary semicolon")
                .primary(node, "help: delete this semicolon");

            ctx.add_err(err);
        }
        None
    }
}

rule_tests! {
  NoExtraSemi::default(),
  err: {
    ";",
    "
      if (foo) {
        ;
      }
      ",
    "
      class Foo {
        ;
      }
      ",
    "class Foo extends Bar {
        constructor() {};
      }
      "
  },
  ok: {
    "
      class Foo {}
      "
  }
}