darklua_core/rules/
no_local_function.rs

1use crate::nodes::{
2    Block, FunctionExpression, LocalAssignStatement, LocalFunctionStatement, Statement,
3};
4use crate::process::{processors::FindVariables, DefaultVisitor, NodeProcessor, NodeVisitor};
5use crate::rules::{
6    Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties,
7};
8
9use serde::ser::{Serialize, Serializer};
10use std::mem;
11
12use super::verify_no_rule_properties;
13
14struct Processor;
15
16impl Processor {
17    fn convert(&self, local_function: &mut LocalFunctionStatement) -> Statement {
18        let mut function_expression = FunctionExpression::default();
19        function_expression.set_variadic(local_function.is_variadic());
20        mem::swap(
21            function_expression.mutate_block(),
22            local_function.mutate_block(),
23        );
24        mem::swap(
25            function_expression.mutate_parameters(),
26            local_function.mutate_parameters(),
27        );
28
29        LocalAssignStatement::from_variable(local_function.get_name())
30            .with_value(function_expression)
31            .into()
32    }
33}
34
35impl NodeProcessor for Processor {
36    fn process_statement(&mut self, statement: &mut Statement) {
37        if let Statement::LocalFunction(local_function) = statement {
38            let name = local_function.get_name().to_owned();
39
40            if local_function.has_parameter(&name) {
41                let mut assign = self.convert(local_function);
42                mem::swap(statement, &mut assign)
43            } else {
44                let mut find_usage = FindVariables::new(&name);
45                DefaultVisitor::visit_block(local_function.mutate_block(), &mut find_usage);
46
47                if !find_usage.has_found_usage() {
48                    let mut assign = self.convert(local_function);
49                    mem::swap(statement, &mut assign)
50                }
51            }
52        };
53    }
54}
55
56pub const CONVERT_LOCAL_FUNCTION_TO_ASSIGN_RULE_NAME: &str = "convert_local_function_to_assign";
57
58/// Convert local function statements into local assignements when the function is not recursive.
59#[derive(Debug, Default, PartialEq, Eq)]
60pub struct ConvertLocalFunctionToAssign {}
61
62impl FlawlessRule for ConvertLocalFunctionToAssign {
63    fn flawless_process(&self, block: &mut Block, _: &Context) {
64        let mut processor = Processor;
65        DefaultVisitor::visit_block(block, &mut processor);
66    }
67}
68
69impl RuleConfiguration for ConvertLocalFunctionToAssign {
70    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
71        verify_no_rule_properties(&properties)?;
72
73        Ok(())
74    }
75
76    fn get_name(&self) -> &'static str {
77        CONVERT_LOCAL_FUNCTION_TO_ASSIGN_RULE_NAME
78    }
79
80    fn serialize_to_properties(&self) -> RuleProperties {
81        RuleProperties::new()
82    }
83}
84
85impl Serialize for ConvertLocalFunctionToAssign {
86    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
87        serializer.serialize_str(CONVERT_LOCAL_FUNCTION_TO_ASSIGN_RULE_NAME)
88    }
89}
90
91#[cfg(test)]
92mod test {
93    use super::*;
94
95    use crate::rules::Rule;
96
97    use insta::assert_json_snapshot;
98
99    fn new_rule() -> ConvertLocalFunctionToAssign {
100        ConvertLocalFunctionToAssign::default()
101    }
102
103    #[test]
104    fn serialize_default_rule() {
105        assert_json_snapshot!("default_convert_local_function_to_assign", new_rule());
106    }
107
108    #[test]
109    fn configure_with_extra_field_error() {
110        let result = json5::from_str::<Box<dyn Rule>>(
111            r#"{
112            rule: 'convert_local_function_to_assign',
113            prop: "something",
114        }"#,
115        );
116        pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
117    }
118}