| //! A "shim crate" intended to multiplex the [`proc_macro`] API on to stable |
| //! Rust. |
| //! |
| //! Procedural macros in Rust operate over the upstream |
| //! [`proc_macro::TokenStream`][ts] type. This type currently is quite |
| //! conservative and exposed no internal implementation details. Nightly |
| //! compilers, however, contain a much richer interface. This richer interface |
| //! allows fine-grained inspection of the token stream which avoids |
| //! stringification/re-lexing and also preserves span information. |
| //! |
| //! The upcoming APIs added to [`proc_macro`] upstream are the foundation for |
| //! productive procedural macros in the ecosystem. To help prepare the ecosystem |
| //! for using them this crate serves to both compile on stable and nightly and |
| //! mirrors the API-to-be. The intention is that procedural macros which switch |
| //! to use this crate will be trivially able to switch to the upstream |
| //! `proc_macro` crate once its API stabilizes. |
| //! |
| //! In the meantime this crate also has a `nightly` Cargo feature which |
| //! enables it to reimplement itself with the unstable API of [`proc_macro`]. |
| //! This'll allow immediate usage of the beneficial upstream API, particularly |
| //! around preserving span information. |
| //! |
| //! # Unstable Features |
| //! |
| //! `proc-macro2` supports exporting some methods from `proc_macro` which are |
| //! currently highly unstable, and may not be stabilized in the first pass of |
| //! `proc_macro` stabilizations. These features are not exported by default. |
| //! Minor versions of `proc-macro2` may make breaking changes to them at any |
| //! time. |
| //! |
| //! To enable these features, the `procmacro2_semver_exempt` config flag must be |
| //! passed to rustc. |
| //! |
| //! ```sh |
| //! RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo build |
| //! ``` |
| //! |
| //! Note that this must not only be done for your crate, but for any crate that |
| //! depends on your crate. This infectious nature is intentional, as it serves |
| //! as a reminder that you are outside of the normal semver guarantees. |
| //! |
| //! [`proc_macro`]: https://doc.rust-lang.org/proc_macro/ |
| //! [ts]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html |
| |
| // Proc-macro2 types in rustdoc of other crates get linked to here. |
| #![doc(html_root_url = "https://docs.rs/proc-macro2/0.3.7")] |
| #![cfg_attr(feature = "nightly", feature(proc_macro))] |
| |
| #[cfg(feature = "proc-macro")] |
| extern crate proc_macro; |
| extern crate unicode_xid; |
| |
| use std::fmt; |
| use std::iter::FromIterator; |
| use std::marker; |
| use std::rc::Rc; |
| use std::str::FromStr; |
| |
| #[macro_use] |
| mod strnom; |
| mod stable; |
| |
| #[cfg(not(feature = "nightly"))] |
| use stable as imp; |
| #[path = "unstable.rs"] |
| #[cfg(feature = "nightly")] |
| mod imp; |
| |
| #[derive(Clone)] |
| pub struct TokenStream { |
| inner: imp::TokenStream, |
| _marker: marker::PhantomData<Rc<()>>, |
| } |
| |
| pub struct LexError { |
| inner: imp::LexError, |
| _marker: marker::PhantomData<Rc<()>>, |
| } |
| |
| impl TokenStream { |
| fn _new(inner: imp::TokenStream) -> TokenStream { |
| TokenStream { |
| inner: inner, |
| _marker: marker::PhantomData, |
| } |
| } |
| |
| fn _new_stable(inner: stable::TokenStream) -> TokenStream { |
| TokenStream { |
| inner: inner.into(), |
| _marker: marker::PhantomData, |
| } |
| } |
| |
| pub fn empty() -> TokenStream { |
| TokenStream::_new(imp::TokenStream::empty()) |
| } |
| |
| pub fn is_empty(&self) -> bool { |
| self.inner.is_empty() |
| } |
| } |
| |
| impl FromStr for TokenStream { |
| type Err = LexError; |
| |
| fn from_str(src: &str) -> Result<TokenStream, LexError> { |
| let e = src.parse().map_err(|e| LexError { |
| inner: e, |
| _marker: marker::PhantomData, |
| })?; |
| Ok(TokenStream::_new(e)) |
| } |
| } |
| |
| #[cfg(feature = "proc-macro")] |
| impl From<proc_macro::TokenStream> for TokenStream { |
| fn from(inner: proc_macro::TokenStream) -> TokenStream { |
| TokenStream::_new(inner.into()) |
| } |
| } |
| |
| #[cfg(feature = "proc-macro")] |
| impl From<TokenStream> for proc_macro::TokenStream { |
| fn from(inner: TokenStream) -> proc_macro::TokenStream { |
| inner.inner.into() |
| } |
| } |
| |
| impl FromIterator<TokenTree> for TokenStream { |
| fn from_iter<I: IntoIterator<Item = TokenTree>>(streams: I) -> Self { |
| TokenStream::_new(streams.into_iter().collect()) |
| } |
| } |
| |
| impl fmt::Display for TokenStream { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.inner.fmt(f) |
| } |
| } |
| |
| impl fmt::Debug for TokenStream { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.inner.fmt(f) |
| } |
| } |
| |
| impl fmt::Debug for LexError { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.inner.fmt(f) |
| } |
| } |
| |
| // Returned by reference, so we can't easily wrap it. |
| #[cfg(procmacro2_semver_exempt)] |
| pub use imp::FileName; |
| |
| #[cfg(procmacro2_semver_exempt)] |
| #[derive(Clone, PartialEq, Eq)] |
| pub struct SourceFile(imp::SourceFile); |
| |
| #[cfg(procmacro2_semver_exempt)] |
| impl SourceFile { |
| /// Get the path to this source file as a string. |
| pub fn path(&self) -> &FileName { |
| self.0.path() |
| } |
| |
| pub fn is_real(&self) -> bool { |
| self.0.is_real() |
| } |
| } |
| |
| #[cfg(procmacro2_semver_exempt)] |
| impl AsRef<FileName> for SourceFile { |
| fn as_ref(&self) -> &FileName { |
| self.0.path() |
| } |
| } |
| |
| #[cfg(procmacro2_semver_exempt)] |
| impl fmt::Debug for SourceFile { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.0.fmt(f) |
| } |
| } |
| |
| #[cfg(procmacro2_semver_exempt)] |
| pub struct LineColumn { |
| pub line: usize, |
| pub column: usize, |
| } |
| |
| #[derive(Copy, Clone)] |
| pub struct Span { |
| inner: imp::Span, |
| _marker: marker::PhantomData<Rc<()>>, |
| } |
| |
| impl Span { |
| fn _new(inner: imp::Span) -> Span { |
| Span { |
| inner: inner, |
| _marker: marker::PhantomData, |
| } |
| } |
| |
| fn _new_stable(inner: stable::Span) -> Span { |
| Span { |
| inner: inner.into(), |
| _marker: marker::PhantomData, |
| } |
| } |
| |
| pub fn call_site() -> Span { |
| Span::_new(imp::Span::call_site()) |
| } |
| |
| #[cfg(procmacro2_semver_exempt)] |
| pub fn def_site() -> Span { |
| Span::_new(imp::Span::def_site()) |
| } |
| |
| /// Creates a new span with the same line/column information as `self` but |
| /// that resolves symbols as though it were at `other`. |
| #[cfg(procmacro2_semver_exempt)] |
| pub fn resolved_at(&self, other: Span) -> Span { |
| Span::_new(self.inner.resolved_at(other.inner)) |
| } |
| |
| /// Creates a new span with the same name resolution behavior as `self` but |
| /// with the line/column information of `other`. |
| #[cfg(procmacro2_semver_exempt)] |
| pub fn located_at(&self, other: Span) -> Span { |
| Span::_new(self.inner.located_at(other.inner)) |
| } |
| |
| /// This method is only available when the `"nightly"` feature is enabled. |
| #[cfg(all(feature = "nightly", feature = "proc-macro"))] |
| pub fn unstable(self) -> proc_macro::Span { |
| self.inner.unstable() |
| } |
| |
| #[cfg(procmacro2_semver_exempt)] |
| pub fn source_file(&self) -> SourceFile { |
| SourceFile(self.inner.source_file()) |
| } |
| |
| #[cfg(procmacro2_semver_exempt)] |
| pub fn start(&self) -> LineColumn { |
| let imp::LineColumn { line, column } = self.inner.start(); |
| LineColumn { |
| line: line, |
| column: column, |
| } |
| } |
| |
| #[cfg(procmacro2_semver_exempt)] |
| pub fn end(&self) -> LineColumn { |
| let imp::LineColumn { line, column } = self.inner.end(); |
| LineColumn { |
| line: line, |
| column: column, |
| } |
| } |
| |
| #[cfg(procmacro2_semver_exempt)] |
| pub fn join(&self, other: Span) -> Option<Span> { |
| self.inner.join(other.inner).map(Span::_new) |
| } |
| |
| #[cfg(procmacro2_semver_exempt)] |
| pub fn eq(&self, other: &Span) -> bool { |
| self.inner.eq(&other.inner) |
| } |
| } |
| |
| impl fmt::Debug for Span { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.inner.fmt(f) |
| } |
| } |
| |
| #[derive(Clone)] |
| pub enum TokenTree { |
| Group(Group), |
| Term(Term), |
| Op(Op), |
| Literal(Literal), |
| } |
| |
| impl TokenTree { |
| pub fn span(&self) -> Span { |
| match *self { |
| TokenTree::Group(ref t) => t.span(), |
| TokenTree::Term(ref t) => t.span(), |
| TokenTree::Op(ref t) => t.span(), |
| TokenTree::Literal(ref t) => t.span(), |
| } |
| } |
| |
| pub fn set_span(&mut self, span: Span) { |
| match *self { |
| TokenTree::Group(ref mut t) => t.set_span(span), |
| TokenTree::Term(ref mut t) => t.set_span(span), |
| TokenTree::Op(ref mut t) => t.set_span(span), |
| TokenTree::Literal(ref mut t) => t.set_span(span), |
| } |
| } |
| } |
| |
| impl From<Group> for TokenTree { |
| fn from(g: Group) -> TokenTree { |
| TokenTree::Group(g) |
| } |
| } |
| |
| impl From<Term> for TokenTree { |
| fn from(g: Term) -> TokenTree { |
| TokenTree::Term(g) |
| } |
| } |
| |
| impl From<Op> for TokenTree { |
| fn from(g: Op) -> TokenTree { |
| TokenTree::Op(g) |
| } |
| } |
| |
| impl From<Literal> for TokenTree { |
| fn from(g: Literal) -> TokenTree { |
| TokenTree::Literal(g) |
| } |
| } |
| |
| impl fmt::Display for TokenTree { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match *self { |
| TokenTree::Group(ref t) => t.fmt(f), |
| TokenTree::Term(ref t) => t.fmt(f), |
| TokenTree::Op(ref t) => t.fmt(f), |
| TokenTree::Literal(ref t) => t.fmt(f), |
| } |
| } |
| } |
| |
| impl fmt::Debug for TokenTree { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| // Each of these has the name in the struct type in the derived debug, |
| // so don't bother with an extra layer of indirection |
| match *self { |
| TokenTree::Group(ref t) => t.fmt(f), |
| TokenTree::Term(ref t) => t.fmt(f), |
| TokenTree::Op(ref t) => t.fmt(f), |
| TokenTree::Literal(ref t) => t.fmt(f), |
| } |
| } |
| } |
| |
| #[derive(Clone)] |
| pub struct Group { |
| delimiter: Delimiter, |
| stream: TokenStream, |
| span: Span, |
| } |
| |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
| pub enum Delimiter { |
| Parenthesis, |
| Brace, |
| Bracket, |
| None, |
| } |
| |
| impl Group { |
| pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group { |
| Group { |
| delimiter: delimiter, |
| stream: stream, |
| span: Span::call_site(), |
| } |
| } |
| |
| pub fn delimiter(&self) -> Delimiter { |
| self.delimiter |
| } |
| |
| pub fn stream(&self) -> TokenStream { |
| self.stream.clone() |
| } |
| |
| pub fn span(&self) -> Span { |
| self.span |
| } |
| |
| pub fn set_span(&mut self, span: Span) { |
| self.span = span; |
| } |
| } |
| |
| impl fmt::Display for Group { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.stream.fmt(f) |
| } |
| } |
| |
| impl fmt::Debug for Group { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| let mut debug = fmt.debug_struct("Group"); |
| debug.field("delimiter", &self.delimiter); |
| debug.field("stream", &self.stream); |
| #[cfg(procmacro2_semver_exempt)] |
| debug.field("span", &self.span); |
| debug.finish() |
| } |
| } |
| |
| #[derive(Copy, Clone)] |
| pub struct Op { |
| op: char, |
| spacing: Spacing, |
| span: Span, |
| } |
| |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
| pub enum Spacing { |
| Alone, |
| Joint, |
| } |
| |
| impl Op { |
| pub fn new(op: char, spacing: Spacing) -> Op { |
| Op { |
| op: op, |
| spacing: spacing, |
| span: Span::call_site(), |
| } |
| } |
| |
| pub fn op(&self) -> char { |
| self.op |
| } |
| |
| pub fn spacing(&self) -> Spacing { |
| self.spacing |
| } |
| |
| pub fn span(&self) -> Span { |
| self.span |
| } |
| |
| pub fn set_span(&mut self, span: Span) { |
| self.span = span; |
| } |
| } |
| |
| impl fmt::Display for Op { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.op.fmt(f) |
| } |
| } |
| |
| impl fmt::Debug for Op { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
| let mut debug = fmt.debug_struct("Op"); |
| debug.field("op", &self.op); |
| debug.field("spacing", &self.spacing); |
| #[cfg(procmacro2_semver_exempt)] |
| debug.field("span", &self.span); |
| debug.finish() |
| } |
| } |
| |
| #[derive(Copy, Clone)] |
| pub struct Term { |
| inner: imp::Term, |
| _marker: marker::PhantomData<Rc<()>>, |
| } |
| |
| impl Term { |
| fn _new(inner: imp::Term) -> Term { |
| Term { |
| inner: inner, |
| _marker: marker::PhantomData, |
| } |
| } |
| |
| pub fn new(string: &str, span: Span) -> Term { |
| Term::_new(imp::Term::new(string, span.inner)) |
| } |
| |
| pub fn as_str(&self) -> &str { |
| self.inner.as_str() |
| } |
| |
| pub fn span(&self) -> Span { |
| Span::_new(self.inner.span()) |
| } |
| |
| pub fn set_span(&mut self, span: Span) { |
| self.inner.set_span(span.inner); |
| } |
| } |
| |
| impl fmt::Display for Term { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.as_str().fmt(f) |
| } |
| } |
| |
| impl fmt::Debug for Term { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.inner.fmt(f) |
| } |
| } |
| |
| #[derive(Clone)] |
| pub struct Literal { |
| inner: imp::Literal, |
| _marker: marker::PhantomData<Rc<()>>, |
| } |
| |
| macro_rules! int_literals { |
| ($($name:ident => $kind:ident,)*) => ($( |
| pub fn $name(n: $kind) -> Literal { |
| Literal::_new(imp::Literal::$name(n)) |
| } |
| )*) |
| } |
| |
| impl Literal { |
| fn _new(inner: imp::Literal) -> Literal { |
| Literal { |
| inner: inner, |
| _marker: marker::PhantomData, |
| } |
| } |
| |
| fn _new_stable(inner: stable::Literal) -> Literal { |
| Literal { |
| inner: inner.into(), |
| _marker: marker::PhantomData, |
| } |
| } |
| |
| int_literals! { |
| u8_suffixed => u8, |
| u16_suffixed => u16, |
| u32_suffixed => u32, |
| u64_suffixed => u64, |
| usize_suffixed => usize, |
| i8_suffixed => i8, |
| i16_suffixed => i16, |
| i32_suffixed => i32, |
| i64_suffixed => i64, |
| isize_suffixed => isize, |
| |
| u8_unsuffixed => u8, |
| u16_unsuffixed => u16, |
| u32_unsuffixed => u32, |
| u64_unsuffixed => u64, |
| usize_unsuffixed => usize, |
| i8_unsuffixed => i8, |
| i16_unsuffixed => i16, |
| i32_unsuffixed => i32, |
| i64_unsuffixed => i64, |
| isize_unsuffixed => isize, |
| } |
| |
| pub fn f64_unsuffixed(f: f64) -> Literal { |
| assert!(f.is_finite()); |
| Literal::_new(imp::Literal::f64_unsuffixed(f)) |
| } |
| |
| pub fn f64_suffixed(f: f64) -> Literal { |
| assert!(f.is_finite()); |
| Literal::_new(imp::Literal::f64_suffixed(f)) |
| } |
| |
| pub fn f32_unsuffixed(f: f32) -> Literal { |
| assert!(f.is_finite()); |
| Literal::_new(imp::Literal::f32_unsuffixed(f)) |
| } |
| |
| pub fn f32_suffixed(f: f32) -> Literal { |
| assert!(f.is_finite()); |
| Literal::_new(imp::Literal::f32_suffixed(f)) |
| } |
| |
| pub fn string(string: &str) -> Literal { |
| Literal::_new(imp::Literal::string(string)) |
| } |
| |
| pub fn character(ch: char) -> Literal { |
| Literal::_new(imp::Literal::character(ch)) |
| } |
| |
| pub fn byte_string(s: &[u8]) -> Literal { |
| Literal::_new(imp::Literal::byte_string(s)) |
| } |
| |
| pub fn span(&self) -> Span { |
| Span::_new(self.inner.span()) |
| } |
| |
| pub fn set_span(&mut self, span: Span) { |
| self.inner.set_span(span.inner); |
| } |
| } |
| |
| impl fmt::Debug for Literal { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.inner.fmt(f) |
| } |
| } |
| |
| impl fmt::Display for Literal { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.inner.fmt(f) |
| } |
| } |
| |
| pub mod token_stream { |
| use std::fmt; |
| use std::marker; |
| use std::rc::Rc; |
| |
| use imp; |
| pub use TokenStream; |
| use TokenTree; |
| |
| pub struct IntoIter { |
| inner: imp::TokenTreeIter, |
| _marker: marker::PhantomData<Rc<()>>, |
| } |
| |
| impl Iterator for IntoIter { |
| type Item = TokenTree; |
| |
| fn next(&mut self) -> Option<TokenTree> { |
| self.inner.next() |
| } |
| } |
| |
| impl fmt::Debug for IntoIter { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| self.inner.fmt(f) |
| } |
| } |
| |
| impl IntoIterator for TokenStream { |
| type Item = TokenTree; |
| type IntoIter = IntoIter; |
| |
| fn into_iter(self) -> IntoIter { |
| IntoIter { |
| inner: self.inner.into_iter(), |
| _marker: marker::PhantomData, |
| } |
| } |
| } |
| } |