1use crate::enums::solver_mode::SolverMode;
2use crate::enums::table_state::TableState;
3use crate::functions::assign_prop_documentation_symbols::assign_prop_documentation_symbols;
4use crate::functions::attach_magic_function::attach_magic_function;
5use crate::functions::get_mutable_type::get_mutable_type_id;
6use crate::functions::make_function_builtin_definitions_alt_b::make_function_type_arena_optional_type_id_initializer_list_type_id_initializer_list_type_pack_id_initializer_list_type_id_initializer_list_type_id_bool as make_function_poly;
7use crate::methods::magic_find_handle_old_solver::magic_find_handle_old_solver;
8use crate::methods::magic_format_handle_old_solver::magic_format_handle_old_solver;
9use crate::methods::magic_format_infer::magic_format_infer;
10use crate::methods::magic_format_type_check::magic_format_type_check;
11use crate::methods::magic_gmatch_handle_old_solver::magic_gmatch_handle_old_solver;
12use crate::methods::magic_match_handle_old_solver::magic_match_handle_old_solver;
13use crate::methods::magic_match_infer::magic_match_infer;
14use crate::records::builtin_types::BuiltinTypes;
15use crate::records::function_type::FunctionType;
16use crate::records::magic_find::MagicFind;
17use crate::records::magic_function::MagicFunction;
18use crate::records::magic_function_call_context::MagicFunctionCallContext;
19use crate::records::magic_gmatch::MagicGmatch;
20use crate::records::magic_refinement_context::MagicRefinementContext;
21use crate::records::property_type::Property;
22use crate::records::table_indexer::TableIndexer;
23use crate::records::table_type::TableType;
24use crate::records::type_arena::TypeArena;
25use crate::records::type_level::TypeLevel;
26use crate::records::type_pack::TypePack;
27use crate::records::union_type::UnionType;
28use crate::records::variadic_type_pack::VariadicTypePack;
29use crate::type_aliases::props_type::Props;
30use crate::type_aliases::type_id::TypeId;
31use crate::type_aliases::type_pack_id::TypePackId;
32use alloc::string::ToString;
33use alloc::sync::Arc;
34use alloc::vec;
35use alloc::vec::Vec;
36use core::ptr::NonNull;
37
38fn noop_refine(_context: &MagicRefinementContext) {}
40
41fn gmatch_infer_shim(context: &MagicFunctionCallContext) -> bool {
44 MagicGmatch {
45 base: MagicFunction {
46 handle_old_solver: magic_gmatch_handle_old_solver,
47 infer: gmatch_infer_shim,
48 refine: noop_refine,
49 type_check: |_| false,
50 },
51 }
52 .infer(context)
53}
54
55fn find_infer_shim(context: &MagicFunctionCallContext) -> bool {
56 MagicFind {
57 base: MagicFunction {
58 handle_old_solver: magic_find_handle_old_solver,
59 infer: find_infer_shim,
60 refine: noop_refine,
61 type_check: |_| false,
62 },
63 handle_old_solver: magic_find_handle_old_solver,
64 infer: find_infer_shim,
65 }
66 .infer(context)
67}
68
69fn make_format_magic() -> Arc<MagicFunction> {
70 Arc::new(MagicFunction {
71 handle_old_solver: magic_format_handle_old_solver,
72 infer: magic_format_infer,
73 refine: noop_refine,
74 type_check: magic_format_type_check,
75 })
76}
77
78fn make_gmatch_magic() -> Arc<MagicFunction> {
79 Arc::new(MagicFunction {
80 handle_old_solver: magic_gmatch_handle_old_solver,
81 infer: gmatch_infer_shim,
82 refine: noop_refine,
83 type_check: |_| false,
84 })
85}
86
87fn make_match_magic() -> Arc<MagicFunction> {
88 Arc::new(MagicFunction {
89 handle_old_solver: magic_match_handle_old_solver,
90 infer: magic_match_infer,
91 refine: noop_refine,
92 type_check: |_| false,
93 })
94}
95
96fn make_find_magic() -> Arc<MagicFunction> {
97 Arc::new(MagicFunction {
98 handle_old_solver: magic_find_handle_old_solver,
99 infer: find_infer_shim,
100 refine: noop_refine,
101 type_check: |_| false,
102 })
103}
104
105fn read_prop(ty: TypeId) -> Property {
106 Property::rw_type_id(ty)
107}
108
109pub fn make_string_metatable(mut builtin_types: NonNull<BuiltinTypes>, mode: SolverMode) -> TypeId {
110 let arena: &mut TypeArena =
112 unsafe { &mut *(&mut *builtin_types.as_mut().arena as *mut TypeArena) };
113 let builtin_types = unsafe { builtin_types.as_ref() };
114
115 let nil_type = builtin_types.nilType;
116 let number_type = builtin_types.numberType;
117 let boolean_type = builtin_types.booleanType;
118 let string_type = builtin_types.stringType;
119
120 let optional_number = arena.add_type(UnionType {
121 options: vec![nil_type, number_type],
122 });
123 let optional_string = arena.add_type(UnionType {
124 options: vec![nil_type, string_type],
125 });
126 let optional_boolean = arena.add_type(UnionType {
127 options: vec![nil_type, boolean_type],
128 });
129
130 let one_string_pack = arena.add_type_pack_initializer_list_type_id(&[string_type]);
131 let any_type_pack = builtin_types.anyTypePack;
132
133 let variadic_tail_pack: TypePackId = if mode == SolverMode::New {
134 builtin_types.unknownTypePack
135 } else {
136 any_type_pack
137 };
138 let empty_pack = arena.add_type_pack_initializer_list_type_id(&[]);
139 let string_variadic_list = arena.add_type_pack_t(VariadicTypePack {
140 ty: string_type,
141 hidden: false,
142 });
143 let number_variadic_list = arena.add_type_pack_t(VariadicTypePack {
144 ty: number_type,
145 hidden: false,
146 });
147
148 let mut format_ftv = FunctionType::function_type_new(
149 arena.add_type_pack_t(TypePack {
150 head: vec![string_type],
151 tail: Some(variadic_tail_pack),
152 }),
153 one_string_pack,
154 None,
155 false,
156 );
157 format_ftv.is_checked_function = true;
158 let format_fn = arena.add_type(format_ftv);
159 attach_magic_function(format_fn, make_format_magic());
160
161 let string_to_string_type = make_function_poly(
162 arena,
163 None,
164 Vec::new(),
165 Vec::new(),
166 vec![string_type],
167 vec![string_type],
168 true,
169 );
170
171 let repl_table = arena.add_type(TableType {
172 props: Props::default(),
173 indexer: Some(TableIndexer {
174 index_type: string_type,
175 index_result_type: string_type,
176 is_read_only: false,
177 }),
178 state: TableState::Generic,
179 level: TypeLevel::default(),
180 scope: core::ptr::null_mut(),
181 name: None,
182 synthetic_name: None,
183 instantiated_type_params: Vec::new(),
184 instantiated_type_pack_params: Vec::new(),
185 definition_module_name: Default::default(),
186 definition_location: Default::default(),
187 bound_to: None,
188 tags: Default::default(),
189 remaining_props: 0,
190 });
191 let repl_fn = make_function_poly(
192 arena,
193 None,
194 Vec::new(),
195 Vec::new(),
196 vec![string_type],
197 vec![string_type],
198 false,
199 );
200 let repl_arg_type = arena.add_type(UnionType {
201 options: vec![string_type, repl_table, repl_fn],
202 });
203 let gsub_func = make_function_poly(
204 arena,
205 Some(string_type),
206 Vec::new(),
207 Vec::new(),
208 vec![string_type, repl_arg_type, optional_number],
209 vec![string_type, number_type],
210 false,
211 );
212
213 let gmatch_iter_ret = arena.add_type(FunctionType::function_type_new(
214 empty_pack,
215 string_variadic_list,
216 None,
217 false,
218 ));
219 let gmatch_func = make_function_poly(
220 arena,
221 Some(string_type),
222 Vec::new(),
223 Vec::new(),
224 vec![string_type],
225 vec![gmatch_iter_ret],
226 true,
227 );
228 attach_magic_function(gmatch_func, make_gmatch_magic());
229
230 let mut match_func_ty = FunctionType::function_type_new(
231 arena.add_type_pack_initializer_list_type_id(&[string_type, string_type, optional_number]),
232 arena.add_type_pack_t(VariadicTypePack {
233 ty: string_type,
234 hidden: false,
235 }),
236 None,
237 false,
238 );
239 match_func_ty.is_checked_function = true;
240 let match_func = arena.add_type(match_func_ty);
241 attach_magic_function(match_func, make_match_magic());
242
243 let mut find_func_ty = FunctionType::function_type_new(
244 arena.add_type_pack_initializer_list_type_id(&[
245 string_type,
246 string_type,
247 optional_number,
248 optional_boolean,
249 ]),
250 arena.add_type_pack_t(TypePack {
251 head: vec![optional_number, optional_number],
252 tail: Some(string_variadic_list),
253 }),
254 None,
255 false,
256 );
257 find_func_ty.is_checked_function = true;
258 let find_func = arena.add_type(find_func_ty);
259 attach_magic_function(find_func, make_find_magic());
260
261 let mut string_dot_byte = FunctionType::function_type_new(
263 arena.add_type_pack_initializer_list_type_id(&[
264 string_type,
265 optional_number,
266 optional_number,
267 ]),
268 number_variadic_list,
269 None,
270 false,
271 );
272 string_dot_byte.is_checked_function = true;
273
274 let mut string_dot_char = FunctionType::function_type_new(
276 number_variadic_list,
277 arena.add_type_pack_initializer_list_type_id(&[string_type]),
278 None,
279 false,
280 );
281 string_dot_char.is_checked_function = true;
282
283 let mut string_dot_unpack = FunctionType::function_type_new(
285 arena.add_type_pack_t(TypePack {
286 head: vec![string_type, string_type, optional_number],
287 tail: None,
288 }),
289 variadic_tail_pack,
290 None,
291 false,
292 );
293 string_dot_unpack.is_checked_function = true;
294
295 let byte_fn = arena.add_type(string_dot_byte);
296 let char_fn = arena.add_type(string_dot_char);
297 let len_fn = make_function_poly(
298 arena,
299 Some(string_type),
300 Vec::new(),
301 Vec::new(),
302 Vec::new(),
303 vec![number_type],
304 true,
305 );
306 let rep_fn = make_function_poly(
307 arena,
308 Some(string_type),
309 Vec::new(),
310 Vec::new(),
311 vec![number_type],
312 vec![string_type],
313 true,
314 );
315 let sub_fn = make_function_poly(
316 arena,
317 Some(string_type),
318 Vec::new(),
319 Vec::new(),
320 vec![number_type, optional_number],
321 vec![string_type],
322 true,
323 );
324 let split_ret_table = arena.add_type(TableType {
325 props: Props::default(),
326 indexer: Some(TableIndexer {
327 index_type: number_type,
328 index_result_type: string_type,
329 is_read_only: false,
330 }),
331 state: TableState::Sealed,
332 level: TypeLevel::default(),
333 scope: core::ptr::null_mut(),
334 name: None,
335 synthetic_name: None,
336 instantiated_type_params: Vec::new(),
337 instantiated_type_pack_params: Vec::new(),
338 definition_module_name: Default::default(),
339 definition_location: Default::default(),
340 bound_to: None,
341 tags: Default::default(),
342 remaining_props: 0,
343 });
344 let split_fn = make_function_poly(
345 arena,
346 Some(string_type),
347 Vec::new(),
348 Vec::new(),
349 vec![optional_string],
350 vec![split_ret_table],
351 true,
352 );
353 let pack_arg_pack = arena.add_type_pack_t(TypePack {
354 head: vec![string_type],
355 tail: Some(variadic_tail_pack),
356 });
357 let pack_fn = arena.add_type(FunctionType::function_type_new(
358 pack_arg_pack,
359 one_string_pack,
360 None,
361 false,
362 ));
363 let packsize_fn = make_function_poly(
364 arena,
365 Some(string_type),
366 Vec::new(),
367 Vec::new(),
368 Vec::new(),
369 vec![number_type],
370 true,
371 );
372 let unpack_fn = arena.add_type(string_dot_unpack);
373
374 let mut string_lib = Props::default();
375 string_lib.insert("byte".to_string(), read_prop(byte_fn));
376 string_lib.insert("char".to_string(), read_prop(char_fn));
377 string_lib.insert("find".to_string(), read_prop(find_func));
378 string_lib.insert("format".to_string(), read_prop(format_fn)); string_lib.insert("gmatch".to_string(), read_prop(gmatch_func));
380 string_lib.insert("gsub".to_string(), read_prop(gsub_func));
381 string_lib.insert("len".to_string(), read_prop(len_fn));
382 string_lib.insert("lower".to_string(), read_prop(string_to_string_type));
383 string_lib.insert("match".to_string(), read_prop(match_func));
384 string_lib.insert("rep".to_string(), read_prop(rep_fn));
385 string_lib.insert("reverse".to_string(), read_prop(string_to_string_type));
386 string_lib.insert("sub".to_string(), read_prop(sub_fn));
387 string_lib.insert("upper".to_string(), read_prop(string_to_string_type));
388 string_lib.insert("split".to_string(), read_prop(split_fn));
389 string_lib.insert("pack".to_string(), read_prop(pack_fn));
390 string_lib.insert("packsize".to_string(), read_prop(packsize_fn));
391 string_lib.insert("unpack".to_string(), read_prop(unpack_fn));
392
393 assign_prop_documentation_symbols(&mut string_lib, "@luau/global/string");
394
395 let table_type = arena.add_type(TableType {
396 props: string_lib,
397 indexer: None,
398 state: TableState::Sealed,
399 level: TypeLevel::default(),
400 scope: core::ptr::null_mut(),
401 name: None,
402 synthetic_name: None,
403 instantiated_type_params: Vec::new(),
404 instantiated_type_pack_params: Vec::new(),
405 definition_module_name: Default::default(),
406 definition_location: Default::default(),
407 bound_to: None,
408 tags: Default::default(),
409 remaining_props: 0,
410 });
411
412 let ttv = unsafe { get_mutable_type_id::<TableType>(table_type) };
413 if !ttv.is_null() {
414 unsafe {
415 (*ttv).name = Some("typeof(string)".to_string());
416 }
417 }
418
419 let mut index_props = Props::default();
420 index_props.insert("__index".to_string(), Property::rw_type_id(table_type));
421 arena.add_type(TableType {
422 props: index_props,
423 indexer: None,
424 state: TableState::Sealed,
425 level: TypeLevel::default(),
426 scope: core::ptr::null_mut(),
427 name: None,
428 synthetic_name: None,
429 instantiated_type_params: Vec::new(),
430 instantiated_type_pack_params: Vec::new(),
431 definition_module_name: Default::default(),
432 definition_location: Default::default(),
433 bound_to: None,
434 tags: Default::default(),
435 remaining_props: 0,
436 })
437}