selene_lib/lints/
suspicious_reverse_loop.rs

1use super::*;
2use std::{convert::Infallible, str};
3
4use full_moon::{
5    ast::{self, Ast},
6    node::Node,
7    visitors::Visitor,
8};
9
10pub struct SuspiciousReverseLoopLint;
11
12impl Lint for SuspiciousReverseLoopLint {
13    type Config = ();
14    type Error = Infallible;
15
16    const SEVERITY: Severity = Severity::Error;
17    const LINT_TYPE: LintType = LintType::Correctness;
18
19    fn new(_: Self::Config) -> Result<Self, Self::Error> {
20        Ok(SuspiciousReverseLoopLint)
21    }
22
23    fn pass(&self, ast: &Ast, _: &Context, _: &AstContext) -> Vec<Diagnostic> {
24        let mut visitor = SuspiciousReverseLoopVisitor {
25            positions: Vec::new(),
26        };
27
28        visitor.visit_ast(ast);
29
30        visitor
31            .positions
32            .iter()
33            .map(|position| {
34                Diagnostic::new_complete(
35                    "suspicious_reverse_loop",
36                    "this loop will only ever run once at most".to_owned(),
37                    Label::new(*position),
38                    vec!["help: try adding `, -1` after `1`".to_owned()],
39                    Vec::new(),
40                )
41            })
42            .collect()
43    }
44}
45
46struct SuspiciousReverseLoopVisitor {
47    positions: Vec<(usize, usize)>,
48}
49
50impl Visitor for SuspiciousReverseLoopVisitor {
51    fn visit_numeric_for(&mut self, node: &ast::NumericFor) {
52        if_chain::if_chain! {
53            if node.step().is_none();
54            if let ast::Expression::UnaryOperator {
55                unop: ast::UnOp::Hash(_),
56                ..
57            } = node.start();
58            if let ast::Expression::Number(number) = node.end();
59            if str::parse::<f32>(&number.token().to_string()).ok() <= Some(1.0);
60            then {
61                self.positions.push((
62                    node.start().start_position().unwrap().bytes(),
63                    node.end().end_position().unwrap().bytes(),
64                ));
65            }
66        };
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::{super::test_util::test_lint, *};
73
74    #[test]
75    fn test_suspicious_reverse_loop() {
76        test_lint(
77            SuspiciousReverseLoopLint::new(()).unwrap(),
78            "suspicious_reverse_loop",
79            "suspicious_reverse_loop",
80        );
81    }
82}