darklua_core/rules/
unused_if_branch.rs1use crate::nodes::{Block, DoStatement, Expression, IfExpression, IfStatement, Statement};
2use crate::process::{DefaultVisitor, Evaluator, NodeProcessor, NodeVisitor};
3use crate::rules::{
4 Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties,
5};
6
7use super::verify_no_rule_properties;
8
9enum FilterResult {
10 Keep,
11 Remove,
12 Replace(Box<Statement>),
13}
14
15#[derive(Debug, Clone, Default)]
16struct IfFilter {
17 evaluator: Evaluator,
18}
19
20impl IfFilter {
21 fn simplify_if_statement(&self, if_statement: &mut IfStatement) -> FilterResult {
22 if let Some(else_block) = if_statement.get_else_block() {
23 if else_block.is_empty() {
24 if_statement.take_else_block();
25 }
26 }
27
28 let mut keep_next_branches = true;
29 let mut replace_else_with = None;
30
31 let is_empty = if_statement.retain_branches_mut(|branch| {
32 keep_next_branches && {
33 let branch_condition_value = self.evaluator.evaluate(branch.get_condition());
34 match branch_condition_value.is_truthy() {
35 Some(true) => {
36 keep_next_branches = false;
37
38 if self.evaluator.has_side_effects(branch.get_condition()) {
39 true
40 } else {
41 replace_else_with = Some(branch.take_block());
42 false
43 }
44 }
45 Some(false) => {
46 if self.evaluator.has_side_effects(branch.get_condition()) {
47 branch.take_block();
48 true
49 } else {
50 false
51 }
52 }
53 None => true,
54 }
55 }
56 });
57
58 if is_empty {
59 if let Some(block_replacer) = replace_else_with {
60 if block_replacer.is_empty() {
61 FilterResult::Remove
62 } else {
63 FilterResult::Replace(Box::new(DoStatement::new(block_replacer).into()))
64 }
65 } else if let Some(else_block) = if_statement.take_else_block() {
66 if else_block.is_empty() {
67 FilterResult::Remove
68 } else {
69 FilterResult::Replace(Box::new(DoStatement::new(else_block).into()))
70 }
71 } else {
72 FilterResult::Remove
73 }
74 } else {
75 if !keep_next_branches {
76 if let Some(block_replacer) = replace_else_with {
77 if_statement.set_else_block(block_replacer);
78 } else {
79 if_statement.take_else_block();
80 }
81 }
82 FilterResult::Keep
83 }
84 }
85
86 fn simplify_if(&self, if_expression: &mut IfExpression) -> Option<Expression> {
87 let condition_value = self.evaluator.evaluate(if_expression.get_condition());
88 match condition_value.is_truthy() {
89 Some(true) => {
90 if self
91 .evaluator
92 .has_side_effects(if_expression.get_condition())
93 {
94 if_expression.clear_elseif_branches();
95 *if_expression.mutate_else_result() = Self::result_placeholder();
96 None
97 } else {
98 let result = if_expression.get_result().clone();
99
100 Some(if self.evaluator.can_return_multiple_values(&result) {
101 result.in_parentheses()
102 } else {
103 result
104 })
105 }
106 }
107 Some(false) => {
108 if self
109 .evaluator
110 .has_side_effects(if_expression.get_condition())
111 {
112 *if_expression.mutate_result() = Self::result_placeholder();
113 None
114 } else if let Some(branch) = if_expression.remove_branch(0) {
115 let (new_condition, new_result) = branch.into_expressions();
116 *if_expression.mutate_condition() = new_condition;
117 *if_expression.mutate_result() = new_result;
118 self.simplify_if(if_expression)
119 } else {
120 let result = if_expression.get_else_result().clone();
121
122 Some(if self.evaluator.can_return_multiple_values(&result) {
123 result.in_parentheses()
124 } else {
125 result
126 })
127 }
128 }
129 None => {
130 let mut keep_next_branches = true;
131 let mut replace_else_with = None;
132
133 if_expression.retain_elseif_branches_mut(|branch| {
134 keep_next_branches && {
135 let branch_condition_value =
136 self.evaluator.evaluate(branch.get_condition());
137 match branch_condition_value.is_truthy() {
138 Some(true) => {
139 keep_next_branches = false;
140
141 if self.evaluator.has_side_effects(branch.get_condition()) {
142 true
143 } else {
144 replace_else_with = Some(branch.get_result().clone());
145 false
146 }
147 }
148 Some(false) => {
149 if self.evaluator.has_side_effects(branch.get_condition()) {
150 *branch.mutate_result() = Self::result_placeholder();
151 true
152 } else {
153 false
154 }
155 }
156 None => true,
157 }
158 }
159 });
160
161 if !keep_next_branches {
162 *if_expression.mutate_else_result() =
163 replace_else_with.unwrap_or_else(Self::result_placeholder);
164 }
165 None
166 }
167 }
168 }
169
170 fn result_placeholder() -> Expression {
171 Expression::nil()
172 }
173}
174
175impl NodeProcessor for IfFilter {
176 fn process_block(&mut self, block: &mut Block) {
177 block.filter_mut_statements(|statement| {
178 if let Statement::If(if_statement) = statement {
179 match self.simplify_if_statement(if_statement) {
180 FilterResult::Keep => true,
181 FilterResult::Remove => false,
182 FilterResult::Replace(new_statement) => {
183 *statement = *new_statement;
184 true
185 }
186 }
187 } else {
188 true
189 }
190 });
191 }
192
193 fn process_expression(&mut self, expression: &mut Expression) {
194 if let Expression::If(if_expression) = expression {
195 if let Some(replace_with) = self.simplify_if(if_expression) {
196 *expression = replace_with;
197 }
198 }
199 }
200}
201
202pub const REMOVE_UNUSED_IF_BRANCH_RULE_NAME: &str = "remove_unused_if_branch";
203
204#[derive(Debug, Default, PartialEq, Eq)]
207pub struct RemoveUnusedIfBranch {}
208
209impl FlawlessRule for RemoveUnusedIfBranch {
210 fn flawless_process(&self, block: &mut Block, _: &Context) {
211 let mut processor = IfFilter::default();
212 DefaultVisitor::visit_block(block, &mut processor);
213 }
214}
215
216impl RuleConfiguration for RemoveUnusedIfBranch {
217 fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
218 verify_no_rule_properties(&properties)?;
219
220 Ok(())
221 }
222
223 fn get_name(&self) -> &'static str {
224 REMOVE_UNUSED_IF_BRANCH_RULE_NAME
225 }
226
227 fn serialize_to_properties(&self) -> RuleProperties {
228 RuleProperties::new()
229 }
230}
231
232#[cfg(test)]
233mod test {
234 use super::*;
235 use crate::rules::Rule;
236
237 use insta::assert_json_snapshot;
238
239 fn new_rule() -> RemoveUnusedIfBranch {
240 RemoveUnusedIfBranch::default()
241 }
242
243 #[test]
244 fn serialize_default_rule() {
245 let rule: Box<dyn Rule> = Box::new(new_rule());
246
247 assert_json_snapshot!("default_remove_unused_if_branch", rule);
248 }
249
250 #[test]
251 fn configure_with_extra_field_error() {
252 let result = json5::from_str::<Box<dyn Rule>>(
253 r#"{
254 rule: 'remove_unused_if_branch',
255 prop: "something",
256 }"#,
257 );
258 pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
259 }
260}