diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..51ad9276 --- /dev/null +++ b/build.rs @@ -0,0 +1,47 @@ +use std::env; +use std::process::Command; +use std::str::{self, FromStr}; + +// The rustc-cfg strings below are *not* public API. Please let us know by +// opening a GitHub issue if your build environment requires some way to enable +// these cfgs other than by executing our build script. +fn main() { + let minor = match rustc_minor_version() { + Some(minor) => minor, + None => return, + }; + + // Macro modularization allows re-exporting the `quote!` macro in 1.30+. + if minor >= 30 { + println!("cargo:rustc-cfg=syn_can_call_macro_by_path"); + } +} + +fn rustc_minor_version() -> Option { + let rustc = match env::var_os("RUSTC") { + Some(rustc) => rustc, + None => return None, + }; + + let output = match Command::new(rustc).arg("--version").output() { + Ok(output) => output, + Err(_) => return None, + }; + + let version = match str::from_utf8(&output.stdout) { + Ok(version) => version, + Err(_) => return None, + }; + + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + + let next = match pieces.next() { + Some(next) => next, + None => return None, + }; + + u32::from_str(next).ok() +} diff --git a/src/export.rs b/src/export.rs index cc7a2224..8e270bd0 100644 --- a/src/export.rs +++ b/src/export.rs @@ -8,6 +8,9 @@ pub use std::marker::Copy; pub use std::option::Option::{None, Some}; pub use std::result::Result::{Err, Ok}; +#[cfg(feature = "printing")] +pub extern crate quote; + pub use proc_macro2::{Span, TokenStream as TokenStream2}; pub use span::IntoSpans; diff --git a/src/parse_quote.rs b/src/parse_quote.rs index e7c3dfeb..93e27592 100644 --- a/src/parse_quote.rs +++ b/src/parse_quote.rs @@ -38,8 +38,14 @@ /// parameter `T` in the input generics. /// /// ``` -/// #[macro_use] -/// extern crate quote; +#[cfg_attr( + not(syn_can_call_macro_by_path), + doc = " #[macro_use]" +)] +#[cfg_attr( + not(syn_can_call_macro_by_path), + doc = " extern crate quote;" +)] /// #[macro_use] /// extern crate syn; /// @@ -76,10 +82,29 @@ /// Panics if the tokens fail to parse as the expected syntax tree type. The /// caller is responsible for ensuring that the input tokens are syntactically /// valid. -#[macro_export] +#[macro_export(local_inner_macros)] macro_rules! parse_quote { ($($tt:tt)*) => { - $crate::parse_quote::parse($crate::export::From::from(quote!($($tt)*))) + $crate::parse_quote::parse($crate::export::From::from(quote_impl!($($tt)*))) + }; +} + +#[cfg(not(syn_can_call_macro_by_path))] +#[doc(hidden)] +#[macro_export] +macro_rules! quote_impl { + ($($tt:tt)*) => { + // Require caller to have their own `#[macro_use] extern crate quote`. + quote!($($tt)*) + }; +} + +#[cfg(syn_can_call_macro_by_path)] +#[doc(hidden)] +#[macro_export] +macro_rules! quote_impl { + ($($tt:tt)*) => { + $crate::export::quote::quote!($($tt)*) }; }