selene_lib/lints/
divide_by_zero.rs

1use super::*;
2use std::convert::Infallible;
3
4use full_moon::{
5    ast::{self, Ast},
6    node::Node,
7    visitors::Visitor,
8};
9
10pub struct DivideByZeroLint;
11
12impl Lint for DivideByZeroLint {
13    type Config = ();
14    type Error = Infallible;
15
16    const SEVERITY: Severity = Severity::Warning;
17    const LINT_TYPE: LintType = LintType::Complexity;
18
19    fn new(_: Self::Config) -> Result<Self, Self::Error> {
20        Ok(DivideByZeroLint)
21    }
22
23    fn pass(&self, ast: &Ast, _: &Context, _: &AstContext) -> Vec<Diagnostic> {
24        let mut visitor = DivideByZeroVisitor {
25            positions: Vec::new(),
26        };
27
28        visitor.visit_ast(ast);
29
30        visitor
31            .positions
32            .iter()
33            .map(|position| {
34                Diagnostic::new(
35                    "divide_by_zero",
36                    "dividing by zero is not allowed, use math.huge instead".to_owned(),
37                    Label::new(*position),
38                )
39            })
40            .collect()
41    }
42}
43
44struct DivideByZeroVisitor {
45    positions: Vec<(usize, usize)>,
46}
47
48fn value_is_zero(value: &ast::Expression) -> bool {
49    if let ast::Expression::Number(token) = value {
50        token.token().to_string() == "0"
51    } else {
52        false
53    }
54}
55
56impl Visitor for DivideByZeroVisitor {
57    fn visit_expression(&mut self, node: &ast::Expression) {
58        if_chain::if_chain! {
59            if let ast::Expression::BinaryOperator { lhs, binop, rhs, .. } = node;
60            if let ast::BinOp::Slash(_) = binop;
61            if value_is_zero(rhs) && !value_is_zero(lhs);
62            then {
63                let range = node.range().unwrap();
64                self.positions.push((range.0.bytes(), range.1.bytes()));
65            }
66        }
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::{super::test_util::test_lint, *};
73
74    #[test]
75    fn test_divide_by_zero() {
76        test_lint(
77            DivideByZeroLint::new(()).unwrap(),
78            "divide_by_zero",
79            "divide_by_zero",
80        );
81    }
82}