]> git.lizzy.rs Git - rust.git/commitdiff
proc_macro: clean up the implementation of quasi-quoting.
authorEduard-Mihai Burtescu <edy.burt@gmail.com>
Sun, 22 Apr 2018 00:05:02 +0000 (03:05 +0300)
committerEduard-Mihai Burtescu <edy.burt@gmail.com>
Thu, 19 Jul 2018 21:15:11 +0000 (00:15 +0300)
src/libproc_macro/lib.rs
src/libproc_macro/quote.rs
src/librustc_metadata/cstore_impl.rs
src/librustc_metadata/lib.rs

index ea4ca03a0a1f624f3e728a109f645c9cdd70fa81..c88a245d3b376872c5d4945de5cdb5909af2cd07 100644 (file)
@@ -145,6 +145,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+#[unstable(feature = "proc_macro_quote", issue = "38356")]
+pub use quote::{quote, quote_span};
+
 /// Creates a token stream containing a single token tree.
     #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
 impl From<TokenTree> for TokenStream {
@@ -237,7 +240,7 @@ fn into_iter(self) -> IntoIter {
 /// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term.
 /// To quote `$` itself, use `$$`.
 ///
-/// This is a dummy macro, the actual implementation is in quote::Quoter
+/// This is a dummy macro, the actual implementation is in `quote::quote`.`
 #[unstable(feature = "proc_macro_quote", issue = "38356")]
 #[macro_export]
 macro_rules! quote { () => {} }
@@ -246,13 +249,6 @@ macro_rules! quote { () => {} }
 #[doc(hidden)]
 mod quote;
 
-/// Quote a `Span` into a `TokenStream`.
-/// This is needed to implement a custom quoter.
-#[unstable(feature = "proc_macro_quote", issue = "38356")]
-pub fn quote_span(span: Span) -> TokenStream {
-    quote::Quote::quote(span)
-}
-
 /// A region of source code, along with macro expansion information.
 #[stable(feature = "proc_macro_lib2", since = "1.29.0")]
 #[derive(Copy, Clone)]
@@ -1364,8 +1360,6 @@ fn to_internal(self) -> tokenstream::TokenStream {
 #[unstable(feature = "proc_macro_internals", issue = "27812")]
 #[doc(hidden)]
 pub mod __internal {
-    pub use quote::{Quoter, unquote};
-
     use std::cell::Cell;
     use std::ptr;
 
index 8ae1d7b8525cf210333b7368f5ff77d5b5736474..7ae7b13a15217b2026cd3adfde27e88c598620c7 100644 (file)
 //! This quasiquoter uses macros 2.0 hygiene to reliably access
 //! items from `proc_macro`, to build a `proc_macro::TokenStream`.
 
-use {Delimiter, Literal, Spacing, Span, Ident, Punct, Group, TokenStream, TokenTree};
-
-use syntax::ext::base::{ExtCtxt, ProcMacro};
-use syntax::tokenstream;
-
-/// This is the actual quote!() proc macro
-///
-/// It is manually loaded in CStore::load_macro_untracked
-pub struct Quoter;
-
-pub fn unquote<T: Into<TokenStream> + Clone>(tokens: &T) -> TokenStream {
-    tokens.clone().into()
-}
-
-pub trait Quote {
-    fn quote(self) -> TokenStream;
-}
-
-macro_rules! tt2ts {
-    ($e:expr) => (TokenStream::from(TokenTree::from($e)))
-}
-
-macro_rules! quote_tok {
-    (,) => { tt2ts!(Punct::new(',', Spacing::Alone)) };
-    (.) => { tt2ts!(Punct::new('.', Spacing::Alone)) };
-    (:) => { tt2ts!(Punct::new(':', Spacing::Alone)) };
-    (;) => { tt2ts!(Punct::new(';', Spacing::Alone)) };
-    (|) => { tt2ts!(Punct::new('|', Spacing::Alone)) };
+use {Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
+
+macro_rules! quote_tt {
+    (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) };
+    ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) };
+    ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
+    (,) => { Punct::new(',', Spacing::Alone) };
+    (.) => { Punct::new('.', Spacing::Alone) };
+    (:) => { Punct::new(':', Spacing::Alone) };
+    (;) => { Punct::new(';', Spacing::Alone) };
+    (!) => { Punct::new('!', Spacing::Alone) };
+    (<) => { Punct::new('<', Spacing::Alone) };
+    (>) => { Punct::new('>', Spacing::Alone) };
+    (&) => { Punct::new('&', Spacing::Alone) };
+    (=) => { Punct::new('=', Spacing::Alone) };
+    ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
+}
+
+macro_rules! quote_ts {
+    ((@ $($t:tt)*)) => { $($t)* };
     (::) => {
         [
             TokenTree::from(Punct::new(':', Spacing::Joint)),
@@ -54,57 +46,45 @@ macro_rules! quote_tok {
             })
             .collect::<TokenStream>()
     };
-    (!) => { tt2ts!(Punct::new('!', Spacing::Alone)) };
-    (<) => { tt2ts!(Punct::new('<', Spacing::Alone)) };
-    (>) => { tt2ts!(Punct::new('>', Spacing::Alone)) };
-    (_) => { tt2ts!(Punct::new('_', Spacing::Alone)) };
-    (0) => { tt2ts!(Literal::i8_unsuffixed(0)) };
-    (&) => { tt2ts!(Punct::new('&', Spacing::Alone)) };
-    (=) => { tt2ts!(Punct::new('=', Spacing::Alone)) };
-    ($i:ident) => { tt2ts!(Ident::new(stringify!($i), Span::def_site())) };
-}
-
-macro_rules! quote_tree {
-    ((unquote $($t:tt)*)) => { $($t)* };
-    ((quote $($t:tt)*)) => { ($($t)*).quote() };
-    (($($t:tt)*)) => { tt2ts!(Group::new(Delimiter::Parenthesis, quote!($($t)*))) };
-    ([$($t:tt)*]) => { tt2ts!(Group::new(Delimiter::Bracket, quote!($($t)*))) };
-    ({$($t:tt)*}) => { tt2ts!(Group::new(Delimiter::Brace, quote!($($t)*))) };
-    ($t:tt) => { quote_tok!($t) };
+    ($t:tt) => { TokenTree::from(quote_tt!($t)) };
 }
 
+/// Simpler version of the real `quote!` macro, implemented solely
+/// through `macro_rules`, for bootstrapping the real implementation
+/// (see the `quote` function), which does not have access to the
+/// real `quote!` macro due to the `proc_macro` crate not being
+/// able to depend on itself.
+///
+/// Note: supported tokens are a subset of the real `quote!`, but
+/// unquoting is different: instead of `$x`, this uses `(@ expr)`.
 macro_rules! quote {
     () => { TokenStream::new() };
     ($($t:tt)*) => {
-        [$(quote_tree!($t),)*].iter()
-            .cloned()
-            .flat_map(|x| x.into_iter())
-            .collect::<TokenStream>()
+        [
+            $(TokenStream::from(quote_ts!($t)),)*
+        ].iter().cloned().collect::<TokenStream>()
     };
 }
 
-impl ProcMacro for Quoter {
-    fn expand<'cx>(&self, cx: &'cx mut ExtCtxt,
-                   _: ::syntax_pos::Span,
-                   stream: tokenstream::TokenStream)
-                   -> tokenstream::TokenStream {
-        ::__internal::set_sess(cx, || TokenStream(stream).quote().0)
+/// Quote a `TokenStream` into a `TokenStream`.
+/// This is the actual `quote!()` proc macro.
+///
+/// It is manually loaded in `CStore::load_macro_untracked`.
+#[unstable(feature = "proc_macro_quote", issue = "38356")]
+pub fn quote(stream: TokenStream) -> TokenStream {
+    if stream.is_empty() {
+        return quote!(::TokenStream::new());
     }
-}
-
-impl Quote for TokenStream {
-    fn quote(self) -> TokenStream {
-        if self.is_empty() {
-            return quote!(::TokenStream::new());
-        }
-        let mut after_dollar = false;
-        let tokens = self.into_iter().filter_map(|tree| {
+    let mut after_dollar = false;
+    let tokens = stream
+        .into_iter()
+        .filter_map(|tree| {
             if after_dollar {
                 after_dollar = false;
                 match tree {
                     TokenTree::Ident(_) => {
-                        let tree = TokenStream::from(tree);
-                        return Some(quote!(::__internal::unquote(&(unquote tree)),));
+                        return Some(quote!(Into::<::TokenStream>::into(
+                        Clone::clone(&(@ tree))),));
                     }
                     TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
                     _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
@@ -116,116 +96,55 @@ fn quote(self) -> TokenStream {
                 }
             }
 
-            Some(quote!(::TokenStream::from((quote tree)),))
-        }).flat_map(|t| t.into_iter()).collect::<TokenStream>();
-
-        if after_dollar {
-            panic!("unexpected trailing `$` in `quote!`");
-        }
-
-        quote!(
-            [(unquote tokens)].iter()
-                .cloned()
-                .flat_map(|x| x.into_iter())
-                .collect::<::TokenStream>()
-        )
-    }
-}
-
-impl Quote for TokenTree {
-    fn quote(self) -> TokenStream {
-        match self {
-            TokenTree::Punct(tt) => quote!(::TokenTree::Punct( (quote tt) )),
-            TokenTree::Group(tt) => quote!(::TokenTree::Group( (quote tt) )),
-            TokenTree::Ident(tt) => quote!(::TokenTree::Ident( (quote tt) )),
-            TokenTree::Literal(tt) => quote!(::TokenTree::Literal( (quote tt) )),
-        }
-    }
-}
-
-impl Quote for char {
-    fn quote(self) -> TokenStream {
-        TokenTree::from(Literal::character(self)).into()
-    }
-}
-
-impl<'a> Quote for &'a str {
-    fn quote(self) -> TokenStream {
-        TokenTree::from(Literal::string(self)).into()
-    }
-}
-
-impl Quote for u16 {
-    fn quote(self) -> TokenStream {
-        TokenTree::from(Literal::u16_unsuffixed(self)).into()
-    }
-}
-
-impl Quote for Group {
-    fn quote(self) -> TokenStream {
-        quote!(::Group::new((quote self.delimiter()), (quote self.stream())))
-    }
-}
-
-impl Quote for Punct {
-    fn quote(self) -> TokenStream {
-        quote!(::Punct::new((quote self.as_char()), (quote self.spacing())))
-    }
-}
-
-impl Quote for Ident {
-    fn quote(self) -> TokenStream {
-        quote!(::Ident::new((quote self.sym.as_str()), (quote self.span())))
-    }
-}
+            Some(quote!(::TokenStream::from((@ match tree {
+                TokenTree::Punct(tt) => quote!(::TokenTree::Punct(::Punct::new(
+                    (@ TokenTree::from(Literal::character(tt.as_char()))),
+                    (@ match tt.spacing() {
+                        Spacing::Alone => quote!(::Spacing::Alone),
+                        Spacing::Joint => quote!(::Spacing::Joint),
+                    }),
+                ))),
+                TokenTree::Group(tt) => quote!(::TokenTree::Group(::Group::new(
+                    (@ match tt.delimiter() {
+                        Delimiter::Parenthesis => quote!(::Delimiter::Parenthesis),
+                        Delimiter::Brace => quote!(::Delimiter::Brace),
+                        Delimiter::Bracket => quote!(::Delimiter::Bracket),
+                        Delimiter::None => quote!(::Delimiter::None),
+                    }),
+                    (@ quote(tt.stream())),
+                ))),
+                TokenTree::Ident(tt) => quote!(::TokenTree::Ident(::Ident::new(
+                    (@ TokenTree::from(Literal::string(&tt.to_string()))),
+                    (@ quote_span(tt.span())),
+                ))),
+                TokenTree::Literal(tt) => quote!(::TokenTree::Literal({
+                    let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
+                        .parse::<::TokenStream>()
+                        .unwrap()
+                        .into_iter();
+                    if let (Some(::TokenTree::Literal(mut lit)), None) =
+                        (iter.next(), iter.next())
+                    {
+                        lit.set_span((@ quote_span(tt.span())));
+                        lit
+                    } else {
+                        unreachable!()
+                    }
+                }))
+            })),))
+        })
+        .collect::<TokenStream>();
 
-impl Quote for Span {
-    fn quote(self) -> TokenStream {
-        quote!(::Span::def_site())
+    if after_dollar {
+        panic!("unexpected trailing `$` in `quote!`");
     }
-}
-
-impl Quote for Literal {
-    fn quote(self) -> TokenStream {
-        quote! {{
-            let mut iter = (quote self.to_string())
-                .parse::<::TokenStream>()
-                .unwrap()
-                .into_iter();
-            if let (Some(::TokenTree::Literal(mut lit)), None) = (iter.next(), iter.next()) {
-                lit.set_span((quote self.span));
-                lit
-            } else {
-                unreachable!()
-            }
-        }}
-    }
-}
-
-impl Quote for Delimiter {
-    fn quote(self) -> TokenStream {
-        macro_rules! gen_match {
-            ($($i:ident),*) => {
-                match self {
-                    $(Delimiter::$i => { quote!(::Delimiter::$i) })*
-                }
-            }
-        }
 
-        gen_match!(Parenthesis, Brace, Bracket, None)
-    }
+    quote!([(@ tokens)].iter().cloned().collect::<::TokenStream>())
 }
 
-impl Quote for Spacing {
-    fn quote(self) -> TokenStream {
-        macro_rules! gen_match {
-            ($($i:ident),*) => {
-                match self {
-                    $(Spacing::$i => { quote!(::Spacing::$i) })*
-                }
-            }
-        }
-
-        gen_match!(Alone, Joint)
-    }
+/// Quote a `Span` into a `TokenStream`.
+/// This is needed to implement a custom quoter.
+#[unstable(feature = "proc_macro_quote", issue = "38356")]
+pub fn quote_span(_: Span) -> TokenStream {
+    quote!(::Span::def_site())
 }
index d5078642147681488abb37c11e75d9a027217306..e3a7918f8c5897df13ba8790fca77ce962daa3a9 100644 (file)
@@ -39,7 +39,6 @@
 use syntax::attr;
 use syntax::codemap;
 use syntax::edition::Edition;
-use syntax::ext::base::SyntaxExtension;
 use syntax::parse::filemap_to_stream;
 use syntax::symbol::Symbol;
 use syntax_pos::{Span, NO_EXPANSION, FileName};
@@ -517,8 +516,11 @@ fn load_macro_untracked(&self, id: DefId, sess: &Session) -> LoadedMacro {
             return LoadedMacro::ProcMacro(proc_macros[id.index.to_proc_macro_index()].1.clone());
         } else if data.name == "proc_macro" &&
                   self.get_crate_data(id.krate).item_name(id.index) == "quote" {
+            use syntax::ext::base::SyntaxExtension;
+            use syntax_ext::proc_macro_impl::BangProcMacro;
+
             let ext = SyntaxExtension::ProcMacro {
-                expander: Box::new(::proc_macro::__internal::Quoter),
+                expander: Box::new(BangProcMacro { inner: ::proc_macro::quote }),
                 allow_internal_unstable: true,
                 edition: data.root.edition,
             };
index 5c9915e94e56d34a15b5a3eee3234cc3d43df5c6..d535c1ef90357d0c83da785a571ec9dbb3bee160 100644 (file)
@@ -19,6 +19,7 @@
 #![feature(libc)]
 #![feature(macro_at_most_once_rep)]
 #![feature(proc_macro_internals)]
+#![feature(proc_macro_quote)]
 #![feature(quote)]
 #![feature(rustc_diagnostic_macros)]
 #![feature(slice_sort_by_cached_key)]