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 85 86 87 88 89 90 91
use super::*; use std::convert::Infallible; use full_moon::{ ast::{self, Ast}, node::Node, visitors::Visitor, }; pub struct DivideByZeroLint; impl Rule for DivideByZeroLint { type Config = (); type Error = Infallible; fn new(_: Self::Config) -> Result<Self, Self::Error> { Ok(DivideByZeroLint) } fn pass(&self, ast: &Ast, _: &Context) -> Vec<Diagnostic> { let mut visitor = DivideByZeroVisitor { positions: Vec::new(), }; visitor.visit_ast(ast); visitor .positions .iter() .map(|position| { Diagnostic::new( "divide_by_zero", "dividing by zero is not allowed, use math.huge instead".to_owned(), Label::new(*position), ) }) .collect() } fn severity(&self) -> Severity { Severity::Warning } fn rule_type(&self) -> RuleType { RuleType::Complexity } } struct DivideByZeroVisitor { positions: Vec<(usize, usize)>, } fn value_is_zero(value: &ast::Value) -> bool { if let ast::Value::Number(token) = value { token.token().to_string() == "0" } else { false } } impl Visitor for DivideByZeroVisitor { fn visit_expression(&mut self, node: &ast::Expression) { if_chain::if_chain! { if let ast::Expression::BinaryOperator { lhs, binop, rhs, .. } = node; if let ast::Expression::Value { value, .. } = &**lhs; if let ast::BinOp::Slash(_) = binop; if let ast::Expression::Value { value: rhs_value, .. } = &**rhs; if value_is_zero(rhs_value) && !value_is_zero(value); then { let range = node.range().unwrap(); self.positions.push((range.0.bytes(), range.1.bytes())); } } } } #[cfg(test)] mod tests { use super::{super::test_util::test_lint, *}; #[test] fn test_divide_by_zero() { test_lint( DivideByZeroLint::new(()).unwrap(), "divide_by_zero", "divide_by_zero", ); } }