luaur_analysis/methods/
lint_unreachable_code_analyze.rs1use 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}