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 &mdash; 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 &mdash; 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 &mdash; 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`.