Skip to main content

luaur_analysis/methods/
frontend_get_source_node.rs

1use crate::functions::get_timestamp::get_timestamp;
2use crate::functions::trace_requires::trace_requires;
3use crate::records::file_resolver::FileResolver;
4use crate::records::frontend::Frontend;
5use crate::records::source_module::SourceModule;
6use crate::records::source_node::SourceNode;
7use crate::records::type_check_limits::TypeCheckLimits;
8use crate::type_aliases::module_name_file_resolver::ModuleName;
9use alloc::sync::Arc;
10use luaur_common::macros::luau_timetrace_argument::LUAU_TIMETRACE_ARGUMENT;
11use luaur_common::macros::luau_timetrace_scope::LUAU_TIMETRACE_SCOPE;
12use luaur_config::records::config::Config;
13
14impl Frontend {
15    pub fn get_source_node(
16        &mut self,
17        name: &ModuleName,
18        limits: &TypeCheckLimits,
19    ) -> (*mut SourceNode, *mut SourceModule) {
20        let already_present = match self.source_nodes.get(name) {
21            Some(node) if !node.has_dirty_source_module() => {
22                let node_ptr = Arc::as_ptr(node) as *mut SourceNode;
23                if let Some(module) = self.source_modules.get(name) {
24                    return (node_ptr, Arc::as_ptr(module) as *mut SourceModule);
25                } else {
26                    // LUAU_ASSERT(!"Everything in sourceNodes should also be in sourceModules");
27                    return (node_ptr, core::ptr::null_mut());
28                }
29            }
30            Some(_) => true,
31            None => false,
32        };
33
34        LUAU_TIMETRACE_SCOPE!("Frontend::getSourceNode", "Frontend");
35        LUAU_TIMETRACE_ARGUMENT!("name", name.as_str());
36
37        let timestamp = get_timestamp();
38
39        let source = unsafe { FileResolver::read_source(self.file_resolver, name) };
40        let environment_name =
41            unsafe { FileResolver::get_environment_for_module(self.file_resolver, name) };
42
43        self.stats.time_read += get_timestamp() - timestamp;
44
45        let source = match source {
46            Some(source) => source,
47            None => {
48                self.source_modules.remove(name);
49                return (core::ptr::null_mut(), core::ptr::null_mut());
50            }
51        };
52
53        let mut opts = {
54            let config: &Config = unsafe {
55                let get_config = (*self.config_resolver)
56                    .get_config
57                    .expect("ConfigResolver::getConfig is not set");
58                &*get_config(self.config_resolver, name, limits)
59            };
60            config.parse_options.clone()
61        };
62        opts.capture_comments = true;
63        let mut result =
64            self.parse_module_name_string_view_parse_options(name, &source.source, &opts);
65        result.r#type = source.r#type;
66
67        let require = trace_requires(self.file_resolver, result.root, name.clone(), limits);
68        self.require_trace.insert(name.clone(), require.clone());
69
70        // std::shared_ptr<SourceNode>& sourceNode = sourceNodes[name];
71        // if (!sourceNode) sourceNode = std::make_shared<SourceNode>();
72        let source_node_arc = self
73            .source_nodes
74            .entry(name.clone())
75            .or_insert_with(|| Arc::new(default_source_node()));
76        let source_node_ptr = Arc::as_ptr(source_node_arc) as *mut SourceNode;
77
78        let source_module_arc = self
79            .source_modules
80            .entry(name.clone())
81            .or_insert_with(|| Arc::new(SourceModule::source_module()));
82        let source_module_ptr = Arc::as_ptr(source_module_arc) as *mut SourceModule;
83
84        unsafe {
85            // *sourceModule = std::move(result);
86            *source_module_ptr = result;
87            (*source_module_ptr).environment_name = environment_name;
88
89            (*source_node_ptr).name = (*source_module_ptr).name.clone();
90            (*source_node_ptr).human_readable_name =
91                (*source_module_ptr).human_readable_name.clone();
92
93            // clear all prior dependents; re-add after parsing the rest of the graph.
94            // C++: `depIt->second->dependents.erase(sourceNode->name);`
95            // The Rust `dependents` set has no `erase`, so we rebuild it without `self_name`
96            // (behaviorally identical to a single-key erase).
97            let prior_locations = (*source_node_ptr).require_locations.clone();
98            let self_name = (*source_node_ptr).name.clone();
99            for (module_name, _) in prior_locations.iter() {
100                if let Some(dep) = self.source_nodes.get(module_name) {
101                    let dep_ptr = Arc::as_ptr(dep) as *mut SourceNode;
102                    let retained: alloc::vec::Vec<ModuleName> = (*dep_ptr)
103                        .dependents
104                        .iter()
105                        .filter(|n| **n != self_name)
106                        .cloned()
107                        .collect();
108                    (*dep_ptr).dependents.clear();
109                    for n in retained {
110                        (*dep_ptr).dependents.insert(n);
111                    }
112                }
113            }
114
115            (*source_node_ptr).require_set.clear();
116            (*source_node_ptr).require_locations.clear();
117            (*source_node_ptr).dirty_source_module = false;
118
119            // `it == sourceNodes.end()` in the C++ corresponds to the node not having
120            // existed prior to this call (a brand-new source node).
121            if !already_present {
122                (*source_node_ptr).dirty_module = true;
123                (*source_node_ptr).dirty_module_for_autocomplete = true;
124            }
125
126            for (module_name, _) in require.require_list.iter() {
127                (*source_node_ptr).require_set.insert(module_name.clone());
128            }
129
130            (*source_node_ptr).require_locations = require.require_list.clone();
131        }
132
133        (source_node_ptr, source_module_ptr)
134    }
135}
136
137fn default_source_node() -> SourceNode {
138    use luaur_common::records::dense_hash_set::DenseHashSet;
139    SourceNode {
140        name: ModuleName::default(),
141        human_readable_name: alloc::string::String::new(),
142        require_set: DenseHashSet::new(ModuleName::default()),
143        require_locations: alloc::vec::Vec::new(),
144        dependents: DenseHashSet::new(ModuleName::default()),
145        dirty_source_module: true,
146        dirty_module: true,
147        dirty_module_for_autocomplete: true,
148        invalid_module_dependency: true,
149        invalid_module_dependency_for_autocomplete: true,
150        autocomplete_limits_mult: 1.0,
151    }
152}