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