Skip to main content

luaur_ast/methods/
parser_parse_declaration.rs

1use crate::enums::ast_table_access::AstTableAccess;
2use crate::records::ast_array::AstArray;
3use crate::records::ast_attr::AstAttr;
4use crate::records::ast_declared_extern_type_property::AstDeclaredExternTypeProperty;
5use crate::records::ast_name::AstName;
6use crate::records::ast_stat::AstStat;
7use crate::records::ast_stat_declare_extern_type::AstStatDeclareExternType;
8use crate::records::ast_stat_declare_function::AstStatDeclareFunction;
9use crate::records::ast_stat_declare_global::AstStatDeclareGlobal;
10use crate::records::ast_type_list::AstTypeList;
11use crate::records::ast_type_pack_explicit::AstTypePackExplicit;
12use crate::records::lexeme::Type;
13use crate::records::location::Location;
14use crate::records::match_lexeme::MatchLexeme;
15use crate::records::name::Name;
16use crate::records::parser::Parser;
17use crate::records::temp_vector::TempVector;
18use crate::type_aliases::ast_argument_name::AstArgumentName;
19use luaur_common::FFlag;
20
21impl Parser {
22    pub fn parse_declaration(
23        &mut self,
24        start: &Location,
25        attributes: &AstArray<*mut AstAttr>,
26    ) -> *mut AstStat {
27        // `declare` token is already parsed at this point
28
29        if (attributes.size != 0) && (self.lexer.current().r#type != Type::ReservedFunction) {
30            return self.report_stat_error(
31                self.lexer.current().location,
32                AstArray::default(),
33                AstArray::default(),
34                format_args!(
35                    "Expected a function type declaration after attribute, but got {} instead",
36                    self.lexer.current().to_string()
37                ),
38            ) as *mut AstStat;
39        }
40
41        if self.lexer.current().r#type == Type::ReservedFunction {
42            self.next_lexeme();
43
44            let global_name = self.parse_name("global function name");
45            let (generics, generic_packs) = self.parse_generic_type_list(false, None, None, None);
46
47            let match_paren = MatchLexeme::new(self.lexer.current());
48
49            self.expect_and_consume_type(Type('(' as i32), "global function declaration");
50
51            let mut args = TempVector::new(&mut self.scratch_binding);
52
53            let mut vararg = false;
54            let mut vararg_location = Location::default();
55            let mut vararg_annotation = core::ptr::null_mut();
56
57            if self.lexer.current().r#type != Type(')' as i32) {
58                let res = self.parse_binding_list(
59                    &mut args,
60                    true,
61                    core::ptr::null_mut(),
62                    core::ptr::null_mut(),
63                    core::ptr::null_mut(),
64                    false,
65                );
66                vararg = res.0;
67                vararg_location = res.1;
68                vararg_annotation = res.2;
69            }
70
71            self.expect_match_and_consume(')', &match_paren, false);
72
73            let mut ret_types = self.parse_optional_return_type(None);
74            if ret_types.is_null() {
75                ret_types = unsafe {
76                    (*self.allocator).alloc(AstTypePackExplicit::new(
77                        self.lexer.current().location,
78                        AstTypeList::default(),
79                    ))
80                } as *mut crate::records::ast_type_pack::AstTypePack;
81            }
82            let end = self.lexer.current().location;
83
84            let mut vars = TempVector::new(&mut self.scratch_type);
85            let mut var_names = TempVector::new(&mut self.scratch_arg_name);
86
87            for i in 0..args.size() {
88                if args[i].annotation.is_null() {
89                    return self.report_stat_error(
90                        Location::new(start.begin, end.end),
91                        AstArray::default(),
92                        AstArray::default(),
93                        format_args!("All declaration parameters must be annotated"),
94                    ) as *mut AstStat;
95                }
96
97                vars.push_back(args[i].annotation);
98                var_names.push_back((args[i].name.name, args[i].name.location));
99            }
100
101            if vararg && vararg_annotation.is_null() {
102                return self.report_stat_error(
103                    Location::new(start.begin, end.end),
104                    AstArray::default(),
105                    AstArray::default(),
106                    format_args!("All declaration parameters must be annotated"),
107                ) as *mut AstStat;
108            }
109
110            unsafe {
111                (*self.allocator).alloc(AstStatDeclareFunction {
112                    base: crate::records::ast_stat::AstStat::new(
113                        <AstStatDeclareFunction as crate::rtti::AstNodeClass>::CLASS_INDEX,
114                        Location::new(start.begin, end.end),
115                    ),
116                    attributes: *attributes,
117                    name: global_name.name,
118                    name_location: global_name.location,
119                    generics,
120                    generic_packs,
121                    params: AstTypeList {
122                        types: self.copy_temp_vector_t(&vars),
123                        tail_type: vararg_annotation,
124                    },
125                    param_names: self.copy_temp_vector_t(&var_names),
126                    vararg,
127                    vararg_location,
128                    ret_types,
129                }) as *mut AstStat
130            }
131        } else if (unsafe {
132            AstName::ast_name_c_char(self.lexer.current().data.name)
133                .operator_eq_c_char(c"class".as_ptr())
134        } && (if FFlag::LuauAllowGlobalDeclarationToBeCalledClass.get() {
135            self.lexer.lookahead().r#type != Type(':' as i32)
136        } else {
137            true
138        })) || unsafe {
139            AstName::ast_name_c_char(self.lexer.current().data.name)
140                .operator_eq_c_char(c"extern".as_ptr())
141        } {
142            let mut found_extern = false;
143            if unsafe {
144                AstName::ast_name_c_char(self.lexer.current().data.name)
145                    .operator_eq_c_char(c"extern".as_ptr())
146            } {
147                found_extern = true;
148                self.next_lexeme();
149                if unsafe {
150                    AstName::ast_name_c_char(self.lexer.current().data.name)
151                        .operator_ne_c_char(c"type".as_ptr())
152                } {
153                    return self.report_stat_error(
154                        self.lexer.current().location,
155                        AstArray::default(),
156                        AstArray::default(),
157                        format_args!(
158                            "Expected `type` keyword after `extern`, but got {} instead",
159                            unsafe {
160                                core::ffi::CStr::from_ptr(self.lexer.current().data.name)
161                                    .to_string_lossy()
162                            }
163                        ),
164                    ) as *mut AstStat;
165                }
166            }
167
168            self.next_lexeme();
169
170            let class_start = self.lexer.current().location;
171            let class_name = self.parse_name("type name");
172            let mut super_name: Option<AstName> = None;
173
174            if unsafe {
175                AstName::ast_name_c_char(self.lexer.current().data.name)
176                    .operator_eq_c_char(c"extends".as_ptr())
177            } {
178                self.next_lexeme();
179                super_name = Some(self.parse_name("supertype name").name);
180            }
181
182            if found_extern {
183                if unsafe {
184                    AstName::ast_name_c_char(self.lexer.current().data.name)
185                        .operator_ne_c_char(c"with".as_ptr())
186                } {
187                    self.report(
188                        self.lexer.current().location,
189                        format_args!(
190                            "Expected `with` keyword before listing properties of the external type, but got {} instead",
191                            unsafe { core::ffi::CStr::from_ptr(self.lexer.current().data.name).to_string_lossy() }
192                        )
193                    );
194                } else {
195                    self.next_lexeme();
196                }
197            }
198
199            let mut props = TempVector::new(&mut self.scratch_declared_class_props);
200            let mut indexer: *mut crate::records::ast_table_indexer::AstTableIndexer =
201                core::ptr::null_mut();
202
203            while self.lexer.current().r#type != Type::ReservedEnd {
204                let mut attributes = AstArray::<*mut AstAttr>::default();
205
206                if self.lexer.current().r#type == Type::Attribute
207                    || self.lexer.current().r#type == Type::AttributeOpen
208                {
209                    attributes = self.parse_attributes();
210
211                    if self.lexer.current().r#type != Type::ReservedFunction {
212                        return self.report_stat_error(
213                            self.lexer.current().location,
214                            AstArray::default(),
215                            AstArray::default(),
216                            format_args!(
217                                "Expected a method type declaration after attribute, but got {} instead",
218                                self.lexer.current().to_string()
219                            ),
220                        ) as *mut AstStat;
221                    }
222                }
223
224                // There are two possibilities: Either it's a property or a function.
225                if self.lexer.current().r#type == Type::ReservedFunction {
226                    props.push_back(self.parse_declared_extern_type_method(&attributes));
227                } else if self.lexer.current().r#type == Type('[' as i32) {
228                    let begin = self.lexer.current().clone();
229                    self.next_lexeme(); // [
230
231                    if (self.lexer.current().r#type == Type::RawString
232                        || self.lexer.current().r#type == Type::QuotedString)
233                        && self.lexer.lookahead().r#type == Type(']' as i32)
234                    {
235                        let name_begin = self.lexer.current().location;
236                        let chars = self.parse_char_array(None);
237
238                        let name_end = *self.lexer.previous_location();
239
240                        self.expect_match_and_consume(']', &MatchLexeme::new(&begin), false);
241                        self.expect_and_consume_char(':', "property type annotation");
242                        let ty = self.parse_type_bool(false);
243
244                        // since AstName contains a char*, it can't contain null
245                        let mut contains_null = false;
246                        if let Some(ref c) = chars {
247                            for &ch in c.as_slice() {
248                                if ch == 0 {
249                                    contains_null = true;
250                                    break;
251                                }
252                            }
253                        }
254
255                        if chars.is_some() && !contains_null {
256                            props.push_back(AstDeclaredExternTypeProperty {
257                                name: AstName {
258                                    value: chars.unwrap().data,
259                                },
260                                name_location: Location::new(name_begin.begin, name_end.end),
261                                ty,
262                                is_method: false,
263                                location: Location::new(
264                                    begin.location.begin,
265                                    self.lexer.previous_location().end,
266                                ),
267                                access: AstTableAccess::ReadWrite,
268                            });
269                        } else {
270                            self.report(
271                                begin.location,
272                                format_args!(
273                                    "String literal contains malformed escape sequence or \\0"
274                                ),
275                            );
276                        }
277                    } else if !indexer.is_null() {
278                        let bad_indexer_res =
279                            self.parse_table_indexer(AstTableAccess::ReadWrite, None, begin);
280                        let bad_indexer = bad_indexer_res.node;
281                        self.report(
282                            unsafe { (*bad_indexer).location },
283                            format_args!("Cannot have more than one indexer on an extern type"),
284                        );
285                    } else {
286                        indexer = self
287                            .parse_table_indexer(AstTableAccess::ReadWrite, None, begin)
288                            .node;
289                    }
290                } else {
291                    let mut access = AstTableAccess::ReadWrite;
292
293                    if self.lexer.current().r#type == Type::Name
294                        && self.lexer.lookahead().r#type != Type(':' as i32)
295                    {
296                        let current_name = unsafe { self.lexer.current().data.name };
297                        if AstName::ast_name_c_char(current_name)
298                            .operator_eq_c_char(c"read".as_ptr())
299                        {
300                            access = AstTableAccess::Read;
301                            self.lexer.next();
302                        } else if AstName::ast_name_c_char(current_name)
303                            .operator_eq_c_char(c"write".as_ptr())
304                        {
305                            access = AstTableAccess::Write;
306                            self.lexer.next();
307                        } else {
308                            self.report(
309                                self.lexer.current().location,
310                                format_args!(
311                                    "Expected blank or 'read' or 'write' attribute, got '{}'",
312                                    unsafe {
313                                        core::ffi::CStr::from_ptr(self.lexer.current().data.name)
314                                            .to_string_lossy()
315                                    }
316                                ),
317                            );
318                            self.lexer.next();
319                        }
320                    }
321
322                    let prop_start = self.lexer.current().location;
323                    let prop_name = self.parse_name_opt("property name");
324
325                    if prop_name.is_none() {
326                        break;
327                    }
328                    let prop_name = prop_name.unwrap();
329
330                    self.expect_and_consume_char(':', "property type annotation");
331                    let prop_type = self.parse_type_bool(false);
332                    props.push_back(AstDeclaredExternTypeProperty {
333                        name: prop_name.name,
334                        name_location: prop_name.location,
335                        ty: prop_type,
336                        is_method: false,
337                        location: Location::new(
338                            prop_start.begin,
339                            self.lexer.previous_location().end,
340                        ),
341                        access,
342                    });
343                }
344            }
345
346            let class_end = self.lexer.current().location;
347            self.next_lexeme(); // skip past `end`
348
349            unsafe {
350                (*self.allocator).alloc(AstStatDeclareExternType::new(
351                    Location::new(class_start.begin, class_end.end),
352                    class_name.name,
353                    super_name,
354                    self.copy_temp_vector_t(&props),
355                    indexer,
356                )) as *mut AstStat
357            }
358        } else if let Some(global_name) = self.parse_name_opt("global variable name") {
359            self.expect_and_consume_char(':', "global variable declaration");
360
361            let ty = self.parse_type_bool(true);
362            unsafe {
363                (*self.allocator).alloc(AstStatDeclareGlobal::new(
364                    Location::new(start.begin, (*ty).base.location.end),
365                    global_name.name,
366                    global_name.location,
367                    ty,
368                )) as *mut AstStat
369            }
370        } else {
371            self.report_stat_error(
372                *start,
373                AstArray::default(),
374                AstArray::default(),
375                format_args!(
376                    "declare must be followed by an identifier, 'function', or 'extern type'"
377                ),
378            ) as *mut AstStat
379        }
380    }
381}