]> git.lizzy.rs Git - rust.git/blob - src/diagnostics.rs
b2590a843ba9676ebb9b691060de71856d1e366d
[rust.git] / src / diagnostics.rs
1 use rustc_mir::interpret::InterpErrorInfo;
2 use std::cell::RefCell;
3
4 use crate::*;
5
6 /// Miri specific diagnostics
7 pub enum NonHaltingDiagnostic {
8     PoppedTrackedPointerTag(Item),
9     CreatedAlloc(AllocId),
10 }
11
12 /// Emit a custom diagnostic without going through the miri-engine machinery
13 pub fn report_diagnostic<'tcx, 'mir>(
14     ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
15     mut e: InterpErrorInfo<'tcx>,
16 ) -> Option<i64> {
17     // Special treatment for some error kinds
18     let msg = match e.kind {
19         InterpError::MachineStop(ref info) => {
20             let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
21             match info {
22                 TerminationInfo::Exit(code) => return Some(*code),
23                 TerminationInfo::Abort(None) => format!("the evaluated program aborted execution"),
24                 TerminationInfo::Abort(Some(msg)) => format!("the evaluated program aborted execution: {}", msg),
25             }
26         }
27         err_unsup!(NoMirFor(..)) => format!(
28             "{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.",
29             e
30         ),
31         InterpError::InvalidProgram(_) => bug!("This error should be impossible in Miri: {}", e),
32         _ => e.to_string(),
33     };
34     e.print_backtrace();
35     report_msg(ecx, msg, true)
36 }
37
38 /// Report an error or note (depending on the `error` argument) at the current frame's current statement.
39 /// Also emits a full stacktrace of the interpreter stack.
40 pub fn report_msg<'tcx, 'mir>(
41     ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
42     msg: String,
43     error: bool,
44 ) -> Option<i64> {
45     if let Some(frame) = ecx.stack().last() {
46         let span = frame.current_source_info().unwrap().span;
47
48         let mut err = if error {
49             let msg = format!("Miri evaluation error: {}", msg);
50             ecx.tcx.sess.struct_span_err(span, msg.as_str())
51         } else {
52             ecx.tcx.sess.diagnostic().span_note_diag(span, msg.as_str())
53         };
54         let frames = ecx.generate_stacktrace(None);
55         err.span_label(span, msg);
56         // We iterate with indices because we need to look at the next frame (the caller).
57         for idx in 0..frames.len() {
58             let frame_info = &frames[idx];
59             let call_site_is_local = frames
60                 .get(idx + 1)
61                 .map_or(false, |caller_info| caller_info.instance.def_id().is_local());
62             if call_site_is_local {
63                 err.span_note(frame_info.call_site, &frame_info.to_string());
64             } else {
65                 err.note(&frame_info.to_string());
66             }
67         }
68         err.emit();
69     } else {
70         ecx.tcx.sess.err(&msg);
71     }
72
73     for (i, frame) in ecx.stack().iter().enumerate() {
74         trace!("-------------------");
75         trace!("Frame {}", i);
76         trace!("    return: {:?}", frame.return_place.map(|p| *p));
77         for (i, local) in frame.locals.iter().enumerate() {
78             trace!("    local {}: {:?}", i, local.value);
79         }
80     }
81     // Let the reported error determine the return code.
82     return None;
83 }
84
85 thread_local! {
86     static DIAGNOSTICS: RefCell<Vec<NonHaltingDiagnostic>> = RefCell::new(Vec::new());
87 }
88
89 /// Schedule a diagnostic for emitting. This function works even if you have no `InterpCx` available.
90 /// The diagnostic will be emitted after the current interpreter step is finished.
91 pub fn register_diagnostic(e: NonHaltingDiagnostic) {
92     DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
93 }
94
95 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
96 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
97     /// Emit all diagnostics that were registed with `register_diagnostics`
98     fn process_diagnostics(&self) {
99         let this = self.eval_context_ref();
100         DIAGNOSTICS.with(|diagnostics| {
101             for e in diagnostics.borrow_mut().drain(..) {
102                 use NonHaltingDiagnostic::*;
103                 let msg = match e {
104                     PoppedTrackedPointerTag(item) =>
105                         format!("popped tracked tag for item {:?}", item),
106                     CreatedAlloc(AllocId(id)) =>
107                         format!("created allocation with id {}", id),
108                 };
109                 report_msg(this, msg, false);
110             }
111         });
112     }
113 }