Skip to main content

luaur_analyze_cli/methods/
cli_file_resolver_resolve_module.rs

1use crate::records::cli_file_resolver::CliFileResolver;
2use crate::records::file_navigation_context::FileNavigationContext;
3use crate::records::luau_config_interrupt_info::LuauConfigInterruptInfo;
4use alloc::boxed::Box;
5use alloc::string::String;
6use luaur_analysis::records::module_info::ModuleInfo;
7use luaur_analysis::records::type_check_limits::TypeCheckLimits;
8use luaur_ast::records::ast_expr::AstExpr;
9use luaur_ast::records::ast_expr_constant_string::AstExprConstantString;
10use luaur_ast::records::ast_node::AstNode;
11use luaur_ast::rtti::ast_node_as;
12use luaur_require::enums::status_require_navigator::Status;
13use luaur_require::records::error_handler::ErrorHandler;
14use luaur_require::records::navigator::Navigator;
15
16/// `Luau::Require::ErrorHandler nullErrorHandler{};` — a no-op error handler
17/// (`CLI/src/Analyze.cpp:207`).
18struct NullErrorHandler;
19
20impl ErrorHandler for NullErrorHandler {
21    fn report_error(&mut self, _message: String) {}
22}
23
24impl CliFileResolver {
25    /// C++ `std::optional<ModuleInfo> resolveModule(const ModuleInfo* context, AstExpr* node, const TypeCheckLimits& limits)`
26    /// (`CLI/src/Analyze.cpp:185-221`).
27    pub unsafe fn resolve_module(
28        &mut self,
29        context: *const ModuleInfo,
30        expr: *mut AstExpr,
31        limits: &TypeCheckLimits,
32    ) -> Option<ModuleInfo> {
33        // if (AstExprConstantString* expr = node->as<AstExprConstantString>())
34        let const_str = ast_node_as::<AstExprConstantString>(expr as *mut AstNode);
35        if const_str.is_null() {
36            return None;
37        }
38
39        // std::string path{expr->value.data, expr->value.size};
40        let bytes: &[u8] = {
41            let slice = (*const_str).value.as_slice();
42            core::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len())
43        };
44        let path = String::from_utf8_lossy(bytes).into_owned();
45
46        // FileNavigationContext navigationContext{context->name};
47        let mut navigation_context = FileNavigationContext::new((*context).name.clone());
48
49        // LuauConfigInterruptInfo info = {limits, path};
50        // navigationContext.luauConfigInit / luauConfigInterrupt capture &info.
51        navigation_context.interrupt_info = Some(Box::new(LuauConfigInterruptInfo {
52            limits: limits.clone(),
53            module: path.clone(),
54        }));
55
56        let mut null_error_handler = NullErrorHandler;
57
58        // Require::Navigator navigator(navigationContext, nullErrorHandler);
59        let mut navigator = Navigator::new(&mut navigation_context, &mut null_error_handler);
60
61        // if (navigator.navigate(std::move(path)) != Status::Success) return std::nullopt;
62        if navigator.navigate(path) != Status::Success {
63            return None;
64        }
65
66        // The navigator borrows navigation_context for its lifetime; drop it before
67        // reading back the (now-mutated) context.
68        drop(navigator);
69
70        // if (!navigationContext.isModulePresent()) return std::nullopt;
71        if !crate::methods::file_navigation_context_is_module_present::file_navigation_context_is_module_present(
72            &navigation_context,
73        ) {
74            return None;
75        }
76
77        // if (std::optional<std::string> identifier = navigationContext.getIdentifier())
78        //     return {{*identifier}};
79        if let Some(identifier) =
80            crate::methods::file_navigation_context_get_identifier::file_navigation_context_get_identifier(
81                &navigation_context,
82            )
83        {
84            return Some(ModuleInfo {
85                name: identifier,
86                optional: false,
87            });
88        }
89
90        None
91    }
92}