Skip to main content

luaur_analysis/methods/
frontend_check_build_queue_item.rs

1use crate::enums::solver_mode::SolverMode;
2use crate::functions::apply_internal_limit_scaling::apply_internal_limit_scaling;
3use crate::functions::copy_errors::copy_errors;
4use crate::functions::filter_lint_options::filter_lint_options;
5use crate::functions::freeze::freeze;
6use crate::functions::get_timestamp::get_timestamp;
7use crate::functions::lint::lint;
8use crate::functions::make_type_check_limits::make_type_check_limits;
9use crate::functions::unfreeze::unfreeze;
10use crate::records::build_queue_item::BuildQueueItem;
11use crate::records::frontend::Frontend;
12use crate::records::module::Module;
13use crate::records::module_has_cyclic_dependency::ModuleHasCyclicDependency;
14use crate::records::source_node::SourceNode;
15use crate::records::syntax_error::SyntaxError;
16use crate::records::type_error::TypeError;
17use crate::type_aliases::type_error_data::TypeErrorData;
18use alloc::sync::Arc;
19use alloc::vec::Vec;
20use luaur_ast::enums::mode::Mode;
21use luaur_common::macros::luau_timetrace_scope::LUAU_TIMETRACE_SCOPE;
22use luaur_common::FFlag;
23use luaur_common::FInt;
24
25impl Frontend {
26    pub fn check_build_queue_item(&mut self, item: &mut BuildQueueItem) {
27        // SAFETY: `item.source_node` / `item.source_module` are `Arc`s owned by the queue.
28        // The C++ takes them by reference and mutates `sourceModule.mode` in place; we mirror
29        // that with raw pointers through the `Arc`.
30        let source_node_ptr = Arc::as_ptr(&item.source_node) as *mut SourceNode;
31
32        let mode: Mode = if FFlag::DebugLuauForceStrictMode.get() {
33            Mode::Strict
34        } else if FFlag::DebugLuauForceNonStrictMode.get() {
35            Mode::Nonstrict
36        } else {
37            item.source_module.mode.unwrap_or(item.config.mode)
38        };
39
40        unsafe {
41            let source_module_ptr = Arc::as_ptr(&item.source_module)
42                as *mut crate::records::source_module::SourceModule;
43            (*source_module_ptr).mode = Some(mode);
44        }
45
46        let environment_scope = item.environment_scope.clone();
47        let timestamp = get_timestamp();
48        let require_cycles = item.require_cycles.clone();
49
50        let mut type_check_limits = make_type_check_limits(&item.options);
51
52        // TODO (C++ comment retained): dirty ad hoc solution for autocomplete timeouts.
53        if item.options.apply_internal_limit_scaling {
54            let autocomplete_mult = unsafe { (*source_node_ptr).autocomplete_limits_mult };
55
56            if FInt::LuauTarjanChildLimit.get() > 0 {
57                type_check_limits.instantiationChildLimit = Some(
58                    1.max((FInt::LuauTarjanChildLimit.get() as f64 * autocomplete_mult) as i32),
59                );
60            } else {
61                type_check_limits.instantiationChildLimit = None;
62            }
63
64            if FInt::LuauTypeInferIterationLimit.get() > 0 {
65                type_check_limits.unifierIterationLimit = Some(1.max(
66                    (FInt::LuauTypeInferIterationLimit.get() as f64 * autocomplete_mult) as i32,
67                ));
68            } else {
69                type_check_limits.unifierIterationLimit = None;
70            }
71        }
72
73        if item.options.for_autocomplete {
74            // The autocomplete typecheck is always in strict mode with DM awareness.
75            let module_for_autocomplete = self.check_source_module_mode_vector_require_cycle_optional_scope_ptr_bool_bool_frontend_stats_type_check_limits(
76                &item.source_module,
77                Mode::Strict,
78                require_cycles,
79                Some(environment_scope.clone()),
80                /* for_autocomplete */ true,
81                /* record_json_log */ false,
82                &mut item.stats,
83                type_check_limits,
84            );
85
86            let duration = get_timestamp() - timestamp;
87            let module_ptr = Arc::as_ptr(&module_for_autocomplete) as *mut Module;
88            unsafe {
89                (*module_ptr).check_duration_sec = duration;
90            }
91
92            self.populate_expected_types(&item.source_module, module_ptr, &environment_scope);
93
94            if item.options.module_time_limit_sec.is_some()
95                && item.options.apply_internal_limit_scaling
96            {
97                apply_internal_limit_scaling(
98                    unsafe { &mut *source_node_ptr },
99                    module_for_autocomplete.clone(),
100                    item.options.module_time_limit_sec.unwrap(),
101                );
102            }
103
104            item.stats.time_check += duration;
105            item.stats.files_strict += 1;
106
107            if item.options.collect_type_allocation_stats {
108                let m = &*module_for_autocomplete;
109                item.stats.types_allocated += m.internal_types.types.size();
110                item.stats.type_packs_allocated += m.internal_types.type_packs.size();
111                item.stats.bool_singletons_minted += m.internal_types.bool_singletons_minted;
112                item.stats.str_singletons_minted += m.internal_types.str_singletons_minted;
113                item.stats.unique_str_singletons_minted +=
114                    m.internal_types.unique_str_singletons_minted.size();
115            }
116
117            if let Some(custom_module_check) = item.options.custom_module_check {
118                custom_module_check(&item.source_module, &module_for_autocomplete);
119            }
120
121            item.module = module_for_autocomplete;
122            return;
123        }
124
125        let module = self.check_source_module_mode_vector_require_cycle_optional_scope_ptr_bool_bool_frontend_stats_type_check_limits(
126            &item.source_module,
127            mode,
128            require_cycles.clone(),
129            Some(environment_scope.clone()),
130            /* for_autocomplete */ false,
131            item.record_json_log,
132            &mut item.stats,
133            type_check_limits,
134        );
135
136        let duration = get_timestamp() - timestamp;
137        let module_ptr: *mut Module = Arc::as_ptr(&module) as *mut Module;
138        unsafe {
139            (*module_ptr).check_duration_sec = duration;
140        }
141
142        self.populate_expected_types(&item.source_module, module_ptr, &environment_scope);
143
144        if item.options.module_time_limit_sec.is_some() && item.options.apply_internal_limit_scaling
145        {
146            apply_internal_limit_scaling(
147                unsafe { &mut *source_node_ptr },
148                module.clone(),
149                item.options.module_time_limit_sec.unwrap(),
150            );
151        }
152
153        item.stats.time_check += duration;
154        item.stats.files_strict += if mode == Mode::Strict { 1 } else { 0 };
155        item.stats.files_nonstrict += if mode == Mode::Nonstrict { 1 } else { 0 };
156
157        if item.options.collect_type_allocation_stats {
158            let m = &*module;
159            item.stats.types_allocated += m.internal_types.types.size();
160            item.stats.type_packs_allocated += m.internal_types.type_packs.size();
161            item.stats.bool_singletons_minted += m.internal_types.bool_singletons_minted;
162            item.stats.str_singletons_minted += m.internal_types.str_singletons_minted;
163            item.stats.unique_str_singletons_minted +=
164                m.internal_types.unique_str_singletons_minted.size();
165        }
166
167        if let Some(custom_module_check) = item.options.custom_module_check {
168            custom_module_check(&item.source_module, &module);
169        }
170
171        if self.get_luau_solver_mode() == SolverMode::New && mode == Mode::NoCheck {
172            unsafe {
173                (*module_ptr).errors.clear();
174            }
175        }
176
177        if item.options.run_lint_checks {
178            LUAU_TIMETRACE_SCOPE!("lint", "Frontend");
179
180            let mut lint_options = item
181                .options
182                .enabled_lint_warnings
183                .clone()
184                .unwrap_or(item.config.enabled_lint);
185            filter_lint_options(&mut lint_options, &item.source_module.hotcomments, mode);
186
187            let lint_timestamp = get_timestamp();
188
189            let warnings = lint(
190                item.source_module.root as *mut luaur_ast::records::ast_stat::AstStat,
191                item.source_module.names.as_ref(),
192                &environment_scope,
193                module_ptr as *const Module,
194                &item.source_module.hotcomments,
195                &lint_options,
196            );
197
198            item.stats.time_lint += get_timestamp() - lint_timestamp;
199
200            let lint_result = self.classify_lints(&warnings, &item.config);
201            unsafe {
202                (*module_ptr).lint_result = lint_result;
203            }
204        }
205
206        if !item.options.retain_full_type_graphs {
207            // copyErrors needs to allocate into interfaceTypes as it copies types out of
208            // internalTypes, so we unfreeze it here.
209            unsafe {
210                unfreeze(&mut (*module_ptr).interface_types);
211                // copyErrors(module->errors, module->interfaceTypes, builtinTypes);
212                let mut errors = core::mem::take(&mut (*module_ptr).errors);
213                copy_errors(
214                    &mut errors,
215                    &mut (*module_ptr).interface_types,
216                    &*self.builtin_types,
217                );
218                (*module_ptr).errors = errors;
219                freeze(&mut (*module_ptr).interface_types);
220
221                (*module_ptr).internal_types.clear();
222                (*module_ptr).def_arena.allocator.clear();
223                (*module_ptr).key_arena.allocator.clear();
224
225                (*module_ptr).ast_types.clear();
226                (*module_ptr).ast_type_packs.clear();
227                (*module_ptr).ast_expected_types.clear();
228                (*module_ptr).ast_original_call_types.clear();
229                (*module_ptr).ast_overload_resolved_types.clear();
230                (*module_ptr).ast_for_in_next_types.clear();
231                (*module_ptr).ast_resolved_types.clear();
232                (*module_ptr).ast_resolved_type_packs.clear();
233                (*module_ptr).ast_compound_assign_result_types.clear();
234                (*module_ptr).ast_scopes.clear();
235                (*module_ptr).upper_bound_contributors.clear();
236                (*module_ptr).scopes.clear();
237            }
238        }
239
240        if mode != Mode::NoCheck {
241            for cyc in &require_cycles {
242                let te = TypeError {
243                    location: cyc.location,
244                    module_name: item.name.clone(),
245                    data: TypeErrorData::ModuleHasCyclicDependency(ModuleHasCyclicDependency::new(
246                        cyc.path.clone(),
247                    )),
248                };
249                unsafe {
250                    (*module_ptr).errors.push(te);
251                }
252            }
253        }
254
255        let mut parse_errors: Vec<TypeError> = Vec::new();
256        for pe in &item.source_module.parse_errors {
257            parse_errors.push(TypeError {
258                location: *pe.get_location(),
259                module_name: item.name.clone(),
260                data: TypeErrorData::SyntaxError(SyntaxError::new(alloc::string::String::from(
261                    pe.what(),
262                ))),
263            });
264        }
265        unsafe {
266            // module->errors.insert(module->errors.begin(), parseErrors.begin(), parseErrors.end());
267            let mut combined = parse_errors;
268            combined.append(&mut (*module_ptr).errors);
269            (*module_ptr).errors = combined;
270        }
271
272        item.module = module;
273    }
274}