Add `-Z proc-macro-backtrace` to allow showing proc-macro panics

Fixes #75050

Previously, we would unconditionally suppress the panic hook during
proc-macro execution. This commit adds a new flag
-Z proc-macro-backtrace, which allows running the panic hook for
easier debugging.
This commit is contained in:
Aaron Hill 2020-08-30 22:17:24 -04:00
parent 36b0d7e257
commit d9208665b5
No known key found for this signature in database
GPG Key ID: B4087E510E98B164
10 changed files with 99 additions and 27 deletions

View File

@ -1788,6 +1788,7 @@ pub struct ExpansionConfig<'feat> {
pub should_test: bool, // If false, strip `#[test]` nodes
pub keep_macs: bool,
pub span_debug: bool, // If true, use verbose debugging for `proc_macro::Span`
pub proc_macro_backtrace: bool, // If true, show backtraces for proc-macro panics
}
impl<'feat> ExpansionConfig<'feat> {
@ -1800,6 +1801,7 @@ impl<'feat> ExpansionConfig<'feat> {
should_test: false,
keep_macs: false,
span_debug: false,
proc_macro_backtrace: false,
}
}

View File

@ -24,7 +24,7 @@ impl base::ProcMacro for BangProcMacro {
input: TokenStream,
) -> Result<TokenStream, ErrorReported> {
let server = proc_macro_server::Rustc::new(ecx);
self.client.run(&EXEC_STRATEGY, server, input).map_err(|e| {
self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace).map_err(|e| {
let mut err = ecx.struct_span_err(span, "proc macro panicked");
if let Some(s) = e.as_str() {
err.help(&format!("message: {}", s));
@ -48,14 +48,16 @@ impl base::AttrProcMacro for AttrProcMacro {
annotated: TokenStream,
) -> Result<TokenStream, ErrorReported> {
let server = proc_macro_server::Rustc::new(ecx);
self.client.run(&EXEC_STRATEGY, server, annotation, annotated).map_err(|e| {
let mut err = ecx.struct_span_err(span, "custom attribute panicked");
if let Some(s) = e.as_str() {
err.help(&format!("message: {}", s));
}
err.emit();
ErrorReported
})
self.client
.run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace)
.map_err(|e| {
let mut err = ecx.struct_span_err(span, "custom attribute panicked");
if let Some(s) = e.as_str() {
err.help(&format!("message: {}", s));
}
err.emit();
ErrorReported
})
}
}
@ -111,17 +113,18 @@ impl MultiItemModifier for ProcMacroDerive {
};
let server = proc_macro_server::Rustc::new(ecx);
let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
Ok(stream) => stream,
Err(e) => {
let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
if let Some(s) = e.as_str() {
err.help(&format!("message: {}", s));
let stream =
match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) {
Ok(stream) => stream,
Err(e) => {
let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
if let Some(s) = e.as_str() {
err.help(&format!("message: {}", s));
}
err.emit();
return ExpandResult::Ready(vec![]);
}
err.emit();
return ExpandResult::Ready(vec![]);
}
};
};
let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
let mut parser =

View File

@ -291,6 +291,7 @@ fn configure_and_expand_inner<'a>(
trace_mac: sess.opts.debugging_opts.trace_macros,
should_test: sess.opts.test,
span_debug: sess.opts.debugging_opts.span_debug,
proc_macro_backtrace: sess.opts.debugging_opts.proc_macro_backtrace,
..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
};

View File

@ -502,6 +502,7 @@ fn test_debugging_options_tracking_hash() {
untracked!(print_llvm_passes, true);
untracked!(print_mono_items, Some(String::from("abc")));
untracked!(print_type_sizes, true);
untracked!(proc_macro_backtrace, true);
untracked!(query_dep_graph, true);
untracked!(query_stats, true);
untracked!(save_analysis, true);

View File

@ -967,6 +967,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"print the result of the monomorphization collection pass"),
print_type_sizes: bool = (false, parse_bool, [UNTRACKED],
"print layout information for each type encountered (default: no)"),
proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
"show backtraces for panics during proc-macro execution (default: no)"),
profile: bool = (false, parse_bool, [TRACKED],
"insert profiling code (default: no)"),
profile_emit: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],

View File

@ -311,11 +311,13 @@ impl Bridge<'_> {
HIDE_PANICS_DURING_EXPANSION.call_once(|| {
let prev = panic::take_hook();
panic::set_hook(Box::new(move |info| {
let hide = BridgeState::with(|state| match state {
BridgeState::NotConnected => false,
BridgeState::Connected(_) | BridgeState::InUse => true,
let show = BridgeState::with(|state| match state {
BridgeState::NotConnected => true,
// Something weird is going on, so don't suppress any backtraces
BridgeState::InUse => true,
BridgeState::Connected(bridge) => bridge.force_show_panics,
});
if !hide {
if show {
prev(info)
}
}));

View File

@ -220,6 +220,9 @@ pub struct Bridge<'a> {
/// Server-side function that the client uses to make requests.
dispatch: closure::Closure<'a, Buffer<u8>, Buffer<u8>>,
/// If 'true', always invoke the default panic hook
force_show_panics: bool,
}
impl<'a> !Sync for Bridge<'a> {}

View File

@ -135,6 +135,7 @@ pub trait ExecutionStrategy {
input: Buffer<u8>,
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
client_data: D,
force_show_panics: bool,
) -> Buffer<u8>;
}
@ -147,10 +148,14 @@ impl ExecutionStrategy for SameThread {
input: Buffer<u8>,
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
client_data: D,
force_show_panics: bool,
) -> Buffer<u8> {
let mut dispatch = |b| dispatcher.dispatch(b);
run_client(Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() }, client_data)
run_client(
Bridge { cached_buffer: input, dispatch: (&mut dispatch).into(), force_show_panics },
client_data,
)
}
}
@ -166,6 +171,7 @@ impl ExecutionStrategy for CrossThread1 {
input: Buffer<u8>,
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
client_data: D,
force_show_panics: bool,
) -> Buffer<u8> {
use std::sync::mpsc::channel;
@ -179,7 +185,11 @@ impl ExecutionStrategy for CrossThread1 {
};
run_client(
Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() },
Bridge {
cached_buffer: input,
dispatch: (&mut dispatch).into(),
force_show_panics,
},
client_data,
)
});
@ -201,6 +211,7 @@ impl ExecutionStrategy for CrossThread2 {
input: Buffer<u8>,
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
client_data: D,
force_show_panics: bool,
) -> Buffer<u8> {
use std::sync::{Arc, Mutex};
@ -226,7 +237,11 @@ impl ExecutionStrategy for CrossThread2 {
};
let r = run_client(
Bridge { cached_buffer: input, dispatch: (&mut dispatch).into() },
Bridge {
cached_buffer: input,
dispatch: (&mut dispatch).into(),
force_show_panics,
},
client_data,
);
@ -265,6 +280,7 @@ fn run_server<
input: I,
run_client: extern "C" fn(Bridge<'_>, D) -> Buffer<u8>,
client_data: D,
force_show_panics: bool,
) -> Result<O, PanicMessage> {
let mut dispatcher =
Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) };
@ -272,7 +288,13 @@ fn run_server<
let mut b = Buffer::new();
input.encode(&mut b, &mut dispatcher.handle_store);
b = strategy.run_bridge_and_client(&mut dispatcher, b, run_client, client_data);
b = strategy.run_bridge_and_client(
&mut dispatcher,
b,
run_client,
client_data,
force_show_panics,
);
Result::decode(&mut &b[..], &mut dispatcher.handle_store)
}
@ -283,6 +305,7 @@ impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
strategy: &impl ExecutionStrategy,
server: S,
input: S::TokenStream,
force_show_panics: bool,
) -> Result<S::TokenStream, PanicMessage> {
let client::Client { get_handle_counters, run, f } = *self;
run_server(
@ -292,6 +315,7 @@ impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
<MarkedTypes<S> as Types>::TokenStream::mark(input),
run,
f,
force_show_panics,
)
.map(<MarkedTypes<S> as Types>::TokenStream::unmark)
}
@ -304,6 +328,7 @@ impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenSt
server: S,
input: S::TokenStream,
input2: S::TokenStream,
force_show_panics: bool,
) -> Result<S::TokenStream, PanicMessage> {
let client::Client { get_handle_counters, run, f } = *self;
run_server(
@ -316,6 +341,7 @@ impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenSt
),
run,
f,
force_show_panics,
)
.map(<MarkedTypes<S> as Types>::TokenStream::unmark)
}

View File

@ -0,0 +1,21 @@
// aux-build:test-macros.rs
// compile-flags: -Z proc-macro-backtrace
// rustc-env:RUST_BACKTRACE=0
// FIXME https://github.com/rust-lang/rust/issues/59998
// normalize-stderr-test "thread '.*' panicked " -> ""
// normalize-stderr-test "note:.*RUST_BACKTRACE=1.*\n" -> ""
// normalize-stderr-test "\nerror: internal compiler error.*\n\n" -> ""
// normalize-stderr-test "note:.*unexpectedly panicked.*\n\n" -> ""
// normalize-stderr-test "note: we would appreciate a bug report.*\n\n" -> ""
// normalize-stderr-test "note: compiler flags.*\n\n" -> ""
// normalize-stderr-test "note: rustc.*running on.*\n\n" -> ""
#[macro_use]
extern crate test_macros;
#[derive(Panic)]
//~^ ERROR: proc-macro derive panicked
struct Foo;
fn main() {}

View File

@ -0,0 +1,11 @@
at 'panic-derive', $DIR/auxiliary/test-macros.rs:43:5
error: proc-macro derive panicked
--> $DIR/load-panic-backtrace.rs:17:10
|
LL | #[derive(Panic)]
| ^^^^^
|
= help: message: panic-derive
error: aborting due to previous error