selene_lib/lints/
suspicious_reverse_loop.rs1use 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}