Skip to content

Commit 3e78bce

Browse files
committed
Consider the cargo workspace when checking if a frame is local
1 parent a12a48b commit 3e78bce

File tree

5 files changed

+67
-20
lines changed

5 files changed

+67
-20
lines changed

cargo-miri/bin.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -482,12 +482,13 @@ path = "lib.rs"
482482
}
483483
}
484484

485-
/// Detect the target directory by calling `cargo metadata`.
486-
fn detect_target_dir() -> PathBuf {
487-
#[derive(Deserialize)]
488-
struct Metadata {
489-
target_directory: PathBuf,
490-
}
485+
#[derive(Deserialize)]
486+
struct Metadata {
487+
target_directory: PathBuf,
488+
workspace_members: Vec<String>,
489+
}
490+
491+
fn get_cargo_metadata() -> Metadata {
491492
let mut cmd = cargo();
492493
// `-Zunstable-options` is required by `--config`.
493494
cmd.args(["metadata", "--no-deps", "--format-version=1", "-Zunstable-options"]);
@@ -516,7 +517,6 @@ fn detect_target_dir() -> PathBuf {
516517
}
517518
metadata
518519
.unwrap_or_else(|e| show_error(format!("invalid `cargo metadata` output: {}", e)))
519-
.target_directory
520520
}
521521

522522
fn phase_cargo_miri(mut args: env::Args) {
@@ -595,8 +595,28 @@ fn phase_cargo_miri(mut args: env::Args) {
595595
}
596596
}
597597

598+
let metadata = get_cargo_metadata();
599+
600+
// Query cargo metadata for all the crates in this workspace, and if the caller hasn't already
601+
// specified which crates we should consider local, add those to the local set.
602+
// workspace members are emitted like "miri 0.1.0 (path+file:///path/to/miri)"
603+
// Additionally, somewhere between cargo-metadata and TyCtxt, - gets replaced with _
604+
assert!(metadata.workspace_members.len() > 0);
605+
let mut flags = env::var("MIRIFLAGS").unwrap_or_default();
606+
if !flags.contains("-Zmiri-local-crates=") {
607+
flags += " -Zmiri-local-crates=";
608+
for member in metadata.workspace_members {
609+
let name = member.split(" ").nth(0).unwrap();
610+
let name = name.replace("-", "_");
611+
flags.push_str(&name);
612+
flags.push(',');
613+
}
614+
flags.pop();
615+
}
616+
env::set_var("MIRIFLAGS", flags);
617+
598618
// Detect the target directory if it's not specified via `--target-dir`.
599-
let target_dir = target_dir.get_or_insert_with(detect_target_dir);
619+
let target_dir = target_dir.get_or_insert(metadata.target_directory);
600620

601621
// Set `--target-dir` to `miri` inside the original target directory.
602622
target_dir.push("miri");

src/bin/miri.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,10 @@ fn main() {
465465
_ => panic!("-Zmiri-backtrace may only be 0, 1, or full"),
466466
};
467467
}
468+
arg if arg.starts_with("-Zmiri-local-crates=") => {
469+
let crates = arg.strip_prefix("-Zmiri-local-crates=").unwrap();
470+
miri_config.local_crates.extend(crates.split(",").map(str::to_string));
471+
}
468472
_ => {
469473
// Forward to rustc.
470474
rustc_args.push(arg);

src/diagnostics.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::num::NonZeroU64;
44

55
use log::trace;
66

7-
use rustc_middle::ty::{self, TyCtxt};
7+
use rustc_middle::ty;
88
use rustc_span::{source_map::DUMMY_SP, Span, SpanData, Symbol};
99

1010
use crate::stacked_borrows::{AccessKind, SbTag};
@@ -93,7 +93,7 @@ fn prune_stacktrace<'mir, 'tcx>(
9393
// Only prune frames if there is at least one local frame. This check ensures that if
9494
// we get a backtrace that never makes it to the user code because it has detected a
9595
// bug in the Rust runtime, we don't prune away every frame.
96-
let has_local_frame = stacktrace.iter().any(|frame| frame.instance.def_id().is_local());
96+
let has_local_frame = stacktrace.iter().any(|frame| ecx.machine.is_local(frame));
9797
if has_local_frame {
9898
// This is part of the logic that `std` uses to select the relevant part of a
9999
// backtrace. But here, we only look for __rust_begin_short_backtrace, not
@@ -114,7 +114,7 @@ fn prune_stacktrace<'mir, 'tcx>(
114114
// This len check ensures that we don't somehow remove every frame, as doing so breaks
115115
// the primary error message.
116116
while stacktrace.len() > 1
117-
&& stacktrace.last().map_or(false, |e| !e.instance.def_id().is_local())
117+
&& stacktrace.last().map_or(false, |frame| !ecx.machine.is_local(frame))
118118
{
119119
stacktrace.pop();
120120
}
@@ -213,7 +213,7 @@ pub fn report_error<'tcx, 'mir>(
213213
e.print_backtrace();
214214
let msg = e.to_string();
215215
report_msg(
216-
*ecx.tcx,
216+
ecx,
217217
DiagLevel::Error,
218218
&if let Some(title) = title { format!("{}: {}", title, msg) } else { msg.clone() },
219219
msg,
@@ -256,19 +256,20 @@ pub fn report_error<'tcx, 'mir>(
256256

257257
/// Report an error or note (depending on the `error` argument) with the given stacktrace.
258258
/// Also emits a full stacktrace of the interpreter stack.
259-
fn report_msg<'tcx>(
260-
tcx: TyCtxt<'tcx>,
259+
fn report_msg<'mir, 'tcx>(
260+
ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
261261
diag_level: DiagLevel,
262262
title: &str,
263263
span_msg: String,
264264
mut helps: Vec<(Option<SpanData>, String)>,
265265
stacktrace: &[FrameInfo<'tcx>],
266266
) {
267267
let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
268+
let sess = ecx.tcx.sess;
268269
let mut err = match diag_level {
269-
DiagLevel::Error => tcx.sess.struct_span_err(span, title).forget_guarantee(),
270-
DiagLevel::Warning => tcx.sess.struct_span_warn(span, title),
271-
DiagLevel::Note => tcx.sess.diagnostic().span_note_diag(span, title),
270+
DiagLevel::Error => sess.struct_span_err(span, title).forget_guarantee(),
271+
DiagLevel::Warning => sess.struct_span_warn(span, title),
272+
DiagLevel::Note => sess.diagnostic().span_note_diag(span, title),
272273
};
273274

274275
// Show main message.
@@ -293,7 +294,7 @@ fn report_msg<'tcx>(
293294
}
294295
// Add backtrace
295296
for (idx, frame_info) in stacktrace.iter().enumerate() {
296-
let is_local = frame_info.instance.def_id().is_local();
297+
let is_local = ecx.machine.is_local(frame_info);
297298
// No span for non-local frames and the first frame (which is the error site).
298299
if is_local && idx > 0 {
299300
err.span_note(frame_info.span, &frame_info.to_string());
@@ -413,7 +414,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
413414
_ => ("tracking was triggered", DiagLevel::Note),
414415
};
415416

416-
report_msg(*this.tcx, diag_level, title, msg, vec![], &stacktrace);
417+
report_msg(this, diag_level, title, msg, vec![], &stacktrace);
417418
}
418419
});
419420
}

src/eval.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ pub struct MiriConfig {
111111
/// Panic when unsupported functionality is encountered
112112
pub panic_on_unsupported: bool,
113113
pub backtrace_style: BacktraceStyle,
114+
/// Crates which are considered local for the purposes of error reporting
115+
pub local_crates: Vec<String>,
114116
}
115117

116118
impl Default for MiriConfig {
@@ -136,6 +138,7 @@ impl Default for MiriConfig {
136138
measureme_out: None,
137139
panic_on_unsupported: false,
138140
backtrace_style: BacktraceStyle::Short,
141+
local_crates: Vec::new(),
139142
}
140143
}
141144
}

src/machine.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_middle::{
1919
Instance, TyCtxt,
2020
},
2121
};
22-
use rustc_span::def_id::DefId;
22+
use rustc_span::def_id::{CrateNum, DefId};
2323
use rustc_span::symbol::{sym, Symbol};
2424
use rustc_target::abi::Size;
2525
use rustc_target::spec::abi::Abi;
@@ -349,10 +349,23 @@ pub struct Evaluator<'mir, 'tcx> {
349349

350350
/// Equivalent setting as RUST_BACKTRACE on encountering an error.
351351
pub(crate) backtrace_style: BacktraceStyle,
352+
353+
/// Crates which are considered local for the purposes of error reporting
354+
pub(crate) local_crates: Vec<CrateNum>,
352355
}
353356

354357
impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
355358
pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self {
359+
// Convert the local crate names from the passed-in config into CrateNums so that they can
360+
// be looked up quickly during execution
361+
let mut local_crates = Vec::new();
362+
for num in layout_cx.tcx.crates(()) {
363+
let name = layout_cx.tcx.crate_name(*num).as_str().to_string();
364+
if config.local_crates.contains(&name) {
365+
local_crates.push(*num);
366+
}
367+
}
368+
356369
let layouts =
357370
PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
358371
let profiler = config.measureme_out.as_ref().map(|out| {
@@ -381,12 +394,18 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
381394
exported_symbols_cache: FxHashMap::default(),
382395
panic_on_unsupported: config.panic_on_unsupported,
383396
backtrace_style: config.backtrace_style,
397+
local_crates,
384398
}
385399
}
386400

387401
pub(crate) fn communicate(&self) -> bool {
388402
self.isolated_op == IsolatedOp::Allow
389403
}
404+
405+
pub(crate) fn is_local(&self, frame: &FrameInfo<'_>) -> bool {
406+
let def_id = frame.instance.def_id();
407+
def_id.is_local() || self.local_crates.contains(&def_id.krate)
408+
}
390409
}
391410

392411
/// A rustc InterpCx for Miri.

0 commit comments

Comments
 (0)