Skip to main content

luaur_analysis/methods/
magic_format_handle_old_solver.rs

1use crate::functions::flatten_type_pack::flatten_type_pack_id;
2use crate::functions::parse_format_string::parse_format_string;
3use crate::records::count_mismatch::CountMismatch;
4use crate::records::type_arena::TypeArena;
5use crate::records::type_checker::TypeChecker;
6use crate::records::type_error::TypeError;
7use crate::records::with_predicate::WithPredicate;
8use crate::type_aliases::scope_ptr_type_infer::ScopePtr;
9use crate::type_aliases::type_id::TypeId;
10use crate::type_aliases::type_pack_id::TypePackId;
11use alloc::vec::Vec;
12use luaur_ast::records::ast_expr_call::AstExprCall;
13use luaur_ast::records::ast_expr_constant_string::AstExprConstantString;
14use luaur_ast::records::ast_expr_group::AstExprGroup;
15use luaur_ast::records::ast_expr_index_name::AstExprIndexName;
16use luaur_ast::records::location::Location;
17use luaur_ast::rtti::ast_node_as;
18
19pub fn magic_format_handle_old_solver(
20    typechecker: &mut TypeChecker,
21    scope: &ScopePtr,
22    expr: &AstExprCall,
23    with_predicate: WithPredicate<TypePackId>,
24) -> Option<WithPredicate<TypePackId>> {
25    let (param_pack, _predicates) = (with_predicate.r#type, with_predicate.predicates);
26
27    let module = typechecker.current_module.as_ref()?;
28    let arena = unsafe {
29        &mut (*(std::sync::Arc::as_ptr(module) as *mut crate::records::module::Module))
30            .internal_types
31    };
32
33    let mut fmt: *mut AstExprConstantString = core::ptr::null_mut();
34
35    if expr.self_ {
36        let index = unsafe {
37            ast_node_as::<AstExprIndexName>(expr.func as *mut luaur_ast::records::ast_node::AstNode)
38        };
39        if !index.is_null() {
40            let group = unsafe {
41                ast_node_as::<AstExprGroup>(
42                    (*index).expr as *mut luaur_ast::records::ast_node::AstNode,
43                )
44            };
45            if !group.is_null() {
46                fmt = unsafe {
47                    ast_node_as::<AstExprConstantString>(
48                        (*group).expr as *mut luaur_ast::records::ast_node::AstNode,
49                    )
50                };
51            } else {
52                fmt = unsafe {
53                    ast_node_as::<AstExprConstantString>(
54                        (*index).expr as *mut luaur_ast::records::ast_node::AstNode,
55                    )
56                };
57            }
58        }
59    }
60
61    if !expr.self_ && expr.args.size > 0 {
62        fmt = unsafe {
63            ast_node_as::<AstExprConstantString>(
64                unsafe { *expr.args.data.add(0) } as *mut luaur_ast::records::ast_node::AstNode
65            )
66        };
67    }
68
69    if fmt.is_null() {
70        return None;
71    }
72
73    let expected: Vec<TypeId> = unsafe {
74        parse_format_string(
75            core::ptr::NonNull::new_unchecked(typechecker.builtin_types),
76            (*fmt).value.data,
77            (*fmt).value.size,
78        )
79    };
80
81    let (params, tail) = flatten_type_pack_id(param_pack);
82
83    let param_offset: usize = 1;
84    let data_offset: usize = if expr.self_ { 0 } else { 1 };
85
86    for i in 0..expected.len() {
87        if i + param_offset >= params.len() {
88            break;
89        }
90        // No argument expressions ⇒ nothing to attach a location to, and
91        // `args.size - 1` would underflow (the self-call path lacks the
92        // `args.size > 0` guard the non-self path has).
93        if expr.args.size == 0 {
94            break;
95        }
96
97        let arg_index = std::cmp::min(i + data_offset, expr.args.size as usize - 1);
98        let location = unsafe { &(*(*expr.args.data.add(arg_index as usize))).base.location };
99
100        typechecker.unify_type_id_type_id_scope_ptr_location(
101            params[i + param_offset],
102            expected[i],
103            scope,
104            location,
105        );
106    }
107
108    let num_actual_params = params.len();
109    let num_expected_params = expected.len() + 1;
110
111    if num_expected_params != num_actual_params
112        && (!tail.is_some() || num_expected_params < num_actual_params)
113    {
114        let error = TypeError::type_error_location_type_error_data(
115            expr.base.base.location,
116            crate::records::type_error_data::TypeErrorData::CountMismatch(CountMismatch {
117                expected: num_expected_params,
118                maximum: None,
119                actual: num_actual_params,
120                context: crate::records::count_mismatch::CountMismatchContext::Arg,
121                is_variadic: false,
122                function: String::new(),
123            }),
124        );
125        typechecker.report_error_type_error(&error);
126    }
127
128    Some(WithPredicate::with_predicate_t(
129        arena.add_type_pack_initializer_list_type_id(&[typechecker.string_type]),
130    ))
131}