Skip to content

Commit d2bb231

Browse files
committed
Prune stacktraces for tag-tracking diagnostics too
1 parent 538aedf commit d2bb231

File tree

1 file changed

+53
-41
lines changed

1 file changed

+53
-41
lines changed

src/diagnostics.rs

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,54 @@ enum DiagLevel {
7575
Note,
7676
}
7777

78+
fn has_local_frame(stacktrace: &[FrameInfo<'_>]) -> bool {
79+
stacktrace.iter().any(|frame| frame.instance.def_id().is_local())
80+
}
81+
82+
fn prune_stacktrace<'mir, 'tcx>(
83+
ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
84+
mut stacktrace: Vec<FrameInfo<'tcx>>,
85+
) -> Vec<FrameInfo<'tcx>> {
86+
match ecx.machine.backtrace_style {
87+
BacktraceStyle::Off => {
88+
// Retain one frame so that we can print a span for the error itself
89+
stacktrace.truncate(1);
90+
}
91+
BacktraceStyle::Short => {
92+
// Only prune frames if there is at least one local frame. This check ensures that if
93+
// we get a backtrace that never makes it to the user code because it has detected a
94+
// bug in the Rust runtime, we don't prune away every frame.
95+
if has_local_frame(&stacktrace) {
96+
// This is part of the logic that `std` uses to select the relevant part of a
97+
// backtrace. But here, we only look for __rust_begin_short_backtrace, not
98+
// __rust_end_short_backtrace because the end symbol comes from a call to the default
99+
// panic handler.
100+
stacktrace = stacktrace
101+
.into_iter()
102+
.take_while(|frame| {
103+
let def_id = frame.instance.def_id();
104+
let path = ecx.tcx.tcx.def_path_str(def_id);
105+
!path.contains("__rust_begin_short_backtrace")
106+
})
107+
.collect::<Vec<_>>();
108+
109+
// After we prune frames from the bottom, there are a few left that are part of the
110+
// Rust runtime. So we remove frames until we get to a local symbol, which should be
111+
// main or a test.
112+
// This len check ensures that we don't somehow remove every frame, as doing so breaks
113+
// the primary error message.
114+
while stacktrace.len() > 1
115+
&& stacktrace.last().map_or(false, |e| !e.instance.def_id().is_local())
116+
{
117+
stacktrace.pop();
118+
}
119+
}
120+
}
121+
BacktraceStyle::Full => {}
122+
}
123+
stacktrace
124+
}
125+
78126
/// Emit a custom diagnostic without going through the miri-engine machinery
79127
pub fn report_error<'tcx, 'mir>(
80128
ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
@@ -157,46 +205,8 @@ pub fn report_error<'tcx, 'mir>(
157205
}
158206
};
159207

160-
let mut stacktrace = ecx.generate_stacktrace();
161-
let has_local_frame = stacktrace.iter().any(|frame| frame.instance.def_id().is_local());
162-
match ecx.machine.backtrace_style {
163-
BacktraceStyle::Off => {
164-
// Retain one frame so that we can print a span for the error itself
165-
stacktrace.truncate(1);
166-
}
167-
BacktraceStyle::Short => {
168-
// Only prune frames if there is at least one local frame. This check ensures that if
169-
// we get a backtrace that never makes it to the user code because it has detected a
170-
// bug in the Rust runtime, we don't prune away every frame.
171-
if has_local_frame {
172-
// This is part of the logic that `std` uses to select the relevant part of a
173-
// backtrace. But here, we only look for __rust_begin_short_backtrace, not
174-
// __rust_end_short_backtrace because the end symbol comes from a call to the default
175-
// panic handler.
176-
stacktrace = stacktrace
177-
.into_iter()
178-
.take_while(|frame| {
179-
let def_id = frame.instance.def_id();
180-
let path = ecx.tcx.tcx.def_path_str(def_id);
181-
!path.contains("__rust_begin_short_backtrace")
182-
})
183-
.collect::<Vec<_>>();
184-
185-
// After we prune frames from the bottom, there are a few left that are part of the
186-
// Rust runtime. So we remove frames until we get to a local symbol, which should be
187-
// main or a test.
188-
// This len check ensures that we don't somehow remove every frame, as doing so breaks
189-
// the primary error message.
190-
while stacktrace.len() > 1
191-
&& stacktrace.last().map_or(false, |e| !e.instance.def_id().is_local())
192-
{
193-
stacktrace.pop();
194-
}
195-
}
196-
}
197-
BacktraceStyle::Full => {}
198-
}
199-
208+
let stacktrace = ecx.generate_stacktrace();
209+
let stacktrace = prune_stacktrace(ecx, stacktrace);
200210
e.print_backtrace();
201211
let msg = e.to_string();
202212
report_msg(
@@ -210,7 +220,7 @@ pub fn report_error<'tcx, 'mir>(
210220

211221
// Include a note like `std` does for short backtraces, but since we are opt-out not opt-in, we
212222
// do not include a note when backtraces are off.
213-
if ecx.machine.backtrace_style == BacktraceStyle::Short && has_local_frame {
223+
if ecx.machine.backtrace_style == BacktraceStyle::Short && has_local_frame(&stacktrace) {
214224
ecx.tcx.sess.diagnostic().note_without_error(
215225
"some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace",
216226
);
@@ -367,6 +377,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
367377
);
368378
}
369379

380+
let stacktrace = prune_stacktrace(this, stacktrace);
381+
370382
// Show diagnostics.
371383
for e in diagnostics.drain(..) {
372384
use NonHaltingDiagnostic::*;

0 commit comments

Comments
 (0)