Skip to main content

luaur_analysis/methods/
lint_unreachable_code_analyze.rs

1use crate::enums::status::Status;
2use crate::functions::does_call_error::does_call_error;
3use crate::functions::emit_warning::emit_warning;
4use crate::records::lint_unreachable_code::LintUnreachableCode;
5use luaur_ast::records::ast_expr_call::AstExprCall;
6use luaur_ast::records::ast_node::AstNode;
7use luaur_ast::records::ast_stat::AstStat;
8use luaur_ast::records::ast_stat_block::AstStatBlock;
9use luaur_ast::records::ast_stat_break::AstStatBreak;
10use luaur_ast::records::ast_stat_continue::AstStatContinue;
11use luaur_ast::records::ast_stat_expr::AstStatExpr;
12use luaur_ast::records::ast_stat_for::AstStatFor;
13use luaur_ast::records::ast_stat_for_in::AstStatForIn;
14use luaur_ast::records::ast_stat_if::AstStatIf;
15use luaur_ast::records::ast_stat_repeat::AstStatRepeat;
16use luaur_ast::records::ast_stat_return::AstStatReturn;
17use luaur_ast::records::ast_stat_while::AstStatWhile;
18use luaur_config::enums::code::Code;
19
20impl LintUnreachableCode {
21    pub fn analyze(&mut self, node: *mut AstStat) -> Status {
22        if node.is_null() {
23            return Status::Unknown;
24        }
25
26        let node_base = node as *mut AstNode;
27
28        let stat = unsafe { luaur_ast::rtti::ast_node_as::<AstStatBlock>(node_base) };
29        if !stat.is_null() {
30            let body = unsafe { (*stat).body };
31
32            for i in 0..body.size {
33                let si = unsafe { *body.data.add(i) };
34                let step = self.analyze(si);
35
36                if step != Status::Unknown {
37                    if i + 1 == body.size {
38                        return step;
39                    }
40
41                    let next = unsafe { *body.data.add(i + 1) };
42
43                    if step == Status::Error
44                        && unsafe {
45                            luaur_ast::rtti::ast_node_is::<AstStatExpr>(&(*si).base)
46                                && luaur_ast::rtti::ast_node_is::<AstStatReturn>(&(*next).base)
47                        }
48                        && i + 2 == body.size
49                    {
50                        return Status::Error;
51                    }
52
53                    emit_warning(
54                        unsafe { &mut *self.context },
55                        Code::Code_UnreachableCode,
56                        unsafe { (*next).base.location },
57                        format_args!(
58                            "Unreachable code (previous statement always {}s)",
59                            self.get_reason(step)
60                        ),
61                    );
62                    return step;
63                }
64            }
65
66            return Status::Unknown;
67        }
68
69        let stat = unsafe { luaur_ast::rtti::ast_node_as::<AstStatIf>(node_base) };
70        if !stat.is_null() {
71            let ifs = self.analyze(unsafe { (*stat).thenbody as *mut AstStat });
72            let elses = unsafe {
73                if (*stat).elsebody.is_null() {
74                    Status::Unknown
75                } else {
76                    self.analyze((*stat).elsebody)
77                }
78            };
79            return min_status(ifs, elses);
80        }
81
82        let stat = unsafe { luaur_ast::rtti::ast_node_as::<AstStatWhile>(node_base) };
83        if !stat.is_null() {
84            self.analyze(unsafe { (*stat).body as *mut AstStat });
85            return Status::Unknown;
86        }
87
88        let stat = unsafe { luaur_ast::rtti::ast_node_as::<AstStatRepeat>(node_base) };
89        if !stat.is_null() {
90            self.analyze(unsafe { (*stat).body as *mut AstStat });
91            return Status::Unknown;
92        }
93
94        if unsafe { luaur_ast::rtti::ast_node_is::<AstStatBreak>(node_base) } {
95            return Status::Break;
96        }
97
98        if unsafe { luaur_ast::rtti::ast_node_is::<AstStatContinue>(node_base) } {
99            return Status::Continue;
100        }
101
102        if unsafe { luaur_ast::rtti::ast_node_is::<AstStatReturn>(node_base) } {
103            return Status::Return;
104        }
105
106        let stat = unsafe { luaur_ast::rtti::ast_node_as::<AstStatExpr>(node_base) };
107        if !stat.is_null() {
108            let expr = unsafe { (*stat).expr };
109            let call = unsafe { luaur_ast::rtti::ast_node_as::<AstExprCall>(expr as *mut AstNode) };
110
111            if !call.is_null() && unsafe { does_call_error(&*call) } {
112                return Status::Error;
113            }
114
115            return Status::Unknown;
116        }
117
118        let stat = unsafe { luaur_ast::rtti::ast_node_as::<AstStatFor>(node_base) };
119        if !stat.is_null() {
120            self.analyze(unsafe { (*stat).body as *mut AstStat });
121            return Status::Unknown;
122        }
123
124        let stat = unsafe { luaur_ast::rtti::ast_node_as::<AstStatForIn>(node_base) };
125        if !stat.is_null() {
126            self.analyze(unsafe { (*stat).body as *mut AstStat });
127            return Status::Unknown;
128        }
129
130        Status::Unknown
131    }
132}
133
134fn min_status(lhs: Status, rhs: Status) -> Status {
135    if status_rank(lhs) <= status_rank(rhs) {
136        lhs
137    } else {
138        rhs
139    }
140}
141
142fn status_rank(status: Status) -> u8 {
143    match status {
144        Status::Unknown => 0,
145        Status::Continue => 1,
146        Status::Break => 2,
147        Status::Return => 3,
148        Status::Error => 4,
149    }
150}