Skip to main content

luaur_analysis/records/
internal_compiler_error.rs

1use luaur_ast::records::location::Location;
2
3#[derive(Debug, Clone)]
4pub struct InternalCompilerError {
5    pub message: alloc::string::String,
6    pub module_name: Option<alloc::string::String>,
7    pub location: Option<Location>,
8    /// A NUL-terminated copy of `message` for the C++-style `what()`, which
9    /// returns `*const c_char` read with `CStr::from_ptr`. A Rust `String` is
10    /// not NUL-terminated, so `message.as_ptr()` would over-read past the buffer
11    /// (UB; flaky garbage across allocators). Built once at construction via
12    /// [`InternalCompilerError::new`] so the pointer stays valid for `&self`.
13    pub(crate) c_message: alloc::ffi::CString,
14}
15
16impl InternalCompilerError {
17    /// Build an `InternalCompilerError`, materializing the NUL-terminated
18    /// `what()` view from `message`.
19    pub fn new(
20        message: alloc::string::String,
21        module_name: Option<alloc::string::String>,
22        location: Option<Location>,
23    ) -> Self {
24        let c_message = nul_terminated(&message);
25        Self {
26            message,
27            module_name,
28            location,
29            c_message,
30        }
31    }
32}
33
34/// NUL-terminated C string from `s`, stripping any (never-expected) interior
35/// NULs so construction cannot fail even mid-panic.
36pub(crate) fn nul_terminated(s: &str) -> alloc::ffi::CString {
37    match alloc::ffi::CString::new(s) {
38        Ok(c) => c,
39        Err(_) => alloc::ffi::CString::new(s.replace('\0', "")).unwrap_or_default(),
40    }
41}
42
43unsafe impl Send for InternalCompilerError {}
44unsafe impl Sync for InternalCompilerError {}
45
46#[cfg(feature = "std")]
47impl std::error::Error for InternalCompilerError {}
48
49impl core::fmt::Display for InternalCompilerError {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        write!(f, "{}", self.message)
52    }
53}