selene_lib/lints/
if_same_then_else.rs

1use super::*;
2use crate::ast_util::range;
3use std::convert::Infallible;
4
5use full_moon::{
6    ast::{self, Ast},
7    node::Node,
8    visitors::Visitor,
9};
10
11pub struct IfSameThenElseLint;
12
13impl Lint for IfSameThenElseLint {
14    type Config = ();
15    type Error = Infallible;
16
17    const SEVERITY: Severity = Severity::Error;
18    const LINT_TYPE: LintType = LintType::Correctness;
19
20    fn new(_: Self::Config) -> Result<Self, Self::Error> {
21        Ok(IfSameThenElseLint)
22    }
23
24    fn pass(&self, ast: &Ast, _: &Context, _: &AstContext) -> Vec<Diagnostic> {
25        let mut visitor = IfSameThenElseVisitor {
26            positions: Vec::new(),
27        };
28
29        visitor.visit_ast(ast);
30
31        visitor
32            .positions
33            .drain(..)
34            .map(|position| {
35                Diagnostic::new_complete(
36                    "if_same_then_else",
37                    "this has the same block as a previous if".to_owned(),
38                    Label::new(position.0),
39                    Vec::new(),
40                    vec![Label::new_with_message(
41                        position.1,
42                        "note: same as this".to_owned(),
43                    )],
44                )
45            })
46            .collect()
47    }
48}
49
50struct IfSameThenElseVisitor {
51    positions: Vec<((u32, u32), (u32, u32))>,
52}
53
54impl Visitor for IfSameThenElseVisitor {
55    fn visit_if(&mut self, if_block: &ast::If) {
56        let else_ifs = if_block
57            .else_if()
58            .map(|else_ifs| else_ifs.iter().collect::<Vec<_>>())
59            .unwrap_or_default();
60
61        let mut blocks = Vec::with_capacity(2 + else_ifs.len());
62        blocks.push(if_block.block());
63
64        'blocks: for block in else_ifs
65            .iter()
66            .map(|else_if| else_if.block())
67            .chain(if_block.else_block())
68        {
69            if block.stmts().next().is_none() {
70                continue;
71            }
72
73            for other in &blocks {
74                if other.similar(&block) {
75                    self.positions.push((range(block), range(other)));
76                    continue 'blocks;
77                }
78            }
79
80            blocks.push(block);
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::{super::test_util::test_lint, *};
88
89    #[test]
90    fn test_if_same_then_else() {
91        test_lint(
92            IfSameThenElseLint::new(()).unwrap(),
93            "if_same_then_else",
94            "if_same_then_else",
95        );
96    }
97}