Merge aosp/upstream-master up to Release 1.0.2 am: cb0f96530f am: 6b1a2b83fb am: f079f81b22
am: efc43820c9
Change-Id: I6db631e4e62d769af01f446459296c1b9913d1c9
diff --git a/.travis.yml b/.travis.yml
index 46d8006..7182d9b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,7 @@
rust:
- stable
- - 1.15.1
+ - 1.31.0
- beta
script:
diff --git a/Android.bp b/Android.bp
index af5bf5e..f0f6261 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,10 +1,9 @@
rust_library_rlib {
name: "libquote",
- deny_warnings: false,
host_supported: true,
crate_name: "quote",
srcs: ["src/lib.rs"],
rlibs: ["libproc_macro2"],
features: ["proc-macro"],
- edition: "2015",
+ edition: "2018",
}
diff --git a/Cargo.toml b/Cargo.toml
index 8171fb0..c052022 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "quote"
-version = "0.6.13" # don't forget to update html_root_url, version in readme for breaking changes
+version = "1.0.2" # don't forget to update html_root_url, version in readme for breaking changes
authors = ["David Tolnay <dtolnay@gmail.com>"]
license = "MIT OR Apache-2.0"
description = "Quasi-quoting macro quote!(...)"
@@ -10,9 +10,17 @@
categories = ["development-tools::procedural-macro-helpers"]
readme = "README.md"
include = ["Cargo.toml", "src/**/*.rs", "tests/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
+edition = "2018"
+
+[lib]
+name = "quote"
[dependencies]
-proc-macro2 = { version = "0.4.21", default-features = false }
+proc-macro2 = { version = "1.0", default-features = false }
+
+[dev-dependencies]
+rustversion = "0.1"
+trybuild = "1.0"
[features]
default = ["proc-macro"]
diff --git a/README.md b/README.md
index 8a5d590..7c7f743 100644
--- a/README.md
+++ b/README.md
@@ -8,13 +8,13 @@
This crate provides the [`quote!`] macro for turning Rust syntax tree data
structures into tokens of source code.
-[`quote!`]: https://docs.rs/quote/0.6/quote/macro.quote.html
+[`quote!`]: https://docs.rs/quote/1.0/quote/macro.quote.html
Procedural macros in Rust receive a stream of tokens as input, execute arbitrary
Rust code to determine how to manipulate those tokens, and produce a stream of
tokens to hand back to the compiler to compile into the caller's crate.
-Quasi-quoting is a solution to one piece of that -- producing tokens to return
-to the compiler.
+Quasi-quoting is a solution to one piece of that — producing tokens to
+return to the compiler.
The idea of quasi-quoting is that we write *code* that we treat as *data*.
Within the `quote!` macro, we can write what looks like code to our text editor
@@ -35,7 +35,7 @@
```toml
[dependencies]
-quote = "0.6"
+quote = "1.0"
```
## Syntax
@@ -44,13 +44,13 @@
that gets packaged into a [`TokenStream`] and can be treated as data. You should
think of `TokenStream` as representing a fragment of Rust source code.
-[`TokenStream`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.TokenStream.html
+[`TokenStream`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.TokenStream.html
Within the `quote!` macro, interpolation is done with `#var`. Any type
implementing the [`quote::ToTokens`] trait can be interpolated. This includes
most Rust primitive types as well as most of the syntax tree types from [`syn`].
-[`quote::ToTokens`]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
+[`quote::ToTokens`]: https://docs.rs/quote/1.0/quote/trait.ToTokens.html
[`syn`]: https://github.com/dtolnay/syn
```rust
@@ -148,8 +148,20 @@
}
```
-The solution is to perform token-level manipulations using the APIs provided by
-Syn and proc-macro2.
+The solution is to build a new identifier token with the correct value. As this
+is such a common case, the `format_ident!` macro provides a convenient utility
+for doing so correctly.
+
+```rust
+let varname = format_ident!("_{}", ident);
+quote! {
+ let mut #varname = 0;
+}
+```
+
+Alternatively, the APIs provided by Syn and proc-macro2 can be used to directly
+build the identifier. This is roughly equivalent to the above, but will not
+handle `ident` being a raw identifier.
```rust
let concatenated = format!("_{}", ident);
@@ -200,30 +212,12 @@
`ToTokens` implementation. Tokens that originate within a `quote!` invocation
are spanned with [`Span::call_site()`].
-[`Span::call_site()`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html#method.call_site
+[`Span::call_site()`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html#method.call_site
A different span can be provided explicitly through the [`quote_spanned!`]
macro.
-[`quote_spanned!`]: https://docs.rs/quote/0.6/quote/macro.quote_spanned.html
-
-### Limitations
-
-- A non-repeating variable may not be interpolated inside of a repeating block
- ([#7]).
-- The same variable may not be interpolated more than once inside of a repeating
- block ([#8]).
-
-[#7]: https://github.com/dtolnay/quote/issues/7
-[#8]: https://github.com/dtolnay/quote/issues/8
-
-### Recursion limit
-
-The `quote!` macro relies on deep recursion so some large invocations may fail
-with "recursion limit reached" when you compile. If it fails, bump up the
-recursion limit by adding `#![recursion_limit = "128"]` to your crate. An even
-higher limit may be necessary for especially large invocations. You don't need
-this unless the compiler tells you that you need it.
+[`quote_spanned!`]: https://docs.rs/quote/1.0/quote/macro.quote_spanned.html
<br>
diff --git a/benches/bench.rs b/benches/bench.rs
index cdbdb45..c76a638 100644
--- a/benches/bench.rs
+++ b/benches/bench.rs
@@ -1,7 +1,6 @@
#![feature(test)]
#![recursion_limit = "512"]
-extern crate quote;
extern crate test;
use quote::quote;
diff --git a/build.rs b/build.rs
deleted file mode 100644
index a0474a4..0000000
--- a/build.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-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,
- };
-
- // 128-bit integers stabilized in Rust 1.26:
- // https://blog.rust-lang.org/2018/05/10/Rust-1.26.html
- if minor >= 26 {
- println!("cargo:rustc-cfg=integer128");
- }
-}
-
-fn rustc_minor_version() -> Option<u32> {
- 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/ext.rs b/src/ext.rs
index 7ebbe30..9e9b4a5 100644
--- a/src/ext.rs
+++ b/src/ext.rs
@@ -17,7 +17,7 @@
/// For use by `ToTokens` implementations.
///
- /// ```edition2018
+ /// ```
/// # use quote::{quote, TokenStreamExt, ToTokens};
/// # use proc_macro2::TokenStream;
/// #
@@ -32,29 +32,29 @@
/// let tokens = quote!(#X);
/// assert_eq!(tokens.to_string(), "true false");
/// ```
- fn append_all<T, I>(&mut self, iter: I)
+ fn append_all<I>(&mut self, iter: I)
where
- T: ToTokens,
- I: IntoIterator<Item = T>;
+ I: IntoIterator,
+ I::Item: ToTokens;
/// For use by `ToTokens` implementations.
///
/// Appends all of the items in the iterator `I`, separated by the tokens
/// `U`.
- fn append_separated<T, I, U>(&mut self, iter: I, op: U)
+ fn append_separated<I, U>(&mut self, iter: I, op: U)
where
- T: ToTokens,
- I: IntoIterator<Item = T>,
+ I: IntoIterator,
+ I::Item: ToTokens,
U: ToTokens;
/// For use by `ToTokens` implementations.
///
/// Appends all tokens in the iterator `I`, appending `U` after each
/// element, including after the last element of the iterator.
- fn append_terminated<T, I, U>(&mut self, iter: I, term: U)
+ fn append_terminated<I, U>(&mut self, iter: I, term: U)
where
- T: ToTokens,
- I: IntoIterator<Item = T>,
+ I: IntoIterator,
+ I::Item: ToTokens,
U: ToTokens;
}
@@ -66,20 +66,20 @@
self.extend(iter::once(token.into()));
}
- fn append_all<T, I>(&mut self, iter: I)
+ fn append_all<I>(&mut self, iter: I)
where
- T: ToTokens,
- I: IntoIterator<Item = T>,
+ I: IntoIterator,
+ I::Item: ToTokens,
{
for token in iter {
token.to_tokens(self);
}
}
- fn append_separated<T, I, U>(&mut self, iter: I, op: U)
+ fn append_separated<I, U>(&mut self, iter: I, op: U)
where
- T: ToTokens,
- I: IntoIterator<Item = T>,
+ I: IntoIterator,
+ I::Item: ToTokens,
U: ToTokens,
{
for (i, token) in iter.into_iter().enumerate() {
@@ -90,10 +90,10 @@
}
}
- fn append_terminated<T, I, U>(&mut self, iter: I, term: U)
+ fn append_terminated<I, U>(&mut self, iter: I, term: U)
where
- T: ToTokens,
- I: IntoIterator<Item = T>,
+ I: IntoIterator,
+ I::Item: ToTokens,
U: ToTokens,
{
for token in iter {
diff --git a/src/format.rs b/src/format.rs
new file mode 100644
index 0000000..13c8811
--- /dev/null
+++ b/src/format.rs
@@ -0,0 +1,164 @@
+/// Formatting macro for constructing `Ident`s.
+///
+/// <br>
+///
+/// # Syntax
+///
+/// Syntax is copied from the [`format!`] macro, supporting both positional and
+/// named arguments.
+///
+/// Only a limited set of formatting traits are supported. The current mapping
+/// of format types to traits is:
+///
+/// * `{}` ⇒ [`IdentFragment`]
+/// * `{:o}` ⇒ [`Octal`](`std::fmt::Octal`)
+/// * `{:x}` ⇒ [`LowerHex`](`std::fmt::LowerHex`)
+/// * `{:X}` ⇒ [`UpperHex`](`std::fmt::UpperHex`)
+/// * `{:b}` ⇒ [`Binary`](`std::fmt::Binary`)
+///
+/// See [`std::fmt`] for more information.
+///
+/// <br>
+///
+/// # IdentFragment
+///
+/// Unlike `format!`, this macro uses the [`IdentFragment`] formatting trait by
+/// default. This trait is like `Display`, with a few differences:
+///
+/// * `IdentFragment` is only implemented for a limited set of types, such as
+/// unsigned integers and strings.
+/// * [`Ident`] arguments will have their `r#` prefixes stripped, if present.
+///
+/// [`Ident`]: `proc_macro2::Ident`
+///
+/// <br>
+///
+/// # Hygiene
+///
+/// The [`Span`] of the first `Ident` argument is used as the span of the final
+/// identifier, falling back to [`Span::call_site`] when no identifiers are
+/// provided.
+///
+/// ```
+/// # use quote::format_ident;
+/// # let ident = format_ident!("Ident");
+/// // If `ident` is an Ident, the span of `my_ident` will be inherited from it.
+/// let my_ident = format_ident!("My{}{}", ident, "IsCool");
+/// assert_eq!(my_ident, "MyIdentIsCool");
+/// ```
+///
+/// Alternatively, the span can be overridden by passing the `span` named
+/// argument.
+///
+/// ```
+/// # use quote::format_ident;
+/// # const IGNORE_TOKENS: &'static str = stringify! {
+/// let my_span = /* ... */;
+/// # };
+/// # let my_span = proc_macro2::Span::call_site();
+/// format_ident!("MyIdent", span = my_span);
+/// ```
+///
+/// [`Span`]: `proc_macro2::Span`
+/// [`Span::call_site`]: `proc_macro2::Span::call_site`
+///
+/// <p><br></p>
+///
+/// # Panics
+///
+/// This method will panic if the resulting formatted string is not a valid
+/// identifier.
+///
+/// <br>
+///
+/// # Examples
+///
+/// Composing raw and non-raw identifiers:
+/// ```
+/// # use quote::format_ident;
+/// let my_ident = format_ident!("My{}", "Ident");
+/// assert_eq!(my_ident, "MyIdent");
+///
+/// let raw = format_ident!("r#Raw");
+/// assert_eq!(raw, "r#Raw");
+///
+/// let my_ident_raw = format_ident!("{}Is{}", my_ident, raw);
+/// assert_eq!(my_ident_raw, "MyIdentIsRaw");
+/// ```
+///
+/// Integer formatting options:
+/// ```
+/// # use quote::format_ident;
+/// let num: u32 = 10;
+///
+/// let decimal = format_ident!("Id_{}", num);
+/// assert_eq!(decimal, "Id_10");
+///
+/// let octal = format_ident!("Id_{:o}", num);
+/// assert_eq!(octal, "Id_12");
+///
+/// let binary = format_ident!("Id_{:b}", num);
+/// assert_eq!(binary, "Id_1010");
+///
+/// let lower_hex = format_ident!("Id_{:x}", num);
+/// assert_eq!(lower_hex, "Id_a");
+///
+/// let upper_hex = format_ident!("Id_{:X}", num);
+/// assert_eq!(upper_hex, "Id_A");
+/// ```
+#[macro_export]
+macro_rules! format_ident {
+ ($fmt:expr) => {
+ $crate::format_ident_impl!([
+ ::std::option::Option::None,
+ $fmt
+ ])
+ };
+
+ ($fmt:expr, $($rest:tt)*) => {
+ $crate::format_ident_impl!([
+ ::std::option::Option::None,
+ $fmt
+ ] $($rest)*)
+ };
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! format_ident_impl {
+ // Final state
+ ([$span:expr, $($fmt:tt)*]) => {
+ $crate::__rt::mk_ident(&format!($($fmt)*), $span)
+ };
+
+ // Span argument
+ ([$old:expr, $($fmt:tt)*] span = $span:expr) => {
+ $crate::format_ident_impl!([$old, $($fmt)*] span = $span,)
+ };
+ ([$old:expr, $($fmt:tt)*] span = $span:expr, $($rest:tt)*) => {
+ $crate::format_ident_impl!([
+ ::std::option::Option::Some::<$crate::__rt::Span>($span),
+ $($fmt)*
+ ] $($rest)*)
+ };
+
+ // Named argument
+ ([$span:expr, $($fmt:tt)*] $name:ident = $arg:expr) => {
+ $crate::format_ident_impl!([$span, $($fmt)*] $name = $arg,)
+ };
+ ([$span:expr, $($fmt:tt)*] $name:ident = $arg:expr, $($rest:tt)*) => {
+ match $crate::__rt::IdentFragmentAdapter(&$arg) {
+ arg => $crate::format_ident_impl!([$span.or(arg.span()), $($fmt)*, $name = arg] $($rest)*),
+ }
+ };
+
+ // Positional argument
+ ([$span:expr, $($fmt:tt)*] $arg:expr) => {
+ $crate::format_ident_impl!([$span, $($fmt)*] $arg,)
+ };
+ ([$span:expr, $($fmt:tt)*] $arg:expr, $($rest:tt)*) => {
+ match $crate::__rt::IdentFragmentAdapter(&$arg) {
+ arg => $crate::format_ident_impl!([$span.or(arg.span()), $($fmt)*, arg] $($rest)*),
+ }
+ };
+}
diff --git a/src/ident_fragment.rs b/src/ident_fragment.rs
new file mode 100644
index 0000000..09ead65
--- /dev/null
+++ b/src/ident_fragment.rs
@@ -0,0 +1,72 @@
+use proc_macro2::{Ident, Span};
+use std::fmt;
+
+/// Specialized formatting trait used by `format_ident!`.
+///
+/// [`Ident`] arguments formatted using this trait will have their `r#` prefix
+/// stripped, if present.
+///
+/// See [`format_ident!`] for more information.
+pub trait IdentFragment {
+ /// Format this value as an identifier fragment.
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
+
+ /// Span associated with this `IdentFragment`.
+ ///
+ /// If non-`None`, may be inherited by formatted identifiers.
+ fn span(&self) -> Option<Span> {
+ None
+ }
+}
+
+impl<'a, T: IdentFragment + ?Sized> IdentFragment for &'a T {
+ fn span(&self) -> Option<Span> {
+ <T as IdentFragment>::span(*self)
+ }
+
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ IdentFragment::fmt(*self, f)
+ }
+}
+
+impl<'a, T: IdentFragment + ?Sized> IdentFragment for &'a mut T {
+ fn span(&self) -> Option<Span> {
+ <T as IdentFragment>::span(*self)
+ }
+
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ IdentFragment::fmt(*self, f)
+ }
+}
+
+impl IdentFragment for Ident {
+ fn span(&self) -> Option<Span> {
+ Some(self.span())
+ }
+
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let id = self.to_string();
+ if id.starts_with("r#") {
+ fmt::Display::fmt(&id[2..], f)
+ } else {
+ fmt::Display::fmt(&id[..], f)
+ }
+ }
+}
+
+// Limited set of types which this is implemented for, as we want to avoid types
+// which will often include non-identifier characters in their `Display` impl.
+macro_rules! ident_fragment_display {
+ ($($T:ty),*) => {
+ $(
+ impl IdentFragment for $T {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+ }
+ )*
+ }
+}
+
+ident_fragment_display!(bool, str, String);
+ident_fragment_display!(u8, u16, u32, u64, u128, usize);
diff --git a/src/lib.rs b/src/lib.rs
index c201d60..3341a16 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,8 +6,8 @@
//! Procedural macros in Rust receive a stream of tokens as input, execute
//! arbitrary Rust code to determine how to manipulate those tokens, and produce
//! a stream of tokens to hand back to the compiler to compile into the caller's
-//! crate. Quasi-quoting is a solution to one piece of that -- producing tokens
-//! to return to the compiler.
+//! crate. Quasi-quoting is a solution to one piece of that — producing
+//! tokens to return to the compiler.
//!
//! The idea of quasi-quoting is that we write *code* that we treat as *data*.
//! Within the `quote!` macro, we can write what looks like code to our text
@@ -21,14 +21,13 @@
//! general-purpose Rust quasi-quoting library and is not specific to procedural
//! macros.
//!
-//! *Version requirement: Quote supports any compiler version back to Rust's
-//! very first support for procedural macros in Rust 1.15.0.*
-//!
//! ```toml
//! [dependencies]
-//! quote = "0.6"
+//! quote = "1.0"
//! ```
//!
+//! <br>
+//!
//! # Example
//!
//! The following quasi-quoted block of code is something you might find in [a]
@@ -41,7 +40,7 @@
//! [a]: https://serde.rs/
//! [`quote_spanned!`]: macro.quote_spanned.html
//!
-//! ```edition2018
+//! ```
//! # use quote::quote;
//! #
//! # let generics = "";
@@ -72,42 +71,45 @@
//! }
//! };
//! ```
-//!
-//! # Recursion limit
-//!
-//! The `quote!` macro relies on deep recursion so some large invocations may
-//! fail with "recursion limit reached" when you compile. If it fails, bump up
-//! the recursion limit by adding `#![recursion_limit = "128"]` to your crate.
-//! An even higher limit may be necessary for especially large invocations.
// Quote types in rustdoc of other crates get linked to here.
-#![doc(html_root_url = "https://docs.rs/quote/0.6.13")]
+#![doc(html_root_url = "https://docs.rs/quote/1.0.2")]
#[cfg(all(
not(all(target_arch = "wasm32", target_os = "unknown")),
feature = "proc-macro"
))]
extern crate proc_macro;
-extern crate proc_macro2;
mod ext;
-pub use ext::TokenStreamExt;
-
+mod format;
+mod ident_fragment;
mod to_tokens;
-pub use to_tokens::ToTokens;
// Not public API.
#[doc(hidden)]
#[path = "runtime.rs"]
pub mod __rt;
+pub use crate::ext::TokenStreamExt;
+pub use crate::ident_fragment::IdentFragment;
+pub use crate::to_tokens::ToTokens;
+
+// Not public API.
+#[doc(hidden)]
+pub mod spanned;
+
/// The whole point.
///
/// Performs variable interpolation against the input and produces it as
-/// [`TokenStream`]. For returning tokens to the compiler in a procedural macro, use
-/// `into()` to build a `TokenStream`.
+/// [`proc_macro2::TokenStream`].
///
-/// [`TokenStream`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.TokenStream.html
+/// Note: for returning tokens to the compiler in a procedural macro, use
+/// `.into()` on the result to convert to [`proc_macro::TokenStream`].
+///
+/// [`TokenStream`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.TokenStream.html
+///
+/// <br>
///
/// # Interpolation
///
@@ -124,27 +126,15 @@
/// Repetition is done using `#(...)*` or `#(...),*` again similar to
/// `macro_rules!`. This iterates through the elements of any variable
/// interpolated within the repetition and inserts a copy of the repetition body
-/// for each one. The variables in an interpolation may be anything that
-/// implements `IntoIterator`, including `Vec` or a pre-existing iterator.
+/// for each one. The variables in an interpolation may be a `Vec`, slice,
+/// `BTreeSet`, or any `Iterator`.
///
/// - `#(#var)*` — no separators
/// - `#(#var),*` — the character before the asterisk is used as a separator
/// - `#( struct #var; )*` — the repetition can contain other tokens
/// - `#( #k => println!("{}", #v), )*` — even multiple interpolations
///
-/// There are two limitations around interpolations in a repetition:
-///
-/// - Every interpolation inside of a repetition must be a distinct variable.
-/// That is, `#(#a #a)*` is not allowed. Work around this by collecting `a`
-/// into a vector and taking references `a1 = &a` and `a2 = &a` which you use
-/// inside the repetition: `#(#a1 #a2)*`. Where possible, use meaningful names
-/// that indicate the distinct role of each copy.
-///
-/// - Every interpolation inside of a repetition must be iterable. If we have
-/// `vec` which is a vector and `ident` which is a single identifier,
-/// `#(#ident #vec)*` is not allowed. Work around this by using
-/// `std::iter::repeat(ident)` to produce an iterable that can be used from
-/// within the repetition.
+/// <br>
///
/// # Hygiene
///
@@ -152,12 +142,14 @@
/// `ToTokens` implementation. Tokens that originate within the `quote!`
/// invocation are spanned with [`Span::call_site()`].
///
-/// [`Span::call_site()`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html#method.call_site
+/// [`Span::call_site()`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html#method.call_site
///
/// A different span can be provided through the [`quote_spanned!`] macro.
///
/// [`quote_spanned!`]: macro.quote_spanned.html
///
+/// <br>
+///
/// # Return type
///
/// The macro evaluates to an expression of type `proc_macro2::TokenStream`.
@@ -178,9 +170,11 @@
///
/// [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
///
+/// <br>
+///
/// # Examples
///
-/// ## Procedural macro
+/// ### Procedural macro
///
/// The structure of a basic procedural macro is as follows. Refer to the [Syn]
/// crate for further useful guidance on using `quote!` as part of a procedural
@@ -188,12 +182,14 @@
///
/// [Syn]: https://github.com/dtolnay/syn
///
-/// ```edition2018
+/// ```
/// # #[cfg(any())]
/// extern crate proc_macro;
-/// # use proc_macro2 as proc_macro;
+/// # extern crate proc_macro2;
///
+/// # #[cfg(any())]
/// use proc_macro::TokenStream;
+/// # use proc_macro2::TokenStream;
/// use quote::quote;
///
/// # const IGNORE_TOKENS: &'static str = stringify! {
@@ -223,14 +219,16 @@
/// }
/// ```
///
-/// ## Combining quoted fragments
+/// <p><br></p>
+///
+/// ### Combining quoted fragments
///
/// Usually you don't end up constructing an entire final `TokenStream` in one
/// piece. Different parts may come from different helper functions. The tokens
/// produced by `quote!` themselves implement `ToTokens` and so can be
/// interpolated into later `quote!` invocations to build up a final result.
///
-/// ```edition2018
+/// ```
/// # use quote::quote;
/// #
/// let type_definition = quote! {...};
@@ -242,7 +240,9 @@
/// };
/// ```
///
-/// ## Constructing identifiers
+/// <p><br></p>
+///
+/// ### Constructing identifiers
///
/// Suppose we have an identifier `ident` which came from somewhere in a macro
/// input and we need to modify it in some way for the macro output. Let's
@@ -252,7 +252,7 @@
/// behavior of concatenating them. The underscore and the identifier will
/// continue to be two separate tokens as if you had written `_ x`.
///
-/// ```edition2018
+/// ```
/// # use proc_macro2::{self as syn, Span};
/// # use quote::quote;
/// #
@@ -265,10 +265,28 @@
/// # ;
/// ```
///
-/// The solution is to perform token-level manipulations using the APIs provided
-/// by Syn and proc-macro2.
+/// The solution is to build a new identifier token with the correct value. As
+/// this is such a common case, the [`format_ident!`] macro provides a
+/// convenient utility for doing so correctly.
///
-/// ```edition2018
+/// ```
+/// # use proc_macro2::{Ident, Span};
+/// # use quote::{format_ident, quote};
+/// #
+/// # let ident = Ident::new("i", Span::call_site());
+/// #
+/// let varname = format_ident!("_{}", ident);
+/// quote! {
+/// let mut #varname = 0;
+/// }
+/// # ;
+/// ```
+///
+/// Alternatively, the APIs provided by Syn and proc-macro2 can be used to
+/// directly build the identifier. This is roughly equivalent to the above, but
+/// will not handle `ident` being a raw identifier.
+///
+/// ```
/// # use proc_macro2::{self as syn, Span};
/// # use quote::quote;
/// #
@@ -282,13 +300,15 @@
/// # ;
/// ```
///
-/// ## Making method calls
+/// <p><br></p>
+///
+/// ### Making method calls
///
/// Let's say our macro requires some type specified in the macro input to have
/// a constructor called `new`. We have the type in a variable called
/// `field_type` of type `syn::Type` and want to invoke the constructor.
///
-/// ```edition2018
+/// ```
/// # use quote::quote;
/// #
/// # let field_type = quote!(...);
@@ -306,7 +326,7 @@
/// syntax. Ordinarily in handwritten Rust we would write `Vec::<i32>::new()`
/// but for macros often the following is more convenient.
///
-/// ```edition2018
+/// ```
/// # use quote::quote;
/// #
/// # let field_type = quote!(...);
@@ -321,7 +341,7 @@
///
/// A similar pattern is appropriate for trait methods.
///
-/// ```edition2018
+/// ```
/// # use quote::quote;
/// #
/// # let field_type = quote!(...);
@@ -331,25 +351,137 @@
/// }
/// # ;
/// ```
-#[macro_export(local_inner_macros)]
+///
+/// <p><br></p>
+///
+/// ### Interpolating text inside of doc comments
+///
+/// Neither doc comments nor string literals get interpolation behavior in
+/// quote:
+///
+/// ```compile_fail
+/// quote! {
+/// /// try to interpolate: #ident
+/// ///
+/// /// ...
+/// }
+/// ```
+///
+/// ```compile_fail
+/// quote! {
+/// #[doc = "try to interpolate: #ident"]
+/// }
+/// ```
+///
+/// Macro calls in a doc attribute are not valid syntax:
+///
+/// ```compile_fail
+/// quote! {
+/// #[doc = concat!("try to interpolate: ", stringify!(#ident))]
+/// }
+/// ```
+///
+/// Instead the best way to build doc comments that involve variables is by
+/// formatting the doc string literal outside of quote.
+///
+/// ```rust
+/// # use proc_macro2::{Ident, Span};
+/// # use quote::quote;
+/// #
+/// # const IGNORE: &str = stringify! {
+/// let msg = format!(...);
+/// # };
+/// #
+/// # let ident = Ident::new("var", Span::call_site());
+/// # let msg = format!("try to interpolate: {}", ident);
+/// quote! {
+/// #[doc = #msg]
+/// ///
+/// /// ...
+/// }
+/// # ;
+/// ```
+///
+/// <p><br></p>
+///
+/// ### Indexing into a tuple struct
+///
+/// When interpolating indices of a tuple or tuple struct, we need them not to
+/// appears suffixed as integer literals by interpolating them as [`syn::Index`]
+/// instead.
+///
+/// [`syn::Index`]: https://docs.rs/syn/1.0/syn/struct.Index.html
+///
+/// ```compile_fail
+/// let i = 0usize..self.fields.len();
+///
+/// // expands to 0 + self.0usize.heap_size() + self.1usize.heap_size() + ...
+/// // which is not valid syntax
+/// quote! {
+/// 0 #( + self.#i.heap_size() )*
+/// }
+/// ```
+///
+/// ```
+/// # use proc_macro2::{Ident, TokenStream};
+/// # use quote::quote;
+/// #
+/// # mod syn {
+/// # use proc_macro2::{Literal, TokenStream};
+/// # use quote::{ToTokens, TokenStreamExt};
+/// #
+/// # pub struct Index(usize);
+/// #
+/// # impl From<usize> for Index {
+/// # fn from(i: usize) -> Self {
+/// # Index(i)
+/// # }
+/// # }
+/// #
+/// # impl ToTokens for Index {
+/// # fn to_tokens(&self, tokens: &mut TokenStream) {
+/// # tokens.append(Literal::usize_unsuffixed(self.0));
+/// # }
+/// # }
+/// # }
+/// #
+/// # struct Struct {
+/// # fields: Vec<Ident>,
+/// # }
+/// #
+/// # impl Struct {
+/// # fn example(&self) -> TokenStream {
+/// let i = (0..self.fields.len()).map(syn::Index::from);
+///
+/// // expands to 0 + self.0.heap_size() + self.1.heap_size() + ...
+/// quote! {
+/// 0 #( + self.#i.heap_size() )*
+/// }
+/// # }
+/// # }
+/// ```
+#[macro_export]
macro_rules! quote {
($($tt:tt)*) => {
- quote_spanned!($crate::__rt::Span::call_site()=> $($tt)*)
+ $crate::quote_spanned!($crate::__rt::Span::call_site()=> $($tt)*)
};
}
/// Same as `quote!`, but applies a given span to all tokens originating within
/// the macro invocation.
///
+/// <br>
+///
/// # Syntax
///
/// A span expression of type [`Span`], followed by `=>`, followed by the tokens
-/// to quote. The span expression should be brief -- use a variable for anything
-/// more than a few characters. There should be no space before the `=>` token.
+/// to quote. The span expression should be brief — use a variable for
+/// anything more than a few characters. There should be no space before the
+/// `=>` token.
///
-/// [`Span`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html
+/// [`Span`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html
///
-/// ```edition2018
+/// ```
/// # use proc_macro2::Span;
/// # use quote::quote_spanned;
/// #
@@ -374,12 +506,16 @@
/// being evaluated in the context of the procedural macro and the remaining
/// tokens being evaluated in the generated code.
///
+/// <br>
+///
/// # Hygiene
///
/// Any interpolated tokens preserve the `Span` information provided by their
/// `ToTokens` implementation. Tokens that originate within the `quote_spanned!`
/// invocation are spanned with the given span argument.
///
+/// <br>
+///
/// # Example
///
/// The following procedural macro code uses `quote_spanned!` to assert that a
@@ -388,7 +524,7 @@
///
/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
///
-/// ```edition2018
+/// ```
/// # use quote::{quote_spanned, TokenStreamExt, ToTokens};
/// # use proc_macro2::{Span, TokenStream};
/// #
@@ -426,436 +562,387 @@
///
/// In this example it is important for the where-clause to be spanned with the
/// line/column information of the user's input type so that error messages are
-/// placed appropriately by the compiler. But it is also incredibly important
-/// that `Sync` resolves at the macro definition site and not the macro call
-/// site. If we resolve `Sync` at the same span that the user's type is going to
-/// be resolved, then they could bypass our check by defining their own trait
-/// named `Sync` that is implemented for their type.
-#[macro_export(local_inner_macros)]
+/// placed appropriately by the compiler.
+#[macro_export]
macro_rules! quote_spanned {
($span:expr=> $($tt:tt)*) => {{
let mut _s = $crate::__rt::TokenStream::new();
- let _span = $span;
- quote_each_token!(_s _span $($tt)*);
+ let _span: $crate::__rt::Span = $span;
+ $crate::quote_each_token!(_s _span $($tt)*);
_s
}};
}
-// Extract the names of all #metavariables and pass them to the $finish macro.
+// Extract the names of all #metavariables and pass them to the $call macro.
//
-// in: pounded_var_names!(then () a #b c #( #d )* #e)
-// out: then!(() b d e)
-#[macro_export(local_inner_macros)]
+// in: pounded_var_names!(then!(...) a #b c #( #d )* #e)
+// out: then!(... b);
+// then!(... d);
+// then!(... e);
+#[macro_export]
#[doc(hidden)]
macro_rules! pounded_var_names {
- ($finish:ident ($($found:ident)*) # ( $($inner:tt)* ) $($rest:tt)*) => {
- pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
- };
-
- ($finish:ident ($($found:ident)*) # [ $($inner:tt)* ] $($rest:tt)*) => {
- pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
- };
-
- ($finish:ident ($($found:ident)*) # { $($inner:tt)* } $($rest:tt)*) => {
- pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
- };
-
- ($finish:ident ($($found:ident)*) # $first:ident $($rest:tt)*) => {
- pounded_var_names!($finish ($($found)* $first) $($rest)*)
- };
-
- ($finish:ident ($($found:ident)*) ( $($inner:tt)* ) $($rest:tt)*) => {
- pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
- };
-
- ($finish:ident ($($found:ident)*) [ $($inner:tt)* ] $($rest:tt)*) => {
- pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
- };
-
- ($finish:ident ($($found:ident)*) { $($inner:tt)* } $($rest:tt)*) => {
- pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
- };
-
- ($finish:ident ($($found:ident)*) $ignore:tt $($rest:tt)*) => {
- pounded_var_names!($finish ($($found)*) $($rest)*)
- };
-
- ($finish:ident ($($found:ident)*)) => {
- $finish!(() $($found)*)
+ ($call:ident! $extra:tt $($tts:tt)*) => {
+ $crate::pounded_var_names_with_context!($call! $extra
+ (@ $($tts)*)
+ ($($tts)* @)
+ )
};
}
-// in: nested_tuples_pat!(() a b c d e)
-// out: ((((a b) c) d) e)
-//
-// in: nested_tuples_pat!(() a)
-// out: a
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
-macro_rules! nested_tuples_pat {
- (()) => {
- &()
- };
-
- (() $first:ident $($rest:ident)*) => {
- nested_tuples_pat!(($first) $($rest)*)
- };
-
- (($pat:pat) $first:ident $($rest:ident)*) => {
- nested_tuples_pat!((($pat, $first)) $($rest)*)
- };
-
- (($done:pat)) => {
- $done
+macro_rules! pounded_var_names_with_context {
+ ($call:ident! $extra:tt ($($b1:tt)*) ($($curr:tt)*)) => {
+ $(
+ $crate::pounded_var_with_context!($call! $extra $b1 $curr);
+ )*
};
}
-// in: multi_zip_expr!(() a b c d e)
-// out: a.into_iter().zip(b).zip(c).zip(d).zip(e)
-//
-// in: multi_zip_iter!(() a)
-// out: a
-#[macro_export(local_inner_macros)]
+#[macro_export]
#[doc(hidden)]
-macro_rules! multi_zip_expr {
- (()) => {
- &[]
+macro_rules! pounded_var_with_context {
+ ($call:ident! $extra:tt $b1:tt ( $($inner:tt)* )) => {
+ $crate::pounded_var_names!($call! $extra $($inner)*);
};
- (() $single:ident) => {
- $single
+ ($call:ident! $extra:tt $b1:tt [ $($inner:tt)* ]) => {
+ $crate::pounded_var_names!($call! $extra $($inner)*);
};
- (() $first:ident $($rest:ident)*) => {
- multi_zip_expr!(($first.into_iter()) $($rest)*)
+ ($call:ident! $extra:tt $b1:tt { $($inner:tt)* }) => {
+ $crate::pounded_var_names!($call! $extra $($inner)*);
};
- (($zips:expr) $first:ident $($rest:ident)*) => {
- multi_zip_expr!(($zips.zip($first)) $($rest)*)
+ ($call:ident!($($extra:tt)*) # $var:ident) => {
+ $crate::$call!($($extra)* $var);
};
- (($done:expr)) => {
- $done
+ ($call:ident! $extra:tt $b1:tt $curr:tt) => {};
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_bind_into_iter {
+ ($has_iter:ident $var:ident) => {
+ // `mut` may be unused if $var occurs multiple times in the list.
+ #[allow(unused_mut)]
+ let (mut $var, i) = $var.quote_into_iter();
+ let $has_iter = $has_iter | i;
};
}
-#[macro_export(local_inner_macros)]
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_bind_next_or_break {
+ ($var:ident) => {
+ let $var = match $var.next() {
+ Some(_x) => $crate::__rt::RepInterp(_x),
+ None => break,
+ };
+ };
+}
+
+#[macro_export]
#[doc(hidden)]
macro_rules! quote_each_token {
- ($tokens:ident $span:ident) => {};
-
- ($tokens:ident $span:ident # ! $($rest:tt)*) => {
- quote_each_token!($tokens $span #);
- quote_each_token!($tokens $span !);
- quote_each_token!($tokens $span $($rest)*);
+ ($tokens:ident $span:ident $($tts:tt)*) => {
+ $crate::quote_tokens_with_context!($tokens $span
+ (@ @ @ @ @ @ $($tts)*)
+ (@ @ @ @ @ $($tts)* @)
+ (@ @ @ @ $($tts)* @ @)
+ (@ @ @ $(($tts))* @ @ @)
+ (@ @ $($tts)* @ @ @ @)
+ (@ $($tts)* @ @ @ @ @)
+ ($($tts)* @ @ @ @ @ @)
+ );
};
+}
- ($tokens:ident $span:ident # ( $($inner:tt)* ) * $($rest:tt)*) => {
- for pounded_var_names!(nested_tuples_pat () $($inner)*)
- in pounded_var_names!(multi_zip_expr () $($inner)*) {
- quote_each_token!($tokens $span $($inner)*);
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_tokens_with_context {
+ ($tokens:ident $span:ident
+ ($($b3:tt)*) ($($b2:tt)*) ($($b1:tt)*)
+ ($($curr:tt)*)
+ ($($a1:tt)*) ($($a2:tt)*) ($($a3:tt)*)
+ ) => {
+ $(
+ $crate::quote_token_with_context!($tokens $span $b3 $b2 $b1 $curr $a1 $a2 $a3);
+ )*
+ };
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_token_with_context {
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt @ $a1:tt $a2:tt $a3:tt) => {};
+
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) * $a3:tt) => {{
+ use $crate::__rt::ext::*;
+ let has_iter = $crate::__rt::ThereIsNoIteratorInRepetition;
+ $crate::pounded_var_names!(quote_bind_into_iter!(has_iter) () $($inner)*);
+ let _: $crate::__rt::HasIterator = has_iter;
+ // This is `while true` instead of `loop` because if there are no
+ // iterators used inside of this repetition then the body would not
+ // contain any `break`, so the compiler would emit unreachable code
+ // warnings on anything below the loop. We use has_iter to detect and
+ // fail to compile when there are no iterators, so here we just work
+ // around the unneeded extra warning.
+ while true {
+ $crate::pounded_var_names!(quote_bind_next_or_break!() () $($inner)*);
+ $crate::quote_each_token!($tokens $span $($inner)*);
}
- quote_each_token!($tokens $span $($rest)*);
- };
+ }};
+ ($tokens:ident $span:ident $b3:tt $b2:tt # (( $($inner:tt)* )) * $a2:tt $a3:tt) => {};
+ ($tokens:ident $span:ident $b3:tt # ( $($inner:tt)* ) (*) $a1:tt $a2:tt $a3:tt) => {};
- ($tokens:ident $span:ident # ( $($inner:tt)* ) $sep:tt * $($rest:tt)*) => {
- for (_i, pounded_var_names!(nested_tuples_pat () $($inner)*))
- in pounded_var_names!(multi_zip_expr () $($inner)*).into_iter().enumerate() {
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) $sep:tt *) => {{
+ use $crate::__rt::ext::*;
+ let mut _i = 0usize;
+ let has_iter = $crate::__rt::ThereIsNoIteratorInRepetition;
+ $crate::pounded_var_names!(quote_bind_into_iter!(has_iter) () $($inner)*);
+ let _: $crate::__rt::HasIterator = has_iter;
+ while true {
+ $crate::pounded_var_names!(quote_bind_next_or_break!() () $($inner)*);
if _i > 0 {
- quote_each_token!($tokens $span $sep);
+ $crate::quote_token!($tokens $span $sep);
}
- quote_each_token!($tokens $span $($inner)*);
+ _i += 1;
+ $crate::quote_each_token!($tokens $span $($inner)*);
}
- quote_each_token!($tokens $span $($rest)*);
+ }};
+ ($tokens:ident $span:ident $b3:tt $b2:tt # (( $($inner:tt)* )) $sep:tt * $a3:tt) => {};
+ ($tokens:ident $span:ident $b3:tt # ( $($inner:tt)* ) ($sep:tt) * $a2:tt $a3:tt) => {};
+ ($tokens:ident $span:ident # ( $($inner:tt)* ) * (*) $a1:tt $a2:tt $a3:tt) => {
+ // https://github.com/dtolnay/quote/issues/130
+ $crate::quote_token!($tokens $span *);
};
+ ($tokens:ident $span:ident # ( $($inner:tt)* ) $sep:tt (*) $a1:tt $a2:tt $a3:tt) => {};
- ($tokens:ident $span:ident # [ $($inner:tt)* ] $($rest:tt)*) => {
- quote_each_token!($tokens $span #);
- $tokens.extend({
- let mut g = $crate::__rt::Group::new(
- $crate::__rt::Delimiter::Bracket,
- quote_spanned!($span=> $($inner)*),
- );
- g.set_span($span);
- Some($crate::__rt::TokenTree::from(g))
- });
- quote_each_token!($tokens $span $($rest)*);
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) $var:ident $a2:tt $a3:tt) => {
+ $crate::ToTokens::to_tokens(&$var, &mut $tokens);
};
-
- ($tokens:ident $span:ident # $first:ident $($rest:tt)*) => {
- $crate::ToTokens::to_tokens(&$first, &mut $tokens);
- quote_each_token!($tokens $span $($rest)*);
+ ($tokens:ident $span:ident $b3:tt $b2:tt # ($var:ident) $a1:tt $a2:tt $a3:tt) => {};
+ ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt ($curr:tt) $a1:tt $a2:tt $a3:tt) => {
+ $crate::quote_token!($tokens $span $curr);
};
+}
- ($tokens:ident $span:ident ( $($first:tt)* ) $($rest:tt)*) => {
+#[macro_export]
+#[doc(hidden)]
+macro_rules! quote_token {
+ ($tokens:ident $span:ident ( $($inner:tt)* )) => {
$tokens.extend({
let mut g = $crate::__rt::Group::new(
$crate::__rt::Delimiter::Parenthesis,
- quote_spanned!($span=> $($first)*),
+ $crate::quote_spanned!($span=> $($inner)*),
);
g.set_span($span);
Some($crate::__rt::TokenTree::from(g))
});
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident [ $($first:tt)* ] $($rest:tt)*) => {
+ ($tokens:ident $span:ident [ $($inner:tt)* ]) => {
$tokens.extend({
let mut g = $crate::__rt::Group::new(
$crate::__rt::Delimiter::Bracket,
- quote_spanned!($span=> $($first)*),
+ $crate::quote_spanned!($span=> $($inner)*),
);
g.set_span($span);
Some($crate::__rt::TokenTree::from(g))
});
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident { $($first:tt)* } $($rest:tt)*) => {
+ ($tokens:ident $span:ident { $($inner:tt)* }) => {
$tokens.extend({
let mut g = $crate::__rt::Group::new(
$crate::__rt::Delimiter::Brace,
- quote_spanned!($span=> $($first)*),
+ $crate::quote_spanned!($span=> $($inner)*),
);
g.set_span($span);
Some($crate::__rt::TokenTree::from(g))
});
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident + $($rest:tt)*) => {
+ ($tokens:ident $span:ident +) => {
$crate::__rt::push_add(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident += $($rest:tt)*) => {
+ ($tokens:ident $span:ident +=) => {
$crate::__rt::push_add_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident & $($rest:tt)*) => {
+ ($tokens:ident $span:ident &) => {
$crate::__rt::push_and(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident && $($rest:tt)*) => {
+ ($tokens:ident $span:ident &&) => {
$crate::__rt::push_and_and(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident &= $($rest:tt)*) => {
+ ($tokens:ident $span:ident &=) => {
$crate::__rt::push_and_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident @ $($rest:tt)*) => {
+ ($tokens:ident $span:ident @) => {
$crate::__rt::push_at(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident ! $($rest:tt)*) => {
+ ($tokens:ident $span:ident !) => {
$crate::__rt::push_bang(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident ^ $($rest:tt)*) => {
+ ($tokens:ident $span:ident ^) => {
$crate::__rt::push_caret(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident ^= $($rest:tt)*) => {
+ ($tokens:ident $span:ident ^=) => {
$crate::__rt::push_caret_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident : $($rest:tt)*) => {
+ ($tokens:ident $span:ident :) => {
$crate::__rt::push_colon(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident :: $($rest:tt)*) => {
+ ($tokens:ident $span:ident ::) => {
$crate::__rt::push_colon2(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident , $($rest:tt)*) => {
+ ($tokens:ident $span:ident ,) => {
$crate::__rt::push_comma(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident / $($rest:tt)*) => {
+ ($tokens:ident $span:ident /) => {
$crate::__rt::push_div(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident /= $($rest:tt)*) => {
+ ($tokens:ident $span:ident /=) => {
$crate::__rt::push_div_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident . $($rest:tt)*) => {
+ ($tokens:ident $span:ident .) => {
$crate::__rt::push_dot(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident .. $($rest:tt)*) => {
+ ($tokens:ident $span:ident ..) => {
$crate::__rt::push_dot2(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident ... $($rest:tt)*) => {
+ ($tokens:ident $span:ident ...) => {
$crate::__rt::push_dot3(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident ..= $($rest:tt)*) => {
+ ($tokens:ident $span:ident ..=) => {
$crate::__rt::push_dot_dot_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident = $($rest:tt)*) => {
+ ($tokens:ident $span:ident =) => {
$crate::__rt::push_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident == $($rest:tt)*) => {
+ ($tokens:ident $span:ident ==) => {
$crate::__rt::push_eq_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident >= $($rest:tt)*) => {
+ ($tokens:ident $span:ident >=) => {
$crate::__rt::push_ge(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident > $($rest:tt)*) => {
+ ($tokens:ident $span:ident >) => {
$crate::__rt::push_gt(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident <= $($rest:tt)*) => {
+ ($tokens:ident $span:ident <=) => {
$crate::__rt::push_le(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident < $($rest:tt)*) => {
+ ($tokens:ident $span:ident <) => {
$crate::__rt::push_lt(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident *= $($rest:tt)*) => {
+ ($tokens:ident $span:ident *=) => {
$crate::__rt::push_mul_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident != $($rest:tt)*) => {
+ ($tokens:ident $span:ident !=) => {
$crate::__rt::push_ne(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident | $($rest:tt)*) => {
+ ($tokens:ident $span:ident |) => {
$crate::__rt::push_or(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident |= $($rest:tt)*) => {
+ ($tokens:ident $span:ident |=) => {
$crate::__rt::push_or_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident || $($rest:tt)*) => {
+ ($tokens:ident $span:ident ||) => {
$crate::__rt::push_or_or(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident # $($rest:tt)*) => {
+ ($tokens:ident $span:ident #) => {
$crate::__rt::push_pound(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident ? $($rest:tt)*) => {
+ ($tokens:ident $span:ident ?) => {
$crate::__rt::push_question(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident -> $($rest:tt)*) => {
+ ($tokens:ident $span:ident ->) => {
$crate::__rt::push_rarrow(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident <- $($rest:tt)*) => {
+ ($tokens:ident $span:ident <-) => {
$crate::__rt::push_larrow(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident % $($rest:tt)*) => {
+ ($tokens:ident $span:ident %) => {
$crate::__rt::push_rem(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident %= $($rest:tt)*) => {
+ ($tokens:ident $span:ident %=) => {
$crate::__rt::push_rem_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident => $($rest:tt)*) => {
+ ($tokens:ident $span:ident =>) => {
$crate::__rt::push_fat_arrow(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident ; $($rest:tt)*) => {
+ ($tokens:ident $span:ident ;) => {
$crate::__rt::push_semi(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident << $($rest:tt)*) => {
+ ($tokens:ident $span:ident <<) => {
$crate::__rt::push_shl(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident <<= $($rest:tt)*) => {
+ ($tokens:ident $span:ident <<=) => {
$crate::__rt::push_shl_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident >> $($rest:tt)*) => {
+ ($tokens:ident $span:ident >>) => {
$crate::__rt::push_shr(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident >>= $($rest:tt)*) => {
+ ($tokens:ident $span:ident >>=) => {
$crate::__rt::push_shr_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident * $($rest:tt)*) => {
+ ($tokens:ident $span:ident *) => {
$crate::__rt::push_star(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident - $($rest:tt)*) => {
+ ($tokens:ident $span:ident -) => {
$crate::__rt::push_sub(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident -= $($rest:tt)*) => {
+ ($tokens:ident $span:ident -=) => {
$crate::__rt::push_sub_eq(&mut $tokens, $span);
- quote_each_token!($tokens $span $($rest)*);
};
- ($tokens:ident $span:ident $first:tt $($rest:tt)*) => {
- $crate::__rt::parse(&mut $tokens, $span, quote_stringify!($first));
- quote_each_token!($tokens $span $($rest)*);
- };
-}
-
-// Unhygienically invoke whatever `stringify` the caller has in scope i.e. not a
-// local macro. The macros marked `local_inner_macros` above cannot invoke
-// `stringify` directly.
-#[macro_export]
-#[doc(hidden)]
-macro_rules! quote_stringify {
- ($tt:tt) => {
- stringify!($tt)
+ ($tokens:ident $span:ident $other:tt) => {
+ $crate::__rt::parse(&mut $tokens, $span, stringify!($other));
};
}
diff --git a/src/runtime.rs b/src/runtime.rs
index 75337c9..4a1c14c 100644
--- a/src/runtime.rs
+++ b/src/runtime.rs
@@ -1,6 +1,185 @@
-use ext::TokenStreamExt;
+use crate::{IdentFragment, ToTokens, TokenStreamExt};
+use std::fmt;
+use std::ops::BitOr;
+
pub use proc_macro2::*;
+pub struct HasIterator; // True
+pub struct ThereIsNoIteratorInRepetition; // False
+
+impl BitOr<ThereIsNoIteratorInRepetition> for ThereIsNoIteratorInRepetition {
+ type Output = ThereIsNoIteratorInRepetition;
+ fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> ThereIsNoIteratorInRepetition {
+ ThereIsNoIteratorInRepetition
+ }
+}
+
+impl BitOr<ThereIsNoIteratorInRepetition> for HasIterator {
+ type Output = HasIterator;
+ fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> HasIterator {
+ HasIterator
+ }
+}
+
+impl BitOr<HasIterator> for ThereIsNoIteratorInRepetition {
+ type Output = HasIterator;
+ fn bitor(self, _rhs: HasIterator) -> HasIterator {
+ HasIterator
+ }
+}
+
+impl BitOr<HasIterator> for HasIterator {
+ type Output = HasIterator;
+ fn bitor(self, _rhs: HasIterator) -> HasIterator {
+ HasIterator
+ }
+}
+
+/// Extension traits used by the implementation of `quote!`. These are defined
+/// in separate traits, rather than as a single trait due to ambiguity issues.
+///
+/// These traits expose a `quote_into_iter` method which should allow calling
+/// whichever impl happens to be applicable. Calling that method repeatedly on
+/// the returned value should be idempotent.
+pub mod ext {
+ use super::RepInterp;
+ use super::{HasIterator as HasIter, ThereIsNoIteratorInRepetition as DoesNotHaveIter};
+ use crate::ToTokens;
+ use std::collections::btree_set::{self, BTreeSet};
+ use std::slice;
+
+ /// Extension trait providing the `quote_into_iter` method on iterators.
+ pub trait RepIteratorExt: Iterator + Sized {
+ fn quote_into_iter(self) -> (Self, HasIter) {
+ (self, HasIter)
+ }
+ }
+
+ impl<T: Iterator> RepIteratorExt for T {}
+
+ /// Extension trait providing the `quote_into_iter` method for
+ /// non-iterable types. These types interpolate the same value in each
+ /// iteration of the repetition.
+ pub trait RepToTokensExt {
+ /// Pretend to be an iterator for the purposes of `quote_into_iter`.
+ /// This allows repeated calls to `quote_into_iter` to continue
+ /// correctly returning DoesNotHaveIter.
+ fn next(&self) -> Option<&Self> {
+ Some(self)
+ }
+
+ fn quote_into_iter(&self) -> (&Self, DoesNotHaveIter) {
+ (self, DoesNotHaveIter)
+ }
+ }
+
+ impl<T: ToTokens + ?Sized> RepToTokensExt for T {}
+
+ /// Extension trait providing the `quote_into_iter` method for types that
+ /// can be referenced as an iterator.
+ pub trait RepAsIteratorExt<'q> {
+ type Iter: Iterator;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter);
+ }
+
+ impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a T {
+ type Iter = T::Iter;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ <T as RepAsIteratorExt>::quote_into_iter(*self)
+ }
+ }
+
+ impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a mut T {
+ type Iter = T::Iter;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ <T as RepAsIteratorExt>::quote_into_iter(*self)
+ }
+ }
+
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] {
+ type Iter = slice::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec<T> {
+ type Iter = slice::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet<T> {
+ type Iter = btree_set::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+
+ macro_rules! array_rep_slice {
+ ($($l:tt)*) => {
+ $(
+ impl<'q, T: 'q> RepAsIteratorExt<'q> for [T; $l] {
+ type Iter = slice::Iter<'q, T>;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ (self.iter(), HasIter)
+ }
+ }
+ )*
+ }
+ }
+
+ array_rep_slice!(
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
+ );
+
+ impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp<T> {
+ type Iter = T::Iter;
+
+ fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
+ self.0.quote_into_iter()
+ }
+ }
+}
+
+// Helper type used within interpolations to allow for repeated binding names.
+// Implements the relevant traits, and exports a dummy `next()` method.
+#[derive(Copy, Clone)]
+pub struct RepInterp<T>(pub T);
+
+impl<T> RepInterp<T> {
+ // This method is intended to look like `Iterator::next`, and is called when
+ // a name is bound multiple times, as the previous binding will shadow the
+ // original `Iterator` object. This allows us to avoid advancing the
+ // iterator multiple times per iteration.
+ pub fn next(self) -> Option<T> {
+ Some(self.0)
+ }
+}
+
+impl<T: Iterator> Iterator for RepInterp<T> {
+ type Item = T::Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next()
+ }
+}
+
+impl<T: ToTokens> ToTokens for RepInterp<T> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.0.to_tokens(tokens);
+ }
+}
+
fn is_ident_start(c: u8) -> bool {
(b'a' <= c && c <= b'z') || (b'A' <= c && c <= b'Z') || c == b'_'
}
@@ -12,7 +191,7 @@
fn is_ident(token: &str) -> bool {
let mut iter = token.bytes();
let first_ok = iter.next().map(is_ident_start).unwrap_or(false);
-
+
first_ok && iter.all(is_ident_continue)
}
@@ -106,3 +285,89 @@
push_punct!(push_star '*');
push_punct!(push_sub '-');
push_punct!(push_sub_eq '-' '=');
+
+// Helper method for constructing identifiers from the `format_ident!` macro,
+// handling `r#` prefixes.
+//
+// Directly parsing the input string may produce a valid identifier,
+// although the input string was invalid, due to ignored characters such as
+// whitespace and comments. Instead, we always create a non-raw identifier
+// to validate that the string is OK, and only parse again if needed.
+//
+// The `is_ident` method defined above is insufficient for validation, as it
+// will reject non-ASCII identifiers.
+pub fn mk_ident(id: &str, span: Option<Span>) -> Ident {
+ let span = span.unwrap_or_else(Span::call_site);
+
+ let is_raw = id.starts_with("r#");
+ let unraw = Ident::new(if is_raw { &id[2..] } else { id }, span);
+ if !is_raw {
+ return unraw;
+ }
+
+ // At this point, the identifier is raw, and the unraw-ed version of it was
+ // successfully converted into an identifier. Try to produce a valid raw
+ // identifier by running the `TokenStream` parser, and unwrapping the first
+ // token as an `Ident`.
+ //
+ // FIXME: When `Ident::new_raw` becomes stable, this method should be
+ // updated to call it when available.
+ match id.parse::<TokenStream>() {
+ Ok(ts) => {
+ let mut iter = ts.into_iter();
+ match (iter.next(), iter.next()) {
+ (Some(TokenTree::Ident(mut id)), None) => {
+ id.set_span(span);
+ id
+ }
+ _ => unreachable!("valid raw ident fails to parse"),
+ }
+ }
+ Err(_) => unreachable!("valid raw ident fails to parse"),
+ }
+}
+
+// Adapts from `IdentFragment` to `fmt::Display` for use by the `format_ident!`
+// macro, and exposes span information from these fragments.
+//
+// This struct also has forwarding implementations of the formatting traits
+// `Octal`, `LowerHex`, `UpperHex`, and `Binary` to allow for their use within
+// `format_ident!`.
+#[derive(Copy, Clone)]
+pub struct IdentFragmentAdapter<T: IdentFragment>(pub T);
+
+impl<T: IdentFragment> IdentFragmentAdapter<T> {
+ pub fn span(&self) -> Option<Span> {
+ self.0.span()
+ }
+}
+
+impl<T: IdentFragment> fmt::Display for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ IdentFragment::fmt(&self.0, f)
+ }
+}
+
+impl<T: IdentFragment + fmt::Octal> fmt::Octal for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Octal::fmt(&self.0, f)
+ }
+}
+
+impl<T: IdentFragment + fmt::LowerHex> fmt::LowerHex for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::LowerHex::fmt(&self.0, f)
+ }
+}
+
+impl<T: IdentFragment + fmt::UpperHex> fmt::UpperHex for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::UpperHex::fmt(&self.0, f)
+ }
+}
+
+impl<T: IdentFragment + fmt::Binary> fmt::Binary for IdentFragmentAdapter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Binary::fmt(&self.0, f)
+ }
+}
diff --git a/src/spanned.rs b/src/spanned.rs
new file mode 100644
index 0000000..55168bd
--- /dev/null
+++ b/src/spanned.rs
@@ -0,0 +1,42 @@
+use crate::ToTokens;
+use proc_macro2::{Span, TokenStream};
+
+pub trait Spanned {
+ fn __span(&self) -> Span;
+}
+
+impl Spanned for Span {
+ fn __span(&self) -> Span {
+ *self
+ }
+}
+
+impl<T: ?Sized + ToTokens> Spanned for T {
+ fn __span(&self) -> Span {
+ join_spans(self.into_token_stream())
+ }
+}
+
+fn join_spans(tokens: TokenStream) -> Span {
+ let mut iter = tokens.into_iter().filter_map(|tt| {
+ // FIXME: This shouldn't be required, since optimally spans should
+ // never be invalid. This filter_map can probably be removed when
+ // https://github.com/rust-lang/rust/issues/43081 is resolved.
+ let span = tt.span();
+ let debug = format!("{:?}", span);
+ if debug.ends_with("bytes(0..0)") {
+ None
+ } else {
+ Some(span)
+ }
+ });
+
+ let first = match iter.next() {
+ Some(span) => span,
+ None => return Span::call_site(),
+ };
+
+ iter.fold(None, |_prev, next| Some(next))
+ .and_then(|last| first.join(last))
+ .unwrap_or(first)
+}
diff --git a/src/to_tokens.rs b/src/to_tokens.rs
index 9d221b2..7f98083 100644
--- a/src/to_tokens.rs
+++ b/src/to_tokens.rs
@@ -6,7 +6,7 @@
use proc_macro2::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
-/// Types that can be interpolated inside a [`quote!`] invocation.
+/// Types that can be interpolated inside a `quote!` invocation.
///
/// [`quote!`]: macro.quote.html
pub trait ToTokens {
@@ -22,7 +22,7 @@
/// Example implementation for a struct representing Rust paths like
/// `std::cmp::PartialEq`:
///
- /// ```edition2018
+ /// ```
/// use proc_macro2::{TokenTree, Spacing, Span, Punct, TokenStream};
/// use quote::{TokenStreamExt, ToTokens};
///
@@ -58,13 +58,21 @@
///
/// This method is implicitly implemented using `to_tokens`, and acts as a
/// convenience method for consumers of the `ToTokens` trait.
+ fn to_token_stream(&self) -> TokenStream {
+ let mut tokens = TokenStream::new();
+ self.to_tokens(&mut tokens);
+ tokens
+ }
+
+ /// Convert `self` directly into a `TokenStream` object.
+ ///
+ /// This method is implicitly implemented using `to_tokens`, and acts as a
+ /// convenience method for consumers of the `ToTokens` trait.
fn into_token_stream(self) -> TokenStream
where
Self: Sized,
{
- let mut tokens = TokenStream::new();
- self.to_tokens(&mut tokens);
- tokens
+ self.to_token_stream()
}
}
@@ -133,24 +141,20 @@
i16 => i16_suffixed
i32 => i32_suffixed
i64 => i64_suffixed
+ i128 => i128_suffixed
isize => isize_suffixed
u8 => u8_suffixed
u16 => u16_suffixed
u32 => u32_suffixed
u64 => u64_suffixed
+ u128 => u128_suffixed
usize => usize_suffixed
f32 => f32_suffixed
f64 => f64_suffixed
}
-#[cfg(integer128)]
-primitive! {
- i128 => i128_suffixed
- u128 => u128_suffixed
-}
-
impl ToTokens for char {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append(Literal::character(*self));
diff --git a/tests/compiletest.rs b/tests/compiletest.rs
new file mode 100644
index 0000000..f9aea23
--- /dev/null
+++ b/tests/compiletest.rs
@@ -0,0 +1,6 @@
+#[rustversion::attr(not(nightly), ignore)]
+#[test]
+fn ui() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/ui/*.rs");
+}
diff --git a/tests/conditional/integer128.rs b/tests/conditional/integer128.rs
deleted file mode 100644
index 61e2274..0000000
--- a/tests/conditional/integer128.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-#[test]
-fn test_integer128() {
- let ii128 = -1i128;
- let uu128 = 1u128;
-
- let tokens = quote! {
- #ii128 #uu128
- };
- let expected = "-1i128 1u128";
- assert_eq!(expected, tokens.to_string());
-}
diff --git a/tests/test.rs b/tests/test.rs
index 85562bf..957d470 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1,18 +1,10 @@
#![cfg_attr(feature = "cargo-clippy", allow(blacklisted_name))]
use std::borrow::Cow;
-
-extern crate proc_macro2;
-#[macro_use]
-extern crate quote;
+use std::collections::BTreeSet;
use proc_macro2::{Ident, Span, TokenStream};
-use quote::TokenStreamExt;
-
-mod conditional {
- #[cfg(integer128)]
- mod integer128;
-}
+use quote::{format_ident, quote, TokenStreamExt};
struct X;
@@ -125,18 +117,20 @@
let ii16 = -1i16;
let ii32 = -1i32;
let ii64 = -1i64;
+ let ii128 = -1i128;
let iisize = -1isize;
let uu8 = 1u8;
let uu16 = 1u16;
let uu32 = 1u32;
let uu64 = 1u64;
+ let uu128 = 1u128;
let uusize = 1usize;
let tokens = quote! {
- #ii8 #ii16 #ii32 #ii64 #iisize
- #uu8 #uu16 #uu32 #uu64 #uusize
+ #ii8 #ii16 #ii32 #ii64 #ii128 #iisize
+ #uu8 #uu16 #uu32 #uu64 #uu128 #uusize
};
- let expected = "-1i8 -1i16 -1i32 -1i64 -1isize 1u8 1u16 1u32 1u64 1usize";
+ let expected = "-1i8 -1i16 -1i32 -1i64 -1i128 -1isize 1u8 1u16 1u32 1u64 1u128 1usize";
assert_eq!(expected, tokens.to_string());
}
@@ -233,9 +227,42 @@
}
#[test]
-fn test_empty_repetition() {
- let tokens = quote!(#(a b)* #(c d),*);
- assert_eq!("", tokens.to_string());
+fn test_duplicate_name_repetition() {
+ let foo = &["a", "b"];
+
+ let tokens = quote! {
+ #(#foo: #foo),*
+ #(#foo: #foo),*
+ };
+
+ let expected = r#""a" : "a" , "b" : "b" "a" : "a" , "b" : "b""#;
+ assert_eq!(expected, tokens.to_string());
+}
+
+#[test]
+fn test_duplicate_name_repetition_no_copy() {
+ let foo = vec!["a".to_owned(), "b".to_owned()];
+
+ let tokens = quote! {
+ #(#foo: #foo),*
+ };
+
+ let expected = r#""a" : "a" , "b" : "b""#;
+ assert_eq!(expected, tokens.to_string());
+}
+
+#[test]
+fn test_btreeset_repetition() {
+ let mut set = BTreeSet::new();
+ set.insert("a".to_owned());
+ set.insert("b".to_owned());
+
+ let tokens = quote! {
+ #(#set: #set),*
+ };
+
+ let expected = r#""a" : "a" , "b" : "b""#;
+ assert_eq!(expected, tokens.to_string());
}
#[test]
@@ -249,6 +276,19 @@
}
#[test]
+fn test_nonrep_in_repetition() {
+ let rep = vec!["a", "b"];
+ let nonrep = "c";
+
+ let tokens = quote! {
+ #(#rep #rep : #nonrep #nonrep),*
+ };
+
+ let expected = r#""a" "a" : "c" "c" , "b" "b" : "c" "c""#;
+ assert_eq!(expected, tokens.to_string());
+}
+
+#[test]
fn test_empty_quote() {
let tokens = quote!();
assert_eq!("", tokens.to_string());
@@ -275,7 +315,7 @@
#[test]
fn test_closure() {
fn field_i(i: usize) -> Ident {
- Ident::new(&format!("__field{}", i), Span::call_site())
+ format_ident!("__field{}", i)
}
let fields = (0usize..3)
@@ -293,3 +333,97 @@
a.append_all(b);
assert_eq!("a b", a.to_string());
}
+
+#[test]
+fn test_format_ident() {
+ let id0 = format_ident!("Aa");
+ let id1 = format_ident!("Hello{x}", x = id0);
+ let id2 = format_ident!("Hello{x}", x = 5usize);
+ let id3 = format_ident!("Hello{}_{x}", id0, x = 10usize);
+ let id4 = format_ident!("Aa", span = Span::call_site());
+
+ assert_eq!(id0, "Aa");
+ assert_eq!(id1, "HelloAa");
+ assert_eq!(id2, "Hello5");
+ assert_eq!(id3, "HelloAa_10");
+ assert_eq!(id4, "Aa");
+}
+
+#[test]
+fn test_format_ident_strip_raw() {
+ let id = format_ident!("r#struct");
+ let my_id = format_ident!("MyId{}", id);
+ let raw_my_id = format_ident!("r#MyId{}", id);
+
+ assert_eq!(id, "r#struct");
+ assert_eq!(my_id, "MyIdstruct");
+ assert_eq!(raw_my_id, "r#MyIdstruct");
+}
+
+#[test]
+fn test_outer_line_comment() {
+ let tokens = quote! {
+ /// doc
+ };
+ let expected = "# [ doc = r\" doc\" ]";
+ assert_eq!(expected, tokens.to_string());
+}
+
+#[test]
+fn test_inner_line_comment() {
+ let tokens = quote! {
+ //! doc
+ };
+ let expected = "# ! [ doc = r\" doc\" ]";
+ assert_eq!(expected, tokens.to_string());
+}
+
+#[test]
+fn test_outer_block_comment() {
+ let tokens = quote! {
+ /** doc */
+ };
+ let expected = "# [ doc = r\" doc \" ]";
+ assert_eq!(expected, tokens.to_string());
+}
+
+#[test]
+fn test_inner_block_comment() {
+ let tokens = quote! {
+ /*! doc */
+ };
+ let expected = "# ! [ doc = r\" doc \" ]";
+ assert_eq!(expected, tokens.to_string());
+}
+
+#[test]
+fn test_outer_attr() {
+ let tokens = quote! {
+ #[inline]
+ };
+ let expected = "# [ inline ]";
+ assert_eq!(expected, tokens.to_string());
+}
+
+#[test]
+fn test_inner_attr() {
+ let tokens = quote! {
+ #![no_std]
+ };
+ let expected = "# ! [ no_std ]";
+ assert_eq!(expected, tokens.to_string());
+}
+
+// https://github.com/dtolnay/quote/issues/130
+#[test]
+fn test_star_after_repetition() {
+ let c = vec!['0', '1'];
+ let tokens = quote! {
+ #(
+ f(#c);
+ )*
+ *out = None;
+ };
+ let expected = "f ( '0' ) ; f ( '1' ) ; * out = None ;";
+ assert_eq!(expected, tokens.to_string());
+}
diff --git a/tests/ui/does-not-have-iter-interpolated-dup.rs b/tests/ui/does-not-have-iter-interpolated-dup.rs
new file mode 100644
index 0000000..0a39f41
--- /dev/null
+++ b/tests/ui/does-not-have-iter-interpolated-dup.rs
@@ -0,0 +1,9 @@
+use quote::quote;
+
+fn main() {
+ let nonrep = "";
+
+ // Without some protection against repetitions with no iterator somewhere
+ // inside, this would loop infinitely.
+ quote!(#(#nonrep #nonrep)*);
+}
diff --git a/tests/ui/does-not-have-iter-interpolated-dup.stderr b/tests/ui/does-not-have-iter-interpolated-dup.stderr
new file mode 100644
index 0000000..6ee6fdf
--- /dev/null
+++ b/tests/ui/does-not-have-iter-interpolated-dup.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+ --> $DIR/does-not-have-iter-interpolated-dup.rs:8:5
+ |
+8 | quote!(#(#nonrep #nonrep)*);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `quote::__rt::HasIterator`, found struct `quote::__rt::ThereIsNoIteratorInRepetition`
+ |
+ = note: expected type `quote::__rt::HasIterator`
+ found type `quote::__rt::ThereIsNoIteratorInRepetition`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/does-not-have-iter-interpolated.rs b/tests/ui/does-not-have-iter-interpolated.rs
new file mode 100644
index 0000000..2c740cc
--- /dev/null
+++ b/tests/ui/does-not-have-iter-interpolated.rs
@@ -0,0 +1,9 @@
+use quote::quote;
+
+fn main() {
+ let nonrep = "";
+
+ // Without some protection against repetitions with no iterator somewhere
+ // inside, this would loop infinitely.
+ quote!(#(#nonrep)*);
+}
diff --git a/tests/ui/does-not-have-iter-interpolated.stderr b/tests/ui/does-not-have-iter-interpolated.stderr
new file mode 100644
index 0000000..8d6c990
--- /dev/null
+++ b/tests/ui/does-not-have-iter-interpolated.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+ --> $DIR/does-not-have-iter-interpolated.rs:8:5
+ |
+8 | quote!(#(#nonrep)*);
+ | ^^^^^^^^^^^^^^^^^^^^ expected struct `quote::__rt::HasIterator`, found struct `quote::__rt::ThereIsNoIteratorInRepetition`
+ |
+ = note: expected type `quote::__rt::HasIterator`
+ found type `quote::__rt::ThereIsNoIteratorInRepetition`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/does-not-have-iter-separated.rs b/tests/ui/does-not-have-iter-separated.rs
new file mode 100644
index 0000000..c027243
--- /dev/null
+++ b/tests/ui/does-not-have-iter-separated.rs
@@ -0,0 +1,5 @@
+use quote::quote;
+
+fn main() {
+ quote!(#(a b),*);
+}
diff --git a/tests/ui/does-not-have-iter-separated.stderr b/tests/ui/does-not-have-iter-separated.stderr
new file mode 100644
index 0000000..c1fd0ad
--- /dev/null
+++ b/tests/ui/does-not-have-iter-separated.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+ --> $DIR/does-not-have-iter-separated.rs:4:5
+ |
+4 | quote!(#(a b),*);
+ | ^^^^^^^^^^^^^^^^^ expected struct `quote::__rt::HasIterator`, found struct `quote::__rt::ThereIsNoIteratorInRepetition`
+ |
+ = note: expected type `quote::__rt::HasIterator`
+ found type `quote::__rt::ThereIsNoIteratorInRepetition`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/does-not-have-iter.rs b/tests/ui/does-not-have-iter.rs
new file mode 100644
index 0000000..8908353
--- /dev/null
+++ b/tests/ui/does-not-have-iter.rs
@@ -0,0 +1,5 @@
+use quote::quote;
+
+fn main() {
+ quote!(#(a b)*);
+}
diff --git a/tests/ui/does-not-have-iter.stderr b/tests/ui/does-not-have-iter.stderr
new file mode 100644
index 0000000..3b87705
--- /dev/null
+++ b/tests/ui/does-not-have-iter.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+ --> $DIR/does-not-have-iter.rs:4:5
+ |
+4 | quote!(#(a b)*);
+ | ^^^^^^^^^^^^^^^^ expected struct `quote::__rt::HasIterator`, found struct `quote::__rt::ThereIsNoIteratorInRepetition`
+ |
+ = note: expected type `quote::__rt::HasIterator`
+ found type `quote::__rt::ThereIsNoIteratorInRepetition`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/not-quotable.rs b/tests/ui/not-quotable.rs
new file mode 100644
index 0000000..f991c18
--- /dev/null
+++ b/tests/ui/not-quotable.rs
@@ -0,0 +1,7 @@
+use quote::quote;
+use std::net::Ipv4Addr;
+
+fn main() {
+ let ip = Ipv4Addr::LOCALHOST;
+ let _ = quote! { #ip };
+}
diff --git a/tests/ui/not-quotable.stderr b/tests/ui/not-quotable.stderr
new file mode 100644
index 0000000..f51f85f
--- /dev/null
+++ b/tests/ui/not-quotable.stderr
@@ -0,0 +1,10 @@
+error[E0277]: the trait bound `std::net::Ipv4Addr: quote::to_tokens::ToTokens` is not satisfied
+ --> $DIR/not-quotable.rs:6:13
+ |
+6 | let _ = quote! { #ip };
+ | ^^^^^^^^^^^^^^ the trait `quote::to_tokens::ToTokens` is not implemented for `std::net::Ipv4Addr`
+ |
+ = note: required by `quote::to_tokens::ToTokens::to_tokens`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/not-repeatable.rs b/tests/ui/not-repeatable.rs
new file mode 100644
index 0000000..ff18060
--- /dev/null
+++ b/tests/ui/not-repeatable.rs
@@ -0,0 +1,7 @@
+use quote::quote;
+use std::net::Ipv4Addr;
+
+fn main() {
+ let ip = Ipv4Addr::LOCALHOST;
+ let _ = quote! { #(#ip)* };
+}
diff --git a/tests/ui/not-repeatable.stderr b/tests/ui/not-repeatable.stderr
new file mode 100644
index 0000000..ddcac05
--- /dev/null
+++ b/tests/ui/not-repeatable.stderr
@@ -0,0 +1,14 @@
+error[E0599]: no method named `quote_into_iter` found for type `std::net::Ipv4Addr` in the current scope
+ --> $DIR/not-repeatable.rs:6:13
+ |
+6 | let _ = quote! { #(#ip)* };
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: the method `quote_into_iter` exists but the following trait bounds were not satisfied:
+ `&mut std::net::Ipv4Addr : quote::__rt::ext::RepIteratorExt`
+ `&std::net::Ipv4Addr : quote::__rt::ext::RepIteratorExt`
+ `std::net::Ipv4Addr : quote::__rt::ext::RepIteratorExt`
+ `std::net::Ipv4Addr : quote::__rt::ext::RepToTokensExt`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/wrong-type-span.rs b/tests/ui/wrong-type-span.rs
new file mode 100644
index 0000000..1ce391c
--- /dev/null
+++ b/tests/ui/wrong-type-span.rs
@@ -0,0 +1,7 @@
+use quote::quote_spanned;
+
+fn main() {
+ let span = "";
+ let x = 0;
+ quote_spanned!(span=> #x);
+}
diff --git a/tests/ui/wrong-type-span.stderr b/tests/ui/wrong-type-span.stderr
new file mode 100644
index 0000000..a6ae8ef
--- /dev/null
+++ b/tests/ui/wrong-type-span.stderr
@@ -0,0 +1,10 @@
+error[E0308]: mismatched types
+ --> $DIR/wrong-type-span.rs:6:20
+ |
+6 | quote_spanned!(span=> #x);
+ | ^^^^ expected struct `proc_macro2::Span`, found &str
+ |
+ = note: expected type `proc_macro2::Span`
+ found type `&str`
+
+For more information about this error, try `rustc --explain E0308`.