Skip to main content

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, RuleMetadata, 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    metadata: RuleMetadata,
62}
63
64impl FlawlessRule for ConvertLocalFunctionToAssign {
65    fn flawless_process(&self, block: &mut Block, _: &Context) {
66        let mut processor = Processor;
67        DefaultVisitor::visit_block(block, &mut processor);
68    }
69}
70
71impl RuleConfiguration for ConvertLocalFunctionToAssign {
72    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
73        verify_no_rule_properties(&properties)?;
74
75        Ok(())
76    }
77
78    fn get_name(&self) -> &'static str {
79        CONVERT_LOCAL_FUNCTION_TO_ASSIGN_RULE_NAME
80    }
81
82    fn serialize_to_properties(&self) -> RuleProperties {
83        RuleProperties::new()
84    }
85
86    fn set_metadata(&mut self, metadata: RuleMetadata) {
87        self.metadata = metadata;
88    }
89
90    fn metadata(&self) -> &RuleMetadata {
91        &self.metadata
92    }
93}
94
95impl Serialize for ConvertLocalFunctionToAssign {
96    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
97        serializer.serialize_str(CONVERT_LOCAL_FUNCTION_TO_ASSIGN_RULE_NAME)
98    }
99}
100
101#[cfg(test)]
102mod test {
103    use super::*;
104
105    use crate::rules::Rule;
106
107    use insta::assert_json_snapshot;
108
109    fn new_rule() -> ConvertLocalFunctionToAssign {
110        ConvertLocalFunctionToAssign::default()
111    }
112
113    #[test]
114    fn serialize_default_rule() {
115        assert_json_snapshot!(new_rule(), @r###""convert_local_function_to_assign""###);
116    }
117
118    #[test]
119    fn configure_with_extra_field_error() {
120        let result = json5::from_str::<Box<dyn Rule>>(
121            r#"{
122            rule: 'convert_local_function_to_assign',
123            prop: "something",
124        }"#,
125        );
126        insta::assert_snapshot!(result.unwrap_err().to_string(), @"unexpected field 'prop' at line 1 column 1")
127    }
128}