Skip to main content

luaur_ast/methods/
parser_parse_local.rs

1use crate::enums::type_lexer::Type;
2use crate::functions::is_enough_values::is_enough_values;
3use crate::records::ast_array::AstArray;
4use crate::records::ast_attr::AstAttr;
5use crate::records::ast_expr::AstExpr;
6use crate::records::ast_local::AstLocal;
7use crate::records::ast_stat::AstStat;
8use crate::records::ast_stat_local::AstStatLocal;
9use crate::records::ast_stat_local_function::AstStatLocalFunction;
10use crate::records::binding::Binding;
11use crate::records::cst_stat_local::CstStatLocal;
12use crate::records::cst_stat_local_function::CstStatLocalFunction;
13use crate::records::lexeme::Lexeme;
14use crate::records::location::Location;
15use crate::records::parser::Parser;
16use crate::records::position::Position;
17use crate::records::temp_vector::TempVector;
18
19impl Parser {
20    pub fn parse_local(
21        &mut self,
22        start: Location,
23        keyword_position: Position,
24        attributes: &AstArray<*mut AstAttr>,
25        is_const: bool,
26    ) -> *mut AstStat {
27        if !is_const {
28            self.next_lexeme();
29        }
30
31        if self.lexer.current().r#type == Type::ReservedFunction {
32            let mut match_function = *self.lexer.current();
33            self.next_lexeme();
34
35            // C++ patches matchFunction's column to where `local` starts so that
36            // `local function` and a column-0 `end` align for the missed-indentation
37            // "did you forget to close X" suspect heuristic. The port patched only a
38            // COPY (function_keyword_position) but passed the UNPATCHED match_function
39            // to parse_function_body, so the end-match compared the `function` keyword
40            // column (6) against `end` (0) and recorded the wrong suspect.
41            let function_keyword_position = match_function.location.begin;
42            if match_function.location.begin.line == start.begin.line {
43                match_function.location.begin.column = start.begin.column;
44            }
45
46            let name = self.parse_name("variable name");
47
48            self.match_recovery_stop_on_token[Type::ReservedEnd.0 as usize] += 1;
49
50            let (body, var) = self.parse_function_body(
51                false,
52                &match_function,
53                &name.name,
54                Some(&name),
55                attributes,
56                is_const,
57            );
58
59            self.match_recovery_stop_on_token[Type::ReservedEnd.0 as usize] -= 1;
60
61            let location = Location::new(start.begin, unsafe { (*body).base.base.location.end });
62
63            let node = unsafe {
64                (*self.allocator).alloc(AstStatLocalFunction::new(location, var, body, is_const))
65            };
66
67            if self.options.store_cst_data {
68                let cst_node = unsafe {
69                    (*self.allocator).alloc(CstStatLocalFunction::new(
70                        keyword_position,
71                        function_keyword_position,
72                    ))
73                };
74                self.cst_node_map.try_insert(
75                    node as *mut crate::records::ast_node::AstNode,
76                    cst_node as *mut crate::records::cst_node::CstNode,
77                );
78            }
79
80            return node as *mut AstStat;
81        } else {
82            if attributes.size != 0 {
83                return self.report_stat_error(
84                    self.lexer.current().location,
85                    AstArray {
86                        data: core::ptr::null_mut(),
87                        size: 0,
88                    },
89                    AstArray {
90                        data: core::ptr::null_mut(),
91                        size: 0,
92                    },
93                    format_args!(
94                        "Expected 'function' after local declaration with attribute, but got {} instead",
95                        self.lexer.current().to_string()
96                    ),
97                ) as *mut AstStat;
98            }
99
100            self.match_recovery_stop_on_token[Type::Operator.0 as usize] += 1;
101
102            let mut names = TempVector::new(&mut self.scratch_binding);
103            let mut vars_comma_positions = AstArray {
104                data: core::ptr::null_mut(),
105                size: 0,
106            };
107
108            if self.options.store_cst_data {
109                self.parse_binding_list(
110                    &mut names,
111                    false,
112                    &mut vars_comma_positions,
113                    core::ptr::null_mut(),
114                    core::ptr::null_mut(),
115                    is_const,
116                );
117            } else {
118                self.parse_binding_list(
119                    &mut names,
120                    false,
121                    core::ptr::null_mut(),
122                    core::ptr::null_mut(),
123                    core::ptr::null_mut(),
124                    is_const,
125                );
126            }
127
128            self.match_recovery_stop_on_token[Type::Operator.0 as usize] -= 1;
129
130            let mut vars = TempVector::new(&mut self.scratch_local);
131            let mut values = TempVector::new(&mut self.scratch_expr);
132            let mut values_comma_positions = TempVector::new(&mut self.scratch_position);
133
134            let mut equals_sign_location = None;
135
136            if self.lexer.current().r#type == Type::Operator {
137                equals_sign_location = Some(self.lexer.current().location);
138                self.next_lexeme();
139
140                if self.options.store_cst_data {
141                    self.parse_expr_list(&mut values, Some(&mut values_comma_positions));
142                } else {
143                    self.parse_expr_list(&mut values, None);
144                }
145            }
146
147            for i in 0..names.size() {
148                vars.push_back(self.push_local(names.operator_index(i)));
149            }
150
151            let end = if values.empty() {
152                *self.lexer.previous_location()
153            } else {
154                unsafe { (**values.back()).base.location }
155            };
156
157            if luaur_common::FFlag::LuauConstJustReportErrorForUnderfill.get() {
158                let node = unsafe {
159                    (*self.allocator).alloc(AstStatLocal::new(
160                        Location::new(start.begin, end.end),
161                        self.copy_temp_vector_t(&vars),
162                        self.copy_temp_vector_t(&values),
163                        equals_sign_location,
164                        is_const,
165                    ))
166                };
167
168                if self.options.store_cst_data {
169                    let cst_node = unsafe {
170                        (*self.allocator).alloc(CstStatLocal::new(
171                            self.extract_annotation_colon_positions(&names),
172                            vars_comma_positions,
173                            self.copy_temp_vector_t(&values_comma_positions),
174                        ))
175                    };
176                    self.cst_node_map.try_insert(
177                        node as *mut crate::records::ast_node::AstNode,
178                        cst_node as *mut crate::records::cst_node::CstNode,
179                    );
180                }
181
182                if is_const && !is_enough_values(&mut values, vars.size()) {
183                    self.report(
184                        unsafe { (*node).base.base.location },
185                        format_args!("Missing initializer in const declaration"),
186                    );
187                }
188
189                return node as *mut AstStat;
190            } else {
191                if is_const && !is_enough_values(&mut values, vars.size()) {
192                    return self.report_stat_error(
193                        Location::new(start.begin, end.end),
194                        AstArray {
195                            data: core::ptr::null_mut(),
196                            size: 0,
197                        },
198                        AstArray {
199                            data: core::ptr::null_mut(),
200                            size: 0,
201                        },
202                        format_args!("Missing initializer in const declaration"),
203                    ) as *mut AstStat;
204                }
205
206                let node = unsafe {
207                    (*self.allocator).alloc(AstStatLocal::new(
208                        Location::new(start.begin, end.end),
209                        self.copy_temp_vector_t(&vars),
210                        self.copy_temp_vector_t(&values),
211                        equals_sign_location,
212                        is_const,
213                    ))
214                };
215
216                if self.options.store_cst_data {
217                    let cst_node = unsafe {
218                        (*self.allocator).alloc(CstStatLocal::new(
219                            self.extract_annotation_colon_positions(&names),
220                            vars_comma_positions,
221                            self.copy_temp_vector_t(&values_comma_positions),
222                        ))
223                    };
224                    self.cst_node_map.try_insert(
225                        node as *mut crate::records::ast_node::AstNode,
226                        cst_node as *mut crate::records::cst_node::CstNode,
227                    );
228                }
229
230                return node as *mut AstStat;
231            }
232        }
233    }
234}