diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7bc6003..498a10c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,10 +24,10 @@ jobs: strategy: fail-fast: false matrix: - rust: [nightly, stable, beta, 1.56.0] + rust: [nightly, stable, beta, 1.76.0, 1.68.0] timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} @@ -35,8 +35,16 @@ jobs: - name: Enable type layout randomization run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV if: matrix.rust == 'nightly' + - run: cargo check - run: cargo test + if: matrix.rust != '1.68.0' - run: cargo run --manifest-path benches/Cargo.toml + - uses: actions/upload-artifact@v6 + if: matrix.rust == 'nightly' && always() + with: + name: Cargo.lock + path: Cargo.lock + continue-on-error: true minimal: name: Minimal versions @@ -45,7 +53,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly - run: cargo generate-lockfile -Z minimal-versions - run: cargo check --locked @@ -59,7 +67,7 @@ jobs: env: RUSTDOCFLAGS: -Dwarnings steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly with: components: rust-src @@ -72,7 +80,7 @@ jobs: if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@nightly with: components: clippy, rust-src @@ -85,7 +93,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@miri - run: cargo miri setup - run: cargo miri test @@ -98,7 +106,7 @@ jobs: if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: dtolnay/install@cargo-outdated - run: cargo outdated --workspace --exit-code 1 diff --git a/.gitignore b/.gitignore index 4fffb2f..e9e2199 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/target +/target/ /Cargo.lock diff --git a/BUILD.gn b/BUILD.gn index d9dba5e..78c560d 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -18,9 +18,13 @@ ohos_cargo_crate("lib") { crate_type = "rlib" crate_root = "src/lib.rs" + rustflags = [ + "--cfg", + "no_diagnostic_namespace" + ] sources = [ "src/lib.rs" ] - edition = "2018" - cargo_pkg_version = "1.0.37" + edition = "2021" + cargo_pkg_version = "1.0.43" cargo_pkg_authors = "David Tolnay " cargo_pkg_name = "quote" cargo_pkg_description = "Quasi-quoting macro quote!(...)" diff --git a/Cargo.toml b/Cargo.toml index 4e2f0e4..0d2a606 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,23 @@ [package] name = "quote" -version = "1.0.37" +version = "1.0.43" authors = ["David Tolnay "] autobenches = false categories = ["development-tools::procedural-macro-helpers"] description = "Quasi-quoting macro quote!(...)" documentation = "https://docs.rs/quote/" -edition = "2018" +edition = "2021" keywords = ["macros", "syn"] license = "MIT OR Apache-2.0" repository = "https://github.com/dtolnay/quote" -rust-version = "1.56" +rust-version = "1.68" [dependencies] proc-macro2 = { version = "1.0.80", default-features = false } [dev-dependencies] rustversion = "1.0" -trybuild = { version = "1.0.66", features = ["diff"] } +trybuild = { version = "1.0.108", features = ["diff"] } [features] default = ["proc-macro"] @@ -25,12 +25,15 @@ default = ["proc-macro"] # libproc_macro in the rustc compiler. proc-macro = ["proc-macro2/proc-macro"] -[lib] -doc-scrape-examples = false - [workspace] members = ["benches"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--generate-link-to-definition"] +rustdoc-args = [ + "--generate-link-to-definition", + "--generate-macro-expansion", + "--extern-html-root-url=core=https://doc.rust-lang.org", + "--extern-html-root-url=alloc=https://doc.rust-lang.org", + "--extern-html-root-url=std=https://doc.rust-lang.org", +] diff --git a/README.OpenSource b/README.OpenSource index 5fdb04e..a82a418 100644 --- a/README.OpenSource +++ b/README.OpenSource @@ -3,9 +3,12 @@ "Name": "Rust Quasi-Quoting", "License": "Apache License V2.0, MIT", "License File": "LICENSE-APACHE, LICENSE-MIT", - "Version Number": "1.0.37", + "Version Number": "1.0.43", "Owner": "fangting12@huawei.com", "Upstream URL": "https://github.com/dtolnay/quote", - "Description": "A Rust library that provides support for generating Rust code." + "Description": "A Rust library that provides support for generating Rust code.", + "Dependencies": [ + "proc-macro2" + ] } ] \ No newline at end of file diff --git a/README.md b/README.md index 58bbf21..c4316be 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ macros. quote = "1.0" ``` -*Version requirement: Quote supports rustc 1.56 and up.*
+*Version requirement: Quote supports rustc 1.68 and up.*
[*Release notes*](https://github.com/dtolnay/quote/releases)
diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 366f9df..7044225 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -2,7 +2,7 @@ name = "quote-benchmark" version = "0.0.0" authors = ["David Tolnay "] -edition = "2018" +edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..50f98cb --- /dev/null +++ b/build.rs @@ -0,0 +1,32 @@ +use std::env; +use std::process::Command; +use std::str; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let Some(minor) = rustc_minor_version() else { + return; + }; + + if minor >= 77 { + println!("cargo:rustc-check-cfg=cfg(no_diagnostic_namespace)"); + } + + // Support for the `#[diagnostic]` tool attribute namespace + // https://blog.rust-lang.org/2024/05/02/Rust-1.78.0.html#diagnostic-attributes + if minor < 78 { + println!("cargo:rustc-cfg=no_diagnostic_namespace"); + } +} + +fn rustc_minor_version() -> Option { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + pieces.next()?.parse().ok() +} diff --git a/src/ext.rs b/src/ext.rs index 92c2315..2553717 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -69,8 +69,16 @@ impl TokenStreamExt for TokenStream { I: IntoIterator, I::Item: ToTokens, { - for token in iter { - token.to_tokens(self); + do_append_all(self, iter.into_iter()); + + fn do_append_all(stream: &mut TokenStream, iter: I) + where + I: Iterator, + I::Item: ToTokens, + { + for token in iter { + token.to_tokens(stream); + } } } @@ -80,11 +88,22 @@ impl TokenStreamExt for TokenStream { I::Item: ToTokens, U: ToTokens, { - for (i, token) in iter.into_iter().enumerate() { - if i > 0 { - op.to_tokens(self); + do_append_separated(self, iter.into_iter(), op); + + fn do_append_separated(stream: &mut TokenStream, iter: I, op: U) + where + I: Iterator, + I::Item: ToTokens, + U: ToTokens, + { + let mut first = true; + for token in iter { + if !first { + op.to_tokens(stream); + } + first = false; + token.to_tokens(stream); } - token.to_tokens(self); } } @@ -94,9 +113,18 @@ impl TokenStreamExt for TokenStream { I::Item: ToTokens, U: ToTokens, { - for token in iter { - token.to_tokens(self); - term.to_tokens(self); + do_append_terminated(self, iter.into_iter(), term); + + fn do_append_terminated(stream: &mut TokenStream, iter: I, term: U) + where + I: Iterator, + I::Item: ToTokens, + U: ToTokens, + { + for token in iter { + token.to_tokens(stream); + term.to_tokens(stream); + } } } } diff --git a/src/format.rs b/src/format.rs index 3cddbd2..09688e9 100644 --- a/src/format.rs +++ b/src/format.rs @@ -4,19 +4,19 @@ /// /// # Syntax /// -/// Syntax is copied from the [`format!`] macro, supporting both positional and -/// named arguments. +/// Syntax is copied from the [`format_args!`] 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) +/// * `{:o}` ⇒ [`Octal`](core::fmt::Octal) +/// * `{:x}` ⇒ [`LowerHex`](core::fmt::LowerHex) +/// * `{:X}` ⇒ [`UpperHex`](core::fmt::UpperHex) +/// * `{:b}` ⇒ [`Binary`](core::fmt::Binary) /// -/// See [`std::fmt`] for more information. +/// See [`core::fmt`] for more information. /// ///
/// @@ -26,7 +26,7 @@ /// 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. +/// unsigned integers and strings. /// * [`Ident`] arguments will have their `r#` prefixes stripped, if present. /// /// [`IdentFragment`]: crate::IdentFragment diff --git a/src/ident_fragment.rs b/src/ident_fragment.rs index 6c2a9a8..646c9c7 100644 --- a/src/ident_fragment.rs +++ b/src/ident_fragment.rs @@ -1,4 +1,5 @@ -use alloc::borrow::Cow; +use alloc::borrow::{Cow, ToOwned}; +use alloc::string::{String, ToString}; use core::fmt; use proc_macro2::{Ident, Span}; diff --git a/src/lib.rs b/src/lib.rs index 2c72da2..12c9e4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,6 @@ //! This crate provides the [`quote!`] macro for turning Rust syntax tree data //! structures into tokens of source code. //! -//! [`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 @@ -46,7 +44,6 @@ //! implementing hygienic procedural macros. //! //! [a]: https://serde.rs/ -//! [`quote_spanned!`]: macro.quote_spanned.html //! //! ``` //! # use quote::quote; @@ -91,18 +88,22 @@ //! //! [prettyplease]: https://github.com/dtolnay/prettyplease -// Quote types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/quote/1.0.37")] +#![no_std] +#![doc(html_root_url = "https://docs.rs/quote/1.0.43")] #![allow( clippy::doc_markdown, + clippy::elidable_lifetime_names, + clippy::items_after_statements, clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::module_name_repetitions, + clippy::needless_lifetimes, // false positive https://github.com/rust-lang/rust-clippy/issues/6983 clippy::wrong_self_convention, )] extern crate alloc; +extern crate std; #[cfg(feature = "proc-macro")] extern crate proc_macro; @@ -135,8 +136,6 @@ macro_rules! __quote { /// 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 - /// ///
/// /// # Interpolation @@ -148,7 +147,6 @@ macro_rules! __quote { /// Rust primitive types as well as most of the syntax tree types from the [Syn] /// crate. /// - /// [`ToTokens`]: trait.ToTokens.html /// [Syn]: https://github.com/dtolnay/syn /// /// Repetition is done using `#(...)*` or `#(...),*` again similar to @@ -170,12 +168,10 @@ macro_rules! __quote { /// `ToTokens` implementation. Tokens that originate within the `quote!` /// invocation are spanned with [`Span::call_site()`]. /// - /// [`Span::call_site()`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html#method.call_site + /// [`Span::call_site()`]: proc_macro2::Span::call_site /// /// A different span can be provided through the [`quote_spanned!`] macro. /// - /// [`quote_spanned!`]: macro.quote_spanned.html - /// ///
/// /// # Return type @@ -196,8 +192,6 @@ macro_rules! __quote { /// `quote!` from a procedural macro usually looks like `tokens.into()` or /// `proc_macro::TokenStream::from(tokens)`. /// - /// [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html - /// ///
/// /// # Examples @@ -280,7 +274,7 @@ macro_rules! __quote { /// 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; /// # @@ -545,7 +539,7 @@ macro_rules! __quote_spanned { /// anything more than a few characters. There should be no space before the /// `=>` token. /// - /// [`Span`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html + /// [`Span`]: proc_macro2::Span /// /// ``` /// # use proc_macro2::Span; @@ -588,8 +582,6 @@ macro_rules! __quote_spanned { /// particular Rust type implements the [`Sync`] trait so that references can be /// safely shared between threads. /// - /// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html - /// /// ``` /// # use quote::{quote_spanned, TokenStreamExt, ToTokens}; /// # use proc_macro2::{Span, TokenStream}; @@ -898,9 +890,9 @@ macro_rules! quote_token_with_context { // A repetition with no separator. ($tokens:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) * $a3:tt) => {{ use $crate::__private::ext::*; - let has_iter = $crate::__private::ThereIsNoIteratorInRepetition; + let has_iter = $crate::__private::HasIterator::; $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($inner)*} - let _: $crate::__private::HasIterator = has_iter; + <_ as $crate::__private::CheckHasIterator>::check(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 @@ -920,16 +912,16 @@ macro_rules! quote_token_with_context { // A repetition with separator. ($tokens:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) $sep:tt *) => {{ use $crate::__private::ext::*; - let mut _i = 0usize; - let has_iter = $crate::__private::ThereIsNoIteratorInRepetition; + let mut _first = true; + let has_iter = $crate::__private::HasIterator::; $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($inner)*} - let _: $crate::__private::HasIterator = has_iter; + <_ as $crate::__private::CheckHasIterator>::check(has_iter); while true { $crate::pounded_var_names!{quote_bind_next_or_break!() () $($inner)*} - if _i > 0 { + if !_first { $crate::quote_token!{$sep $tokens} } - _i += 1; + _first = false; $crate::quote_each_token!{$tokens $($inner)*} } }}; @@ -968,9 +960,9 @@ macro_rules! quote_token_with_context_spanned { ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) * $a3:tt) => {{ use $crate::__private::ext::*; - let has_iter = $crate::__private::ThereIsNoIteratorInRepetition; + let has_iter = $crate::__private::HasIterator::; $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($inner)*} - let _: $crate::__private::HasIterator = has_iter; + <_ as $crate::__private::CheckHasIterator>::check(has_iter); while true { $crate::pounded_var_names!{quote_bind_next_or_break!() () $($inner)*} $crate::quote_each_token_spanned!{$tokens $span $($inner)*} @@ -981,16 +973,16 @@ macro_rules! quote_token_with_context_spanned { ($tokens:ident $span:ident $b3:tt $b2:tt $b1:tt (#) ( $($inner:tt)* ) $sep:tt *) => {{ use $crate::__private::ext::*; - let mut _i = 0usize; - let has_iter = $crate::__private::ThereIsNoIteratorInRepetition; + let mut _first = true; + let has_iter = $crate::__private::HasIterator::; $crate::pounded_var_names!{quote_bind_into_iter!(has_iter) () $($inner)*} - let _: $crate::__private::HasIterator = has_iter; + <_ as $crate::__private::CheckHasIterator>::check(has_iter); while true { $crate::pounded_var_names!{quote_bind_next_or_break!() () $($inner)*} - if _i > 0 { + if !_first { $crate::quote_token_spanned!{$sep $tokens $span} } - _i += 1; + _first = false; $crate::quote_each_token_spanned!{$tokens $span $($inner)*} } }}; @@ -1023,7 +1015,10 @@ macro_rules! quote_token_with_context_spanned { #[doc(hidden)] macro_rules! quote_token { ($ident:ident $tokens:ident) => { - $crate::__private::push_ident(&mut $tokens, stringify!($ident)); + $crate::__private::push_ident( + &mut $tokens, + $crate::__private::stringify!($ident), + ); }; (:: $tokens:ident) => { @@ -1227,7 +1222,10 @@ macro_rules! quote_token { }; ($lifetime:lifetime $tokens:ident) => { - $crate::__private::push_lifetime(&mut $tokens, stringify!($lifetime)); + $crate::__private::push_lifetime( + &mut $tokens, + $crate::__private::stringify!($lifetime), + ); }; (_ $tokens:ident) => { @@ -1235,7 +1233,10 @@ macro_rules! quote_token { }; ($other:tt $tokens:ident) => { - $crate::__private::parse(&mut $tokens, stringify!($other)); + $crate::__private::parse( + &mut $tokens, + $crate::__private::stringify!($other), + ); }; } @@ -1244,7 +1245,11 @@ macro_rules! quote_token { #[doc(hidden)] macro_rules! quote_token_spanned { ($ident:ident $tokens:ident $span:ident) => { - $crate::__private::push_ident_spanned(&mut $tokens, $span, stringify!($ident)); + $crate::__private::push_ident_spanned( + &mut $tokens, + $span, + $crate::__private::stringify!($ident), + ); }; (:: $tokens:ident $span:ident) => { @@ -1451,7 +1456,11 @@ macro_rules! quote_token_spanned { }; ($lifetime:lifetime $tokens:ident $span:ident) => { - $crate::__private::push_lifetime_spanned(&mut $tokens, $span, stringify!($lifetime)); + $crate::__private::push_lifetime_spanned( + &mut $tokens, + $span, + $crate::__private::stringify!($lifetime), + ); }; (_ $tokens:ident $span:ident) => { @@ -1459,6 +1468,10 @@ macro_rules! quote_token_spanned { }; ($other:tt $tokens:ident $span:ident) => { - $crate::__private::parse_spanned(&mut $tokens, $span, stringify!($other)); + $crate::__private::parse_spanned( + &mut $tokens, + $span, + $crate::__private::stringify!($other), + ); }; } diff --git a/src/runtime.rs b/src/runtime.rs index eff044a..a27ae82 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -9,6 +9,8 @@ use proc_macro2::{Group, Ident, Punct, Spacing, TokenTree}; pub use alloc::format; #[doc(hidden)] pub use core::option::Option; +#[doc(hidden)] +pub use core::stringify; #[doc(hidden)] pub type Delimiter = proc_macro2::Delimiter; @@ -18,37 +20,49 @@ pub type Span = proc_macro2::Span; pub type TokenStream = proc_macro2::TokenStream; #[doc(hidden)] -pub struct HasIterator; // True +pub struct HasIterator; + +impl BitOr> for HasIterator { + type Output = HasIterator; + fn bitor(self, _rhs: HasIterator) -> HasIterator { + HasIterator:: + } +} + +impl BitOr> for HasIterator { + type Output = HasIterator; + fn bitor(self, _rhs: HasIterator) -> HasIterator { + HasIterator:: + } +} + +impl BitOr> for HasIterator { + type Output = HasIterator; + fn bitor(self, _rhs: HasIterator) -> HasIterator { + HasIterator:: + } +} + +impl BitOr> for HasIterator { + type Output = HasIterator; + fn bitor(self, _rhs: HasIterator) -> HasIterator { + HasIterator:: + } +} + #[doc(hidden)] -pub struct ThereIsNoIteratorInRepetition; // False - -impl BitOr for ThereIsNoIteratorInRepetition { - type Output = ThereIsNoIteratorInRepetition; - fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> ThereIsNoIteratorInRepetition { - ThereIsNoIteratorInRepetition - } +#[cfg_attr( + not(no_diagnostic_namespace), + diagnostic::on_unimplemented( + message = "repetition contains no interpolated value that is an iterator", + label = "none of the values interpolated inside this repetition are iterable" + ) +)] +pub trait CheckHasIterator: Sized { + fn check(self) {} } -impl BitOr for HasIterator { - type Output = HasIterator; - fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> HasIterator { - HasIterator - } -} - -impl BitOr for ThereIsNoIteratorInRepetition { - type Output = HasIterator; - fn bitor(self, _rhs: HasIterator) -> HasIterator { - HasIterator - } -} - -impl BitOr for HasIterator { - type Output = HasIterator; - fn bitor(self, _rhs: HasIterator) -> HasIterator { - HasIterator - } -} +impl CheckHasIterator for 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. @@ -58,17 +72,17 @@ impl BitOr for HasIterator { /// the returned value should be idempotent. #[doc(hidden)] pub mod ext { - use super::RepInterp; - use super::{HasIterator as HasIter, ThereIsNoIteratorInRepetition as DoesNotHaveIter}; + use super::{HasIterator, RepInterp}; use crate::ToTokens; use alloc::collections::btree_set::{self, BTreeSet}; + use alloc::vec::Vec; use core::slice; /// Extension trait providing the `quote_into_iter` method on iterators. #[doc(hidden)] pub trait RepIteratorExt: Iterator + Sized { - fn quote_into_iter(self) -> (Self, HasIter) { - (self, HasIter) + fn quote_into_iter(self) -> (Self, HasIterator) { + (self, HasIterator::) } } @@ -81,13 +95,13 @@ pub mod ext { 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. + /// correctly returning HasIterator. fn next(&self) -> Option<&Self> { Some(self) } - fn quote_into_iter(&self) -> (&Self, DoesNotHaveIter) { - (self, DoesNotHaveIter) + fn quote_into_iter(&self) -> (&Self, HasIterator) { + (self, HasIterator::) } } @@ -99,21 +113,21 @@ pub mod ext { pub trait RepAsIteratorExt<'q> { type Iter: Iterator; - fn quote_into_iter(&'q self) -> (Self::Iter, HasIter); + fn quote_into_iter(&'q self) -> (Self::Iter, HasIterator); } - impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a T { + impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &T { type Iter = T::Iter; - fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + fn quote_into_iter(&'q self) -> (Self::Iter, HasIterator) { ::quote_into_iter(*self) } } - impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a mut T { + impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &mut T { type Iter = T::Iter; - fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + fn quote_into_iter(&'q self) -> (Self::Iter, HasIterator) { ::quote_into_iter(*self) } } @@ -121,31 +135,39 @@ pub mod ext { 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) + fn quote_into_iter(&'q self) -> (Self::Iter, HasIterator) { + (self.iter(), HasIterator::) + } + } + + impl<'q, T: 'q, const N: usize> RepAsIteratorExt<'q> for [T; N] { + type Iter = slice::Iter<'q, T>; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIterator) { + (self.iter(), HasIterator::) } } impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec { type Iter = slice::Iter<'q, T>; - fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { - (self.iter(), HasIter) + fn quote_into_iter(&'q self) -> (Self::Iter, HasIterator) { + (self.iter(), HasIterator::) } } impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet { type Iter = btree_set::Iter<'q, T>; - fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { - (self.iter(), HasIter) + fn quote_into_iter(&'q self) -> (Self::Iter, HasIterator) { + (self.iter(), HasIterator::) } } impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp { type Iter = T::Iter; - fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + fn quote_into_iter(&'q self) -> (Self::Iter, HasIterator) { self.0.quote_into_iter() } } @@ -264,19 +286,20 @@ pub fn parse(tokens: &mut TokenStream, s: &str) { #[doc(hidden)] pub fn parse_spanned(tokens: &mut TokenStream, span: Span, s: &str) { let s: TokenStream = s.parse().expect("invalid token stream"); - tokens.extend(s.into_iter().map(|t| respan_token_tree(t, span))); + for token in s { + tokens.append(respan_token_tree(token, span)); + } } // Token tree with every span replaced by the given one. fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree { match &mut token { TokenTree::Group(g) => { - let stream = g - .stream() - .into_iter() - .map(|token| respan_token_tree(token, span)) - .collect(); - *g = Group::new(g.delimiter(), stream); + let mut tokens = TokenStream::new(); + for token in g.stream() { + tokens.append(respan_token_tree(token, span)); + } + *g = Group::new(g.delimiter(), tokens); g.set_span(span); } other => other.set_span(span), @@ -297,68 +320,21 @@ pub fn push_ident_spanned(tokens: &mut TokenStream, span: Span, s: &str) { #[doc(hidden)] pub fn push_lifetime(tokens: &mut TokenStream, lifetime: &str) { - struct Lifetime<'a> { - name: &'a str, - state: u8, - } - - impl<'a> Iterator for Lifetime<'a> { - type Item = TokenTree; - - fn next(&mut self) -> Option { - match self.state { - 0 => { - self.state = 1; - Some(TokenTree::Punct(Punct::new('\'', Spacing::Joint))) - } - 1 => { - self.state = 2; - Some(TokenTree::Ident(Ident::new(self.name, Span::call_site()))) - } - _ => None, - } - } - } - - tokens.extend(Lifetime { - name: &lifetime[1..], - state: 0, - }); + tokens.append(TokenTree::Punct(Punct::new('\'', Spacing::Joint))); + tokens.append(TokenTree::Ident(Ident::new( + &lifetime[1..], + Span::call_site(), + ))); } #[doc(hidden)] pub fn push_lifetime_spanned(tokens: &mut TokenStream, span: Span, lifetime: &str) { - struct Lifetime<'a> { - name: &'a str, - span: Span, - state: u8, - } - - impl<'a> Iterator for Lifetime<'a> { - type Item = TokenTree; - - fn next(&mut self) -> Option { - match self.state { - 0 => { - self.state = 1; - let mut apostrophe = Punct::new('\'', Spacing::Joint); - apostrophe.set_span(self.span); - Some(TokenTree::Punct(apostrophe)) - } - 1 => { - self.state = 2; - Some(TokenTree::Ident(Ident::new(self.name, self.span))) - } - _ => None, - } - } - } - - tokens.extend(Lifetime { - name: &lifetime[1..], - span, - state: 0, - }); + tokens.append(TokenTree::Punct({ + let mut apostrophe = Punct::new('\'', Spacing::Joint); + apostrophe.set_span(span); + apostrophe + })); + tokens.append(TokenTree::Ident(Ident::new(&lifetime[1..], span))); } macro_rules! push_punct { diff --git a/src/spanned.rs b/src/spanned.rs index 6eba644..6afc6b3 100644 --- a/src/spanned.rs +++ b/src/spanned.rs @@ -28,9 +28,8 @@ impl Spanned for T { fn join_spans(tokens: TokenStream) -> Span { let mut iter = tokens.into_iter().map(|tt| tt.span()); - let first = match iter.next() { - Some(span) => span, - None => return Span::call_site(), + let Some(first) = iter.next() else { + return Span::call_site(); }; iter.fold(None, |_prev, next| Some(next)) diff --git a/src/to_tokens.rs b/src/to_tokens.rs index 2bcb961..aec6d2b 100644 --- a/src/to_tokens.rs +++ b/src/to_tokens.rs @@ -1,21 +1,20 @@ use super::TokenStreamExt; -use alloc::borrow::Cow; +use alloc::borrow::{Cow, ToOwned}; +use alloc::boxed::Box; +use alloc::ffi::CString; use alloc::rc::Rc; +use alloc::string::String; +use core::ffi::CStr; use core::iter; use proc_macro2::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree}; -use std::ffi::{CStr, CString}; /// Types that can be interpolated inside a `quote!` invocation. -/// -/// [`quote!`]: macro.quote.html pub trait ToTokens { /// Write `self` to the given `TokenStream`. /// /// The token append methods provided by the [`TokenStreamExt`] extension /// trait may be useful for implementing `ToTokens`. /// - /// [`TokenStreamExt`]: trait.TokenStreamExt.html - /// /// # Example /// /// Example implementation for a struct representing Rust paths like @@ -75,13 +74,13 @@ pub trait ToTokens { } } -impl<'a, T: ?Sized + ToTokens> ToTokens for &'a T { +impl ToTokens for &T { fn to_tokens(&self, tokens: &mut TokenStream) { (**self).to_tokens(tokens); } } -impl<'a, T: ?Sized + ToTokens> ToTokens for &'a mut T { +impl ToTokens for &mut T { fn to_tokens(&self, tokens: &mut TokenStream) { (**self).to_tokens(tokens); } diff --git a/tests/test.rs b/tests/test.rs index 6ff1402..3320b0c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -105,6 +105,9 @@ fn test_array() { let ref_slice: &[X] = &[X, X]; let _ = quote!(#(#ref_slice #ref_slice)*); + + let array_of_array: [[u8; 2]; 2] = [[0; 2]; 2]; + let _ = quote!(#(#(#array_of_array)*)*); } #[test] @@ -197,7 +200,7 @@ fn test_floating() { #e32 #e64 }; - let expected = concat!("2.345f32 2.345f64"); + let expected = "2.345f32 2.345f64"; assert_eq!(expected, tokens.to_string()); } @@ -558,6 +561,7 @@ fn test_type_inference_for_span() { let inferred = CallSite::get(); let _ = quote_spanned!(inferred=> ...); + #[cfg(feature = "proc-macro")] if false { let proc_macro_span = proc_macro::Span::call_site(); let _ = quote_spanned!(proc_macro_span.into()=> ...); diff --git a/tests/ui/does-not-have-iter-interpolated-dup.stderr b/tests/ui/does-not-have-iter-interpolated-dup.stderr index 99c20a5..96af816 100644 --- a/tests/ui/does-not-have-iter-interpolated-dup.stderr +++ b/tests/ui/does-not-have-iter-interpolated-dup.stderr @@ -1,11 +1,13 @@ -error[E0308]: mismatched types +error[E0277]: repetition contains no interpolated value that is an iterator --> tests/ui/does-not-have-iter-interpolated-dup.rs:8:5 | 8 | quote!(#(#nonrep #nonrep)*); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` - | expected due to this - | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ none of the values interpolated inside this repetition are iterable | +help: the trait `CheckHasIterator` is not implemented for `HasIterator` + but it is implemented for `HasIterator` + --> src/runtime.rs + | + | impl CheckHasIterator for HasIterator {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::quote_token_with_context` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/does-not-have-iter-interpolated.stderr b/tests/ui/does-not-have-iter-interpolated.stderr index ef90813..0c0572c 100644 --- a/tests/ui/does-not-have-iter-interpolated.stderr +++ b/tests/ui/does-not-have-iter-interpolated.stderr @@ -1,11 +1,13 @@ -error[E0308]: mismatched types +error[E0277]: repetition contains no interpolated value that is an iterator --> tests/ui/does-not-have-iter-interpolated.rs:8:5 | 8 | quote!(#(#nonrep)*); - | ^^^^^^^^^^^^^^^^^^^ - | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` - | expected due to this - | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` + | ^^^^^^^^^^^^^^^^^^^ none of the values interpolated inside this repetition are iterable | +help: the trait `CheckHasIterator` is not implemented for `HasIterator` + but it is implemented for `HasIterator` + --> src/runtime.rs + | + | impl CheckHasIterator for HasIterator {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::quote_token_with_context` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/does-not-have-iter-separated.stderr b/tests/ui/does-not-have-iter-separated.stderr index 7c6e30f..e899fb4 100644 --- a/tests/ui/does-not-have-iter-separated.stderr +++ b/tests/ui/does-not-have-iter-separated.stderr @@ -1,10 +1,13 @@ -error[E0308]: mismatched types +error[E0277]: repetition contains no interpolated value that is an iterator --> tests/ui/does-not-have-iter-separated.rs:4:5 | 4 | quote!(#(a b),*); - | ^^^^^^^^^^^^^^^^ - | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` - | expected due to this + | ^^^^^^^^^^^^^^^^ none of the values interpolated inside this repetition are iterable | +help: the trait `CheckHasIterator` is not implemented for `HasIterator` + but it is implemented for `HasIterator` + --> src/runtime.rs + | + | impl CheckHasIterator for HasIterator {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::quote_token_with_context` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/does-not-have-iter.stderr b/tests/ui/does-not-have-iter.stderr index 0b13e5c..348071c 100644 --- a/tests/ui/does-not-have-iter.stderr +++ b/tests/ui/does-not-have-iter.stderr @@ -1,10 +1,13 @@ -error[E0308]: mismatched types +error[E0277]: repetition contains no interpolated value that is an iterator --> tests/ui/does-not-have-iter.rs:4:5 | 4 | quote!(#(a b)*); - | ^^^^^^^^^^^^^^^ - | | - | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` - | expected due to this + | ^^^^^^^^^^^^^^^ none of the values interpolated inside this repetition are iterable | +help: the trait `CheckHasIterator` is not implemented for `HasIterator` + but it is implemented for `HasIterator` + --> src/runtime.rs + | + | impl CheckHasIterator for HasIterator {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `$crate::quote_token_with_context` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/not-quotable.stderr b/tests/ui/not-quotable.stderr index 35cb6f2..1549246 100644 --- a/tests/ui/not-quotable.stderr +++ b/tests/ui/not-quotable.stderr @@ -8,13 +8,13 @@ error[E0277]: the trait bound `Ipv4Addr: ToTokens` is not satisfied | required by a bound introduced by this call | = help: the following other types implement trait `ToTokens`: - bool - char - isize - i8 - i16 - i32 - i64 - i128 + &T + &mut T + Box + CStr + CString + Cow<'a, T> + Option + Rc and $N others = note: this error originates in the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/not-repeatable.stderr b/tests/ui/not-repeatable.stderr index 2ed1da0..d5e13b0 100644 --- a/tests/ui/not-repeatable.stderr +++ b/tests/ui/not-repeatable.stderr @@ -2,13 +2,7 @@ error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its --> tests/ui/not-repeatable.rs:7:13 | 3 | struct Ipv4Addr; - | --------------- - | | - | method `quote_into_iter` not found for this struct - | doesn't satisfy `Ipv4Addr: Iterator` - | doesn't satisfy `Ipv4Addr: ToTokens` - | doesn't satisfy `Ipv4Addr: ext::RepIteratorExt` - | doesn't satisfy `Ipv4Addr: ext::RepToTokensExt` + | --------------- method `quote_into_iter` not found for this struct because it doesn't satisfy `Ipv4Addr: Iterator`, `Ipv4Addr: ToTokens`, `Ipv4Addr: ext::RepIteratorExt` or `Ipv4Addr: ext::RepToTokensExt` ... 7 | let _ = quote! { #(#ip)* }; | ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds @@ -22,14 +16,27 @@ error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its which is required by `Ipv4Addr: ext::RepToTokensExt` `&mut Ipv4Addr: Iterator` which is required by `&mut Ipv4Addr: ext::RepIteratorExt` -note: the traits `ToTokens` and `Iterator` must be implemented - --> src/to_tokens.rs - | - | pub trait ToTokens { - | ^^^^^^^^^^^^^^^^^^ - | - ::: $RUST/core/src/iter/traits/iterator.rs +note: the traits `Iterator` and `ToTokens` must be implemented + --> $RUST/core/src/iter/traits/iterator.rs | | pub trait Iterator { | ^^^^^^^^^^^^^^^^^^ + | + ::: src/to_tokens.rs + | + | pub trait ToTokens { + | ^^^^^^^^^^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `quote_into_iter`, perhaps you need to implement one of them: + candidate #1: `ext::RepAsIteratorExt` + candidate #2: `ext::RepIteratorExt` + candidate #3: `ext::RepToTokensExt` = note: this error originates in the macro `$crate::quote_bind_into_iter` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0282]: type annotations needed + --> tests/ui/not-repeatable.rs:7:13 + | +7 | let _ = quote! { #(#ip)* }; + | ^^^^^^^^^^^^^^^^^^ cannot infer type + | + = note: this error originates in the macro `$crate::quote_bind_next_or_break` which comes from the expansion of the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info)