1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//! This module contains some supplemental functions for dealing with errors.

#[llvm_versions(3.6..3.8)]
use llvm_sys::core::{LLVMInstallFatalErrorHandler, LLVMResetFatalErrorHandler};
#[llvm_versions(3.8..=latest)]
use llvm_sys::error_handling::{LLVMInstallFatalErrorHandler, LLVMResetFatalErrorHandler};
use llvm_sys::core::{LLVMGetDiagInfoDescription, LLVMGetDiagInfoSeverity};
use llvm_sys::prelude::LLVMDiagnosticInfoRef;
use llvm_sys::LLVMDiagnosticSeverity;
use libc::c_void;

// REVIEW: Maybe it's possible to have a safe wrapper? If we can
// wrap the provided function input ptr into a &CStr somehow
// TODOC: Can be used like this:
// extern "C" fn print_before_exit(msg: *const i8) {
//    let c_str = unsafe { ::std::ffi::CStr::from_ptr(msg) };
//
//    eprintln!("LLVM fatally errored: {:?}", c_str);
// }
// unsafe {
//     install_fatal_error_handler(print_before_exit);
// }
// and will be called before LLVM calls C exit()
/// Installs an error handler to be called before LLVM exits.
pub unsafe fn install_fatal_error_handler(handler: extern "C" fn(*const i8)) {
    LLVMInstallFatalErrorHandler(Some(handler))
}

/// Resets LLVM's fatal error handler back to the default
pub fn reset_fatal_error_handler() {
    unsafe {
        LLVMResetFatalErrorHandler()
    }
}

pub(crate) struct DiagnosticInfo {
    diagnostic_info: LLVMDiagnosticInfoRef,
}

impl DiagnosticInfo {
    pub(crate) fn new(diagnostic_info: LLVMDiagnosticInfoRef) -> Self {
        DiagnosticInfo {
            diagnostic_info,
        }
    }

    pub(crate) fn get_description(&self) -> *mut i8 {
        unsafe {
            LLVMGetDiagInfoDescription(self.diagnostic_info)
        }
    }

    pub(crate) fn severity_is_error(&self) -> bool {
        unsafe {
            match LLVMGetDiagInfoSeverity(self.diagnostic_info) {
                LLVMDiagnosticSeverity::LLVMDSError => true,
                _ => false,
            }
        }
    }
}

// Assmuptions this handler makes:
// * A valid *mut *mut i8 is provided as the void_ptr (via context.set_diagnostic_handler)
//
// https://github.com/llvm-mirror/llvm/blob/master/tools/llvm-c-test/diagnostic.c was super useful
// for figuring out how to get this to work
pub(crate) extern "C" fn get_error_str_diagnostic_handler(diagnostic_info: LLVMDiagnosticInfoRef, void_ptr: *mut c_void) {
    let diagnostic_info = DiagnosticInfo::new(diagnostic_info);

    if diagnostic_info.severity_is_error() {
        let i8_ptr_ptr = void_ptr as *mut *mut c_void as *mut *mut i8;

        unsafe {
            *i8_ptr_ptr = diagnostic_info.get_description();
        }
    }
}