2017-03-17 23:41:09 +00:00
|
|
|
//! # Quasiquoter
|
2017-06-05 01:41:33 +00:00
|
|
|
//! This file contains the implementation internals of the quasiquoter provided by `quote!`.
|
|
|
|
|
2017-11-05 20:21:05 +02:00
|
|
|
//! This quasiquoter uses macros 2.0 hygiene to reliably access
|
|
|
|
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
|
2017-03-17 23:41:09 +00:00
|
|
|
|
2019-02-04 03:55:40 +09:00
|
|
|
use crate::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
|
2018-04-22 03:05:02 +03:00
|
|
|
|
|
|
|
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) };
|
|
|
|
($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! quote_ts {
|
|
|
|
((@ $($t:tt)*)) => { $($t)* };
|
2017-11-05 18:25:45 +02:00
|
|
|
(::) => {
|
|
|
|
[
|
2018-05-05 21:09:41 +03:00
|
|
|
TokenTree::from(Punct::new(':', Spacing::Joint)),
|
|
|
|
TokenTree::from(Punct::new(':', Spacing::Alone)),
|
2018-04-02 08:19:32 -07:00
|
|
|
].iter()
|
|
|
|
.cloned()
|
|
|
|
.map(|mut x| {
|
|
|
|
x.set_span(Span::def_site());
|
|
|
|
x
|
|
|
|
})
|
|
|
|
.collect::<TokenStream>()
|
2017-11-05 18:25:45 +02:00
|
|
|
};
|
2018-04-22 03:05:02 +03:00
|
|
|
($t:tt) => { TokenTree::from(quote_tt!($t)) };
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
|
2018-04-22 03:05:02 +03:00
|
|
|
/// 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)`.
|
2017-03-17 23:41:09 +00:00
|
|
|
macro_rules! quote {
|
2018-05-25 19:44:07 -07:00
|
|
|
() => { TokenStream::new() };
|
2017-11-05 18:25:45 +02:00
|
|
|
($($t:tt)*) => {
|
2018-04-22 03:05:02 +03:00
|
|
|
[
|
|
|
|
$(TokenStream::from(quote_ts!($t)),)*
|
|
|
|
].iter().cloned().collect::<TokenStream>()
|
2017-11-05 18:25:45 +02:00
|
|
|
};
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
|
|
|
|
2018-04-22 03:05:02 +03:00
|
|
|
/// Quote a `TokenStream` into a `TokenStream`.
|
2019-08-20 02:26:40 +03:00
|
|
|
/// This is the actual implementation of the `quote!()` proc macro.
|
2018-04-22 03:05:02 +03:00
|
|
|
///
|
2019-08-20 02:26:40 +03:00
|
|
|
/// It is loaded by the compiler in `register_builtin_macros`.
|
2018-10-01 10:42:16 -07:00
|
|
|
#[unstable(feature = "proc_macro_quote", issue = "54722")]
|
2018-04-22 03:05:02 +03:00
|
|
|
pub fn quote(stream: TokenStream) -> TokenStream {
|
|
|
|
if stream.is_empty() {
|
2019-02-04 03:55:40 +09:00
|
|
|
return quote!(crate::TokenStream::new());
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
Implement span quoting for proc-macros
This PR implements span quoting, allowing proc-macros to produce spans
pointing *into their own crate*. This is used by the unstable
`proc_macro::quote!` macro, allowing us to get error messages like this:
```
error[E0412]: cannot find type `MissingType` in this scope
--> $DIR/auxiliary/span-from-proc-macro.rs:37:20
|
LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream {
| ----------------------------------------------------------------------------------- in this expansion of procedural macro `#[error_from_attribute]`
...
LL | field: MissingType
| ^^^^^^^^^^^ not found in this scope
|
::: $DIR/span-from-proc-macro.rs:8:1
|
LL | #[error_from_attribute]
| ----------------------- in this macro invocation
```
Here, `MissingType` occurs inside the implementation of the proc-macro
`#[error_from_attribute]`. Previosuly, this would always result in a
span pointing at `#[error_from_attribute]`
This will make many proc-macro-related error message much more useful -
when a proc-macro generates code containing an error, users will get an
error message pointing directly at that code (within the macro
definition), instead of always getting a span pointing at the macro
invocation site.
This is implemented as follows:
* When a proc-macro crate is being *compiled*, it causes the `quote!`
macro to get run. This saves all of the sapns in the input to `quote!`
into the metadata of *the proc-macro-crate* (which we are currently
compiling). The `quote!` macro then expands to a call to
`proc_macro::Span::recover_proc_macro_span(id)`, where `id` is an
opaque identifier for the span in the crate metadata.
* When the same proc-macro crate is *run* (e.g. it is loaded from disk
and invoked by some consumer crate), the call to
`proc_macro::Span::recover_proc_macro_span` causes us to load the span
from the proc-macro crate's metadata. The proc-macro then produces a
`TokenStream` containing a `Span` pointing into the proc-macro crate
itself.
The recursive nature of 'quote!' can be difficult to understand at
first. The file `src/test/ui/proc-macro/quote-debug.stdout` shows
the output of the `quote!` macro, which should make this eaier to
understand.
This PR also supports custom quoting spans in custom quote macros (e.g.
the `quote` crate). All span quoting goes through the
`proc_macro::quote_span` method, which can be called by a custom quote
macro to perform span quoting. An example of this usage is provided in
`src/test/ui/proc-macro/auxiliary/custom-quote.rs`
Custom quoting currently has a few limitations:
In order to quote a span, we need to generate a call to
`proc_macro::Span::recover_proc_macro_span`. However, proc-macros
support renaming the `proc_macro` crate, so we can't simply hardcode
this path. Previously, the `quote_span` method used the path
`crate::Span` - however, this only works when it is called by the
builtin `quote!` macro in the same crate. To support being called from
arbitrary crates, we need access to the name of the `proc_macro` crate
to generate a path. This PR adds an additional argument to `quote_span`
to specify the name of the `proc_macro` crate. Howver, this feels kind
of hacky, and we may want to change this before stabilizing anything
quote-related.
Additionally, using `quote_span` currently requires enabling the
`proc_macro_internals` feature. The builtin `quote!` macro
has an `#[allow_internal_unstable]` attribute, but this won't work for
custom quote implementations. This will likely require some additional
tricks to apply `allow_internal_unstable` to the span of
`proc_macro::Span::recover_proc_macro_span`.
2020-08-02 19:52:16 -04:00
|
|
|
let proc_macro_crate = quote!(crate);
|
2018-04-22 03:05:02 +03:00
|
|
|
let mut after_dollar = false;
|
|
|
|
let tokens = stream
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|tree| {
|
2017-11-05 20:21:05 +02:00
|
|
|
if after_dollar {
|
|
|
|
after_dollar = false;
|
2018-04-02 08:19:32 -07:00
|
|
|
match tree {
|
2018-05-05 21:09:41 +03:00
|
|
|
TokenTree::Ident(_) => {
|
2019-02-04 03:55:40 +09:00
|
|
|
return Some(quote!(Into::<crate::TokenStream>::into(
|
2018-04-22 03:05:02 +03:00
|
|
|
Clone::clone(&(@ tree))),));
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
2018-05-05 21:09:41 +03:00
|
|
|
TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
|
2017-11-05 20:21:05 +02:00
|
|
|
_ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
2018-05-13 18:35:57 +03:00
|
|
|
} else if let TokenTree::Punct(ref tt) = tree {
|
2018-05-05 21:09:41 +03:00
|
|
|
if tt.as_char() == '$' {
|
2018-04-02 08:19:32 -07:00
|
|
|
after_dollar = true;
|
|
|
|
return None;
|
|
|
|
}
|
2017-11-05 20:21:05 +02:00
|
|
|
}
|
2017-03-17 23:41:09 +00:00
|
|
|
|
2019-02-04 03:55:40 +09:00
|
|
|
Some(quote!(crate::TokenStream::from((@ match tree {
|
|
|
|
TokenTree::Punct(tt) => quote!(crate::TokenTree::Punct(crate::Punct::new(
|
2018-04-22 03:05:02 +03:00
|
|
|
(@ TokenTree::from(Literal::character(tt.as_char()))),
|
|
|
|
(@ match tt.spacing() {
|
2019-02-04 03:55:40 +09:00
|
|
|
Spacing::Alone => quote!(crate::Spacing::Alone),
|
|
|
|
Spacing::Joint => quote!(crate::Spacing::Joint),
|
2018-04-22 03:05:02 +03:00
|
|
|
}),
|
|
|
|
))),
|
2019-02-04 03:55:40 +09:00
|
|
|
TokenTree::Group(tt) => quote!(crate::TokenTree::Group(crate::Group::new(
|
2018-04-22 03:05:02 +03:00
|
|
|
(@ match tt.delimiter() {
|
2019-02-04 03:55:40 +09:00
|
|
|
Delimiter::Parenthesis => quote!(crate::Delimiter::Parenthesis),
|
|
|
|
Delimiter::Brace => quote!(crate::Delimiter::Brace),
|
|
|
|
Delimiter::Bracket => quote!(crate::Delimiter::Bracket),
|
|
|
|
Delimiter::None => quote!(crate::Delimiter::None),
|
2018-04-22 03:05:02 +03:00
|
|
|
}),
|
|
|
|
(@ quote(tt.stream())),
|
|
|
|
))),
|
2019-02-04 03:55:40 +09:00
|
|
|
TokenTree::Ident(tt) => quote!(crate::TokenTree::Ident(crate::Ident::new(
|
2018-04-22 03:05:02 +03:00
|
|
|
(@ TokenTree::from(Literal::string(&tt.to_string()))),
|
Implement span quoting for proc-macros
This PR implements span quoting, allowing proc-macros to produce spans
pointing *into their own crate*. This is used by the unstable
`proc_macro::quote!` macro, allowing us to get error messages like this:
```
error[E0412]: cannot find type `MissingType` in this scope
--> $DIR/auxiliary/span-from-proc-macro.rs:37:20
|
LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream {
| ----------------------------------------------------------------------------------- in this expansion of procedural macro `#[error_from_attribute]`
...
LL | field: MissingType
| ^^^^^^^^^^^ not found in this scope
|
::: $DIR/span-from-proc-macro.rs:8:1
|
LL | #[error_from_attribute]
| ----------------------- in this macro invocation
```
Here, `MissingType` occurs inside the implementation of the proc-macro
`#[error_from_attribute]`. Previosuly, this would always result in a
span pointing at `#[error_from_attribute]`
This will make many proc-macro-related error message much more useful -
when a proc-macro generates code containing an error, users will get an
error message pointing directly at that code (within the macro
definition), instead of always getting a span pointing at the macro
invocation site.
This is implemented as follows:
* When a proc-macro crate is being *compiled*, it causes the `quote!`
macro to get run. This saves all of the sapns in the input to `quote!`
into the metadata of *the proc-macro-crate* (which we are currently
compiling). The `quote!` macro then expands to a call to
`proc_macro::Span::recover_proc_macro_span(id)`, where `id` is an
opaque identifier for the span in the crate metadata.
* When the same proc-macro crate is *run* (e.g. it is loaded from disk
and invoked by some consumer crate), the call to
`proc_macro::Span::recover_proc_macro_span` causes us to load the span
from the proc-macro crate's metadata. The proc-macro then produces a
`TokenStream` containing a `Span` pointing into the proc-macro crate
itself.
The recursive nature of 'quote!' can be difficult to understand at
first. The file `src/test/ui/proc-macro/quote-debug.stdout` shows
the output of the `quote!` macro, which should make this eaier to
understand.
This PR also supports custom quoting spans in custom quote macros (e.g.
the `quote` crate). All span quoting goes through the
`proc_macro::quote_span` method, which can be called by a custom quote
macro to perform span quoting. An example of this usage is provided in
`src/test/ui/proc-macro/auxiliary/custom-quote.rs`
Custom quoting currently has a few limitations:
In order to quote a span, we need to generate a call to
`proc_macro::Span::recover_proc_macro_span`. However, proc-macros
support renaming the `proc_macro` crate, so we can't simply hardcode
this path. Previously, the `quote_span` method used the path
`crate::Span` - however, this only works when it is called by the
builtin `quote!` macro in the same crate. To support being called from
arbitrary crates, we need access to the name of the `proc_macro` crate
to generate a path. This PR adds an additional argument to `quote_span`
to specify the name of the `proc_macro` crate. Howver, this feels kind
of hacky, and we may want to change this before stabilizing anything
quote-related.
Additionally, using `quote_span` currently requires enabling the
`proc_macro_internals` feature. The builtin `quote!` macro
has an `#[allow_internal_unstable]` attribute, but this won't work for
custom quote implementations. This will likely require some additional
tricks to apply `allow_internal_unstable` to the span of
`proc_macro::Span::recover_proc_macro_span`.
2020-08-02 19:52:16 -04:00
|
|
|
(@ quote_span(proc_macro_crate.clone(), tt.span())),
|
2018-04-22 03:05:02 +03:00
|
|
|
))),
|
2019-02-04 03:55:40 +09:00
|
|
|
TokenTree::Literal(tt) => quote!(crate::TokenTree::Literal({
|
2018-04-22 03:05:02 +03:00
|
|
|
let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
|
2019-02-04 03:55:40 +09:00
|
|
|
.parse::<crate::TokenStream>()
|
2018-04-22 03:05:02 +03:00
|
|
|
.unwrap()
|
|
|
|
.into_iter();
|
2019-02-04 03:55:40 +09:00
|
|
|
if let (Some(crate::TokenTree::Literal(mut lit)), None) =
|
2018-04-22 03:05:02 +03:00
|
|
|
(iter.next(), iter.next())
|
|
|
|
{
|
Implement span quoting for proc-macros
This PR implements span quoting, allowing proc-macros to produce spans
pointing *into their own crate*. This is used by the unstable
`proc_macro::quote!` macro, allowing us to get error messages like this:
```
error[E0412]: cannot find type `MissingType` in this scope
--> $DIR/auxiliary/span-from-proc-macro.rs:37:20
|
LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream {
| ----------------------------------------------------------------------------------- in this expansion of procedural macro `#[error_from_attribute]`
...
LL | field: MissingType
| ^^^^^^^^^^^ not found in this scope
|
::: $DIR/span-from-proc-macro.rs:8:1
|
LL | #[error_from_attribute]
| ----------------------- in this macro invocation
```
Here, `MissingType` occurs inside the implementation of the proc-macro
`#[error_from_attribute]`. Previosuly, this would always result in a
span pointing at `#[error_from_attribute]`
This will make many proc-macro-related error message much more useful -
when a proc-macro generates code containing an error, users will get an
error message pointing directly at that code (within the macro
definition), instead of always getting a span pointing at the macro
invocation site.
This is implemented as follows:
* When a proc-macro crate is being *compiled*, it causes the `quote!`
macro to get run. This saves all of the sapns in the input to `quote!`
into the metadata of *the proc-macro-crate* (which we are currently
compiling). The `quote!` macro then expands to a call to
`proc_macro::Span::recover_proc_macro_span(id)`, where `id` is an
opaque identifier for the span in the crate metadata.
* When the same proc-macro crate is *run* (e.g. it is loaded from disk
and invoked by some consumer crate), the call to
`proc_macro::Span::recover_proc_macro_span` causes us to load the span
from the proc-macro crate's metadata. The proc-macro then produces a
`TokenStream` containing a `Span` pointing into the proc-macro crate
itself.
The recursive nature of 'quote!' can be difficult to understand at
first. The file `src/test/ui/proc-macro/quote-debug.stdout` shows
the output of the `quote!` macro, which should make this eaier to
understand.
This PR also supports custom quoting spans in custom quote macros (e.g.
the `quote` crate). All span quoting goes through the
`proc_macro::quote_span` method, which can be called by a custom quote
macro to perform span quoting. An example of this usage is provided in
`src/test/ui/proc-macro/auxiliary/custom-quote.rs`
Custom quoting currently has a few limitations:
In order to quote a span, we need to generate a call to
`proc_macro::Span::recover_proc_macro_span`. However, proc-macros
support renaming the `proc_macro` crate, so we can't simply hardcode
this path. Previously, the `quote_span` method used the path
`crate::Span` - however, this only works when it is called by the
builtin `quote!` macro in the same crate. To support being called from
arbitrary crates, we need access to the name of the `proc_macro` crate
to generate a path. This PR adds an additional argument to `quote_span`
to specify the name of the `proc_macro` crate. Howver, this feels kind
of hacky, and we may want to change this before stabilizing anything
quote-related.
Additionally, using `quote_span` currently requires enabling the
`proc_macro_internals` feature. The builtin `quote!` macro
has an `#[allow_internal_unstable]` attribute, but this won't work for
custom quote implementations. This will likely require some additional
tricks to apply `allow_internal_unstable` to the span of
`proc_macro::Span::recover_proc_macro_span`.
2020-08-02 19:52:16 -04:00
|
|
|
lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span())));
|
2018-04-22 03:05:02 +03:00
|
|
|
lit
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
})),))
|
|
|
|
})
|
|
|
|
.collect::<TokenStream>();
|
2017-03-17 23:41:09 +00:00
|
|
|
|
2018-04-22 03:05:02 +03:00
|
|
|
if after_dollar {
|
|
|
|
panic!("unexpected trailing `$` in `quote!`");
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|
2017-11-05 20:21:05 +02:00
|
|
|
|
2019-02-04 03:55:40 +09:00
|
|
|
quote!([(@ tokens)].iter().cloned().collect::<crate::TokenStream>())
|
2017-11-05 20:21:05 +02:00
|
|
|
}
|
|
|
|
|
2018-04-22 03:05:02 +03:00
|
|
|
/// Quote a `Span` into a `TokenStream`.
|
|
|
|
/// This is needed to implement a custom quoter.
|
2018-10-01 10:42:16 -07:00
|
|
|
#[unstable(feature = "proc_macro_quote", issue = "54722")]
|
Implement span quoting for proc-macros
This PR implements span quoting, allowing proc-macros to produce spans
pointing *into their own crate*. This is used by the unstable
`proc_macro::quote!` macro, allowing us to get error messages like this:
```
error[E0412]: cannot find type `MissingType` in this scope
--> $DIR/auxiliary/span-from-proc-macro.rs:37:20
|
LL | pub fn error_from_attribute(_args: TokenStream, _input: TokenStream) -> TokenStream {
| ----------------------------------------------------------------------------------- in this expansion of procedural macro `#[error_from_attribute]`
...
LL | field: MissingType
| ^^^^^^^^^^^ not found in this scope
|
::: $DIR/span-from-proc-macro.rs:8:1
|
LL | #[error_from_attribute]
| ----------------------- in this macro invocation
```
Here, `MissingType` occurs inside the implementation of the proc-macro
`#[error_from_attribute]`. Previosuly, this would always result in a
span pointing at `#[error_from_attribute]`
This will make many proc-macro-related error message much more useful -
when a proc-macro generates code containing an error, users will get an
error message pointing directly at that code (within the macro
definition), instead of always getting a span pointing at the macro
invocation site.
This is implemented as follows:
* When a proc-macro crate is being *compiled*, it causes the `quote!`
macro to get run. This saves all of the sapns in the input to `quote!`
into the metadata of *the proc-macro-crate* (which we are currently
compiling). The `quote!` macro then expands to a call to
`proc_macro::Span::recover_proc_macro_span(id)`, where `id` is an
opaque identifier for the span in the crate metadata.
* When the same proc-macro crate is *run* (e.g. it is loaded from disk
and invoked by some consumer crate), the call to
`proc_macro::Span::recover_proc_macro_span` causes us to load the span
from the proc-macro crate's metadata. The proc-macro then produces a
`TokenStream` containing a `Span` pointing into the proc-macro crate
itself.
The recursive nature of 'quote!' can be difficult to understand at
first. The file `src/test/ui/proc-macro/quote-debug.stdout` shows
the output of the `quote!` macro, which should make this eaier to
understand.
This PR also supports custom quoting spans in custom quote macros (e.g.
the `quote` crate). All span quoting goes through the
`proc_macro::quote_span` method, which can be called by a custom quote
macro to perform span quoting. An example of this usage is provided in
`src/test/ui/proc-macro/auxiliary/custom-quote.rs`
Custom quoting currently has a few limitations:
In order to quote a span, we need to generate a call to
`proc_macro::Span::recover_proc_macro_span`. However, proc-macros
support renaming the `proc_macro` crate, so we can't simply hardcode
this path. Previously, the `quote_span` method used the path
`crate::Span` - however, this only works when it is called by the
builtin `quote!` macro in the same crate. To support being called from
arbitrary crates, we need access to the name of the `proc_macro` crate
to generate a path. This PR adds an additional argument to `quote_span`
to specify the name of the `proc_macro` crate. Howver, this feels kind
of hacky, and we may want to change this before stabilizing anything
quote-related.
Additionally, using `quote_span` currently requires enabling the
`proc_macro_internals` feature. The builtin `quote!` macro
has an `#[allow_internal_unstable]` attribute, but this won't work for
custom quote implementations. This will likely require some additional
tricks to apply `allow_internal_unstable` to the span of
`proc_macro::Span::recover_proc_macro_span`.
2020-08-02 19:52:16 -04:00
|
|
|
pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream {
|
|
|
|
let id = span.save_span();
|
|
|
|
quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id)))))
|
2017-03-17 23:41:09 +00:00
|
|
|
}
|