Skip to content

Commit fad56f7

Browse files
committed
Move proc-macro detection to a module
1 parent b8e8a63 commit fad56f7

File tree

3 files changed

+61
-58
lines changed

3 files changed

+61
-58
lines changed

src/detection.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use std::panic::{self, PanicInfo};
2+
use std::sync::atomic::*;
3+
use std::sync::Once;
4+
5+
static WORKS: AtomicUsize = AtomicUsize::new(0);
6+
static INIT: Once = Once::new();
7+
8+
pub(crate) fn inside_proc_macro() -> bool {
9+
match WORKS.load(Ordering::SeqCst) {
10+
1 => return false,
11+
2 => return true,
12+
_ => {}
13+
}
14+
15+
// Swap in a null panic hook to avoid printing "thread panicked" to stderr,
16+
// then use catch_unwind to determine whether the compiler's proc_macro is
17+
// working. When proc-macro2 is used from outside of a procedural macro all
18+
// of the proc_macro crate's APIs currently panic.
19+
//
20+
// The Once is to prevent the possibility of this ordering:
21+
//
22+
// thread 1 calls take_hook, gets the user's original hook
23+
// thread 1 calls set_hook with the null hook
24+
// thread 2 calls take_hook, thinks null hook is the original hook
25+
// thread 2 calls set_hook with the null hook
26+
// thread 1 calls set_hook with the actual original hook
27+
// thread 2 calls set_hook with what it thinks is the original hook
28+
//
29+
// in which the user's hook has been lost.
30+
//
31+
// There is still a race condition where a panic in a different thread can
32+
// happen during the interval that the user's original panic hook is
33+
// unregistered such that their hook is incorrectly not called. This is
34+
// sufficiently unlikely and less bad than printing panic messages to stderr
35+
// on correct use of this crate. Maybe there is a libstd feature request
36+
// here. For now, if a user needs to guarantee that this failure mode does
37+
// not occur, they need to call e.g. `proc_macro2::Span::call_site()` from
38+
// the main thread before launching any other threads.
39+
INIT.call_once(|| {
40+
type PanicHook = dyn Fn(&PanicInfo) + Sync + Send + 'static;
41+
42+
let null_hook: Box<PanicHook> = Box::new(|_panic_info| { /* ignore */ });
43+
let sanity_check = &*null_hook as *const PanicHook;
44+
let original_hook = panic::take_hook();
45+
panic::set_hook(null_hook);
46+
47+
let works = panic::catch_unwind(proc_macro::Span::call_site).is_ok();
48+
WORKS.store(works as usize + 1, Ordering::SeqCst);
49+
50+
let hopefully_null_hook = panic::take_hook();
51+
panic::set_hook(original_hook);
52+
if sanity_check != &*hopefully_null_hook {
53+
panic!("observed race condition in proc_macro2::inside_proc_macro");
54+
}
55+
});
56+
57+
inside_proc_macro()
58+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ use std::path::PathBuf;
9696
use std::rc::Rc;
9797
use std::str::FromStr;
9898

99+
mod detection;
99100
#[macro_use]
100101
mod strnom;
101102
mod fallback;

src/wrapper.rs

Lines changed: 2 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use std::fmt;
22
use std::iter;
33
use std::ops::RangeBounds;
4-
use std::panic::{self, PanicInfo};
4+
use std::panic;
55
#[cfg(super_unstable)]
66
use std::path::PathBuf;
77
use std::str::FromStr;
88

9+
use crate::detection::inside_proc_macro;
910
use crate::{fallback, Delimiter, Punct, Spacing, TokenTree};
1011

1112
#[derive(Clone)]
@@ -29,63 +30,6 @@ pub(crate) enum LexError {
2930
Fallback(fallback::LexError),
3031
}
3132

32-
fn inside_proc_macro() -> bool {
33-
use std::sync::atomic::*;
34-
use std::sync::Once;
35-
36-
static WORKS: AtomicUsize = AtomicUsize::new(0);
37-
static INIT: Once = Once::new();
38-
39-
match WORKS.load(Ordering::SeqCst) {
40-
1 => return false,
41-
2 => return true,
42-
_ => {}
43-
}
44-
45-
// Swap in a null panic hook to avoid printing "thread panicked" to stderr,
46-
// then use catch_unwind to determine whether the compiler's proc_macro is
47-
// working. When proc-macro2 is used from outside of a procedural macro all
48-
// of the proc_macro crate's APIs currently panic.
49-
//
50-
// The Once is to prevent the possibility of this ordering:
51-
//
52-
// thread 1 calls take_hook, gets the user's original hook
53-
// thread 1 calls set_hook with the null hook
54-
// thread 2 calls take_hook, thinks null hook is the original hook
55-
// thread 2 calls set_hook with the null hook
56-
// thread 1 calls set_hook with the actual original hook
57-
// thread 2 calls set_hook with what it thinks is the original hook
58-
//
59-
// in which the user's hook has been lost.
60-
//
61-
// There is still a race condition where a panic in a different thread can
62-
// happen during the interval that the user's original panic hook is
63-
// unregistered such that their hook is incorrectly not called. This is
64-
// sufficiently unlikely and less bad than printing panic messages to stderr
65-
// on correct use of this crate. Maybe there is a libstd feature request
66-
// here. For now, if a user needs to guarantee that this failure mode does
67-
// not occur, they need to call e.g. `proc_macro2::Span::call_site()` from
68-
// the main thread before launching any other threads.
69-
INIT.call_once(|| {
70-
type PanicHook = dyn Fn(&PanicInfo) + Sync + Send + 'static;
71-
72-
let null_hook: Box<PanicHook> = Box::new(|_panic_info| { /* ignore */ });
73-
let sanity_check = &*null_hook as *const PanicHook;
74-
let original_hook = panic::take_hook();
75-
panic::set_hook(null_hook);
76-
77-
let works = panic::catch_unwind(proc_macro::Span::call_site).is_ok();
78-
WORKS.store(works as usize + 1, Ordering::SeqCst);
79-
80-
let hopefully_null_hook = panic::take_hook();
81-
panic::set_hook(original_hook);
82-
if sanity_check != &*hopefully_null_hook {
83-
panic!("observed race condition in proc_macro2::inside_proc_macro");
84-
}
85-
});
86-
inside_proc_macro()
87-
}
88-
8933
fn mismatch() -> ! {
9034
panic!("stable/nightly mismatch")
9135
}

0 commit comments

Comments
 (0)