about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-04-22 23:19:24 +0200
committerGitHub <noreply@github.com>2020-04-22 23:19:24 +0200
commit0f806534c01ea906a2e178b31ea2733a92e4bc0a (patch)
tree4e9e25f2d3c3a1d755c7852e9393dc24e4871d82
parent16be619c6aec0db952be61ebb45c63a1ebe4b1a5 (diff)
parent3bd742ff4e61f6752d76a75f53c140170a117554 (diff)
downloadrust-0f806534c01ea906a2e178b31ea2733a92e4bc0a.tar.gz
rust-0f806534c01ea906a2e178b31ea2733a92e4bc0a.zip
Rollup merge of #71400 - dtolnay:isavailable, r=petrochenkov
proc_macro::is_available()

This PR adds `proc_macro::is_available() -> bool` to determine whether proc_macro has been made accessible to the currently running program.

The proc_macro crate is only intended for use inside the implementation of procedural macros. All the functions in the crate panic if invoked from outside of a procedural macro, such as from a build script or unit test or ordinary Rust binary.

Unfortunately those panics made it impossible for libraries that are designed to support both macro and non-macro use cases (e.g. Syn) to be used from binaries that are compiled with panic=abort. In panic=unwind mode we're able to attempt a proc macro call inside catch_unwind and use libproc_macro's result if it succeeds, otherwise fall back to a non-macro alternative implementation. But in panic=abort there was no way to determine which implementation needs to be used.

r? @eddyb
attn: @petrochenkov @adetaylor
ref: https://github.com/dtolnay/cxx/issues/130
-rw-r--r--src/libproc_macro/bridge/client.rs7
-rw-r--r--src/libproc_macro/lib.rs18
-rw-r--r--src/test/ui/proc-macro/auxiliary/is-available.rs14
-rw-r--r--src/test/ui/proc-macro/is-available.rs17
4 files changed, 56 insertions, 0 deletions
diff --git a/src/libproc_macro/bridge/client.rs b/src/libproc_macro/bridge/client.rs
index 088db92253a..d2222d12623 100644
--- a/src/libproc_macro/bridge/client.rs
+++ b/src/libproc_macro/bridge/client.rs
@@ -290,6 +290,13 @@ impl BridgeState<'_> {
 }
 
 impl Bridge<'_> {
+    pub(crate) fn is_available() -> bool {
+        BridgeState::with(|state| match state {
+            BridgeState::Connected(_) | BridgeState::InUse => true,
+            BridgeState::NotConnected => false,
+        })
+    }
+
     fn enter<R>(self, f: impl FnOnce() -> R) -> R {
         // Hide the default panic output within `proc_macro` expansions.
         // NB. the server can't do this because it may use a different libstd.
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index a975ce93bb1..3cbe852de7b 100644
--- a/src/libproc_macro/lib.rs
+++ b/src/libproc_macro/lib.rs
@@ -45,6 +45,24 @@ use std::path::PathBuf;
 use std::str::FromStr;
 use std::{error, fmt, iter, mem};
 
+/// Determines whether proc_macro has been made accessible to the currently
+/// running program.
+///
+/// The proc_macro crate is only intended for use inside the implementation of
+/// procedural macros. All the functions in this crate panic if invoked from
+/// outside of a procedural macro, such as from a build script or unit test or
+/// ordinary Rust binary.
+///
+/// With consideration for Rust libraries that are designed to support both
+/// macro and non-macro use cases, `proc_macro::is_available()` provides a
+/// non-panicking way to detect whether the infrastructure required to use the
+/// API of proc_macro is presently available. Returns true if invoked from
+/// inside of a procedural macro, false if invoked from any other binary.
+#[unstable(feature = "proc_macro_is_available", issue = "71436")]
+pub fn is_available() -> bool {
+    bridge::Bridge::is_available()
+}
+
 /// The main type provided by this crate, representing an abstract stream of
 /// tokens, or, more specifically, a sequence of token trees.
 /// The type provide interfaces for iterating over those token trees and, conversely,
diff --git a/src/test/ui/proc-macro/auxiliary/is-available.rs b/src/test/ui/proc-macro/auxiliary/is-available.rs
new file mode 100644
index 00000000000..0caf186db1d
--- /dev/null
+++ b/src/test/ui/proc-macro/auxiliary/is-available.rs
@@ -0,0 +1,14 @@
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(proc_macro_is_available)]
+
+extern crate proc_macro;
+
+use proc_macro::{Literal, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn from_inside_proc_macro(_input: TokenStream) -> TokenStream {
+    proc_macro::is_available().to_string().parse().unwrap()
+}
diff --git a/src/test/ui/proc-macro/is-available.rs b/src/test/ui/proc-macro/is-available.rs
new file mode 100644
index 00000000000..943d9fe797a
--- /dev/null
+++ b/src/test/ui/proc-macro/is-available.rs
@@ -0,0 +1,17 @@
+// run-pass
+
+#![feature(proc_macro_hygiene, proc_macro_is_available)]
+
+extern crate proc_macro;
+
+// aux-build:is-available.rs
+extern crate is_available;
+
+fn main() {
+    let a = proc_macro::is_available();
+    let b = is_available::from_inside_proc_macro!();
+    let c = proc_macro::is_available();
+    assert!(!a);
+    assert!(b);
+    assert!(!c);
+}