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 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 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(); 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 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(); 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}