selene_lib/lints/
ifs_same_cond.rs1use super::*;
2use crate::ast_util::{range, HasSideEffects};
3use std::convert::Infallible;
4
5use full_moon::{
6 ast::{self, Ast},
7 node::Node,
8 visitors::Visitor,
9};
10
11pub struct IfsSameCondLint;
12
13impl Lint for IfsSameCondLint {
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(IfsSameCondLint)
22 }
23
24 fn pass(&self, ast: &Ast, _: &Context, _: &AstContext) -> Vec<Diagnostic> {
25 let mut visitor = IfsSameCondVisitor {
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 "ifs_same_cond",
37 "this `elseif` has the same condition 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 IfsSameCondVisitor {
51 positions: Vec<((u32, u32), (u32, u32))>,
52}
53
54impl Visitor for IfsSameCondVisitor {
55 fn visit_if(&mut self, if_block: &ast::If) {
56 if let Some(else_ifs) = if_block.else_if() {
57 let mut conditions = Vec::with_capacity(else_ifs.len() + 1);
58 if !if_block.condition().has_side_effects() {
59 conditions.push(if_block.condition());
60 }
61
62 'else_ifs: for else_if in else_ifs {
63 let condition = else_if.condition();
64 if !condition.has_side_effects() {
65 for other in &conditions {
66 if other.similar(&condition) {
67 self.positions.push((range(condition), range(other)));
68 continue 'else_ifs;
69 }
70 }
71
72 conditions.push(condition);
73 }
74 }
75 }
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::{super::test_util::test_lint, *};
82
83 #[test]
84 fn test_ifs_same_cond() {
85 test_lint(
86 IfsSameCondLint::new(()).unwrap(),
87 "ifs_same_cond",
88 "ifs_same_cond",
89 );
90 }
91}