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
use super::*; use std::{convert::Infallible, str}; use full_moon::{ ast::{self, Ast}, node::Node, visitors::Visitor, }; pub struct SuspiciousReverseLoopLint; impl Rule for SuspiciousReverseLoopLint { type Config = (); type Error = Infallible; fn new(_: Self::Config) -> Result<Self, Self::Error> { Ok(SuspiciousReverseLoopLint) } fn pass(&self, ast: &Ast, _: &Context) -> Vec<Diagnostic> { let mut visitor = SuspiciousReverseLoopVisitor { positions: Vec::new(), }; visitor.visit_ast(ast); visitor .positions .iter() .map(|position| { Diagnostic::new_complete( "suspicious_reverse_loop", "this loop will only ever run once at most".to_owned(), Label::new(*position), vec!["help: try adding `, -1` after `1`".to_owned()], Vec::new(), ) }) .collect() } fn severity(&self) -> Severity { Severity::Error } fn rule_type(&self) -> RuleType { RuleType::Correctness } } struct SuspiciousReverseLoopVisitor { positions: Vec<(usize, usize)>, } impl Visitor for SuspiciousReverseLoopVisitor { fn visit_numeric_for(&mut self, node: &ast::NumericFor) { if_chain::if_chain! { if node.step().is_none(); if let ast::Expression::UnaryOperator { unop: ast::UnOp::Hash(_), .. } = node.start(); if let ast::Expression::Value { value, .. } = node.end(); if let ast::Value::Number(number) = &**value; if str::parse::<f32>(&number.token().to_string()).ok() <= Some(1.0); then { self.positions.push(( node.start().start_position().unwrap().bytes(), node.end().end_position().unwrap().bytes(), )); } }; } } #[cfg(test)] mod tests { use super::{super::test_util::test_lint, *}; #[test] fn test_suspicious_reverse_loop() { test_lint( SuspiciousReverseLoopLint::new(()).unwrap(), "suspicious_reverse_loop", "suspicious_reverse_loop", ); } }