Skip to main content

luaur_analysis/methods/
lint_for_range_visit.rs

1use crate::functions::emit_warning::emit_warning;
2use crate::records::lint_for_range::LintForRange;
3use luaur_ast::records::ast_expr_constant_number::AstExprConstantNumber;
4use luaur_ast::records::ast_expr_unary::{AstExprUnary, AstExprUnaryOp};
5use luaur_ast::records::ast_node::AstNode;
6use luaur_ast::records::ast_stat_for::AstStatFor;
7use luaur_ast::records::location::Location;
8use luaur_ast::rtti::ast_node_as;
9use luaur_config::enums::code::Code;
10
11impl LintForRange {
12    pub fn visit_ast_stat_for_linter(&mut self, node: *mut AstStatFor) -> bool {
13        unsafe {
14            if (*node).step.is_null() {
15                let fc = ast_node_as::<AstExprConstantNumber>((*node).from as *mut AstNode);
16                let fu = ast_node_as::<AstExprUnary>((*node).from as *mut AstNode);
17                let tc = ast_node_as::<AstExprConstantNumber>((*node).to as *mut AstNode);
18                let tu = ast_node_as::<AstExprUnary>((*node).to as *mut AstNode);
19
20                let range_location = Location::new(
21                    (*(*node).from).base.location.begin,
22                    (*(*node).to).base.location.end,
23                );
24
25                if !fu.is_null()
26                    && (*fu).op == AstExprUnaryOp::Len
27                    && !tc.is_null()
28                    && (*tc).value == 1.0
29                {
30                    emit_warning(
31                        &mut *self.context,
32                        Code::Code_ForRange,
33                        range_location,
34                        format_args!("For loop should iterate backwards; did you forget to specify -1 as step?"),
35                    );
36                } else if !fc.is_null() && !tc.is_null() && (*fc).value > (*tc).value {
37                    emit_warning(
38                        &mut *self.context,
39                        Code::Code_ForRange,
40                        range_location,
41                        format_args!("For loop should iterate backwards; did you forget to specify -1 as step?"),
42                    );
43                } else if !fc.is_null()
44                    && !tc.is_null()
45                    && self.get_loop_end((*fc).value, (*tc).value) != (*tc).value
46                {
47                    emit_warning(
48                        &mut *self.context,
49                        Code::Code_ForRange,
50                        range_location,
51                        format_args!(
52                            "For loop ends at {} instead of {}; did you forget to specify step?",
53                            self.get_loop_end((*fc).value, (*tc).value),
54                            (*tc).value
55                        ),
56                    );
57                } else if !fc.is_null()
58                    && !tu.is_null()
59                    && (*fc).value == 0.0
60                    && (*tu).op == AstExprUnaryOp::Len
61                {
62                    emit_warning(
63                        &mut *self.context,
64                        Code::Code_ForRange,
65                        range_location,
66                        format_args!("For loop starts at 0, but arrays start at 1"),
67                    );
68                } else if !fu.is_null()
69                    && (*fu).op == AstExprUnaryOp::Len
70                    && !tc.is_null()
71                    && (*tc).value == 0.0
72                {
73                    emit_warning(
74                        &mut *self.context,
75                        Code::Code_ForRange,
76                        range_location,
77                        format_args!("For loop should iterate backwards; did you forget to specify -1 as step? Also consider changing 0 to 1 since arrays start at 1"),
78                    );
79                }
80            }
81        }
82
83        true
84    }
85}