selene_lib/lints/
if_same_then_else.rs1use 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}