Skip to main content

luaur_analysis/methods/
frontend_load_definition_file.rs

1//! C++ `Frontend::loadDefinitionFile` (`Analysis/src/Frontend.cpp:218-247`).
2use crate::functions::parse_source_for_module::parse_source_for_module;
3use crate::functions::persist_checked_types::persist_checked_types;
4use crate::records::frontend::{Frontend, FrontendStats};
5use crate::records::global_types::GlobalTypes;
6use crate::records::load_definition_file_result::LoadDefinitionFileResult;
7use crate::records::require_cycle::RequireCycle;
8use crate::records::source_module::SourceModule;
9use crate::records::type_check_limits::TypeCheckLimits;
10use crate::type_aliases::scope_ptr_type::ScopePtr;
11use alloc::string::String;
12use alloc::vec::Vec;
13use luaur_ast::enums::mode::Mode;
14use luaur_common::macros::luau_timetrace_scope::LUAU_TIMETRACE_SCOPE;
15
16impl Frontend {
17    pub fn load_definition_file(
18        &mut self,
19        globals: &mut GlobalTypes,
20        target_scope: ScopePtr,
21        source: &str,
22        package_name: String,
23        capture_comments: bool,
24        _type_check_for_autocomplete: bool,
25    ) -> LoadDefinitionFileResult {
26        LUAU_TIMETRACE_SCOPE!("loadDefinitionFile", "Frontend");
27
28        let mut source_module = SourceModule::source_module();
29        source_module.name = package_name.clone();
30        source_module.human_readable_name = package_name.clone();
31
32        let parse_result = parse_source_for_module(source, &mut source_module, capture_comments);
33        if !parse_result.errors.is_empty() {
34            return LoadDefinitionFileResult {
35                success: false,
36                parse_result,
37                source_module,
38                module: None,
39            };
40        }
41
42        let mut dummy_stats = FrontendStats::default();
43        let checked_module = self.check_source_module_mode_vector_require_cycle_optional_scope_ptr_bool_bool_frontend_stats_type_check_limits(
44            &source_module,
45            Mode::Definition,
46            Vec::<RequireCycle>::new(),
47            None,
48            /* forAutocomplete */ false,
49            /* recordJsonLog */ false,
50            &mut dummy_stats,
51            TypeCheckLimits::default(),
52        );
53
54        if !checked_module.errors.is_empty() {
55            return LoadDefinitionFileResult {
56                success: false,
57                parse_result,
58                source_module,
59                module: Some(checked_module),
60            };
61        }
62
63        persist_checked_types(checked_module.clone(), globals, target_scope, package_name);
64
65        // Retain the checked module so its `TypeArena` — which owns the types
66        // just persisted into the global scope — outlives the returned
67        // `LoadDefinitionFileResult`. Without this, dropping the result (e.g. at
68        // the end of `register_builtin_globals`, which loads `"@luau"` twice)
69        // frees the arena while the global scope still references its types: a
70        // use-after-free the type checker then reads, SIGSEGVing on some
71        // toolchains (issue #6). Append-only so a repeated package name does not
72        // evict an earlier, still-referenced module.
73        globals.retained_modules.push(checked_module.clone());
74
75        LoadDefinitionFileResult {
76            success: true,
77            parse_result,
78            source_module,
79            module: Some(checked_module),
80        }
81    }
82}