Skip to content

Commit 2ed15d5

Browse files
committed
Hide panic output in nightly_works
1 parent b07f847 commit 2ed15d5

File tree

1 file changed

+36
-3
lines changed

1 file changed

+36
-3
lines changed

src/unstable.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,49 @@ pub enum LexError {
2323

2424
fn nightly_works() -> bool {
2525
use std::sync::atomic::*;
26+
use std::sync::Once;
27+
2628
static WORKS: AtomicUsize = ATOMIC_USIZE_INIT;
29+
static INIT: Once = Once::new();
2730

2831
match WORKS.load(Ordering::SeqCst) {
2932
1 => return false,
3033
2 => return true,
3134
_ => {}
3235
}
33-
let works = panic::catch_unwind(|| proc_macro::Span::call_site()).is_ok();
34-
WORKS.store(works as usize + 1, Ordering::SeqCst);
35-
works
36+
37+
// Swap in a null panic hook to avoid printing "thread panicked" to stderr,
38+
// then use catch_unwind to determine whether the compiler's proc_macro is
39+
// working. When proc-macro2 is used from outside of a procedural macro all
40+
// of the proc_macro crate's APIs currently panic.
41+
//
42+
// The Once is to prevent the possibility of this ordering:
43+
//
44+
// thread 1 calls take_hook, gets the user's original hook
45+
// thread 1 calls set_hook with the null hook
46+
// thread 2 calls take_hook, thinks null hook is the original hook
47+
// thread 2 calls set_hook with the null hook
48+
// thread 1 calls set_hook with the actual original hook
49+
// thread 2 calls set_hook with what it thinks is the original hook
50+
//
51+
// in which the user's hook has been lost.
52+
//
53+
// There is still a race condition where a panic in a different thread can
54+
// happen during the interval that the user's original panic hook is
55+
// unregistered such that their hook is incorrectly not called. This is
56+
// sufficiently unlikely and less bad than printing panic messages to stderr
57+
// on correct use of this crate. Maybe there is a libstd feature request
58+
// here. For now, if a user needs to guarantee that this failure mode does
59+
// not occur, they need to call e.g. `proc_macro2::Span::call_site()` from
60+
// the main thread before launching any other threads.
61+
INIT.call_once(|| {
62+
let original_hook = panic::take_hook();
63+
panic::set_hook(Box::new(|_panic_info| { /* ignore */ }));
64+
let works = panic::catch_unwind(|| proc_macro::Span::call_site()).is_ok();
65+
WORKS.store(works as usize + 1, Ordering::SeqCst);
66+
panic::set_hook(original_hook);
67+
});
68+
nightly_works()
3669
}
3770

3871
fn mismatch() -> ! {

0 commit comments

Comments
 (0)