diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 79e9b8430b8d5..c1ce988f7a1fe 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -44,6 +44,7 @@ mod diagnostic; mod escape; mod to_tokens; +use core::ops::BitOr; use std::ffi::CStr; use std::ops::{Range, RangeBounds}; use std::path::PathBuf; @@ -1613,3 +1614,202 @@ pub mod tracked_path { crate::bridge::client::FreeFunctions::track_path(path); } } + +#[doc(hidden)] +#[unstable(feature = "proc_macro_quote", issue = "54722")] +#[derive(Debug)] +pub struct HasIterator; // True +#[doc(hidden)] +#[unstable(feature = "proc_macro_quote", issue = "54722")] +#[derive(Debug)] +pub struct ThereIsNoIteratorInRepetition; // False + +#[unstable(feature = "proc_macro_quote", issue = "54722")] +impl BitOr for ThereIsNoIteratorInRepetition { + type Output = ThereIsNoIteratorInRepetition; + fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> ThereIsNoIteratorInRepetition { + ThereIsNoIteratorInRepetition + } +} + +#[unstable(feature = "proc_macro_quote", issue = "54722")] +impl BitOr for HasIterator { + type Output = HasIterator; + fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> HasIterator { + HasIterator + } +} + +#[unstable(feature = "proc_macro_quote", issue = "54722")] +impl BitOr for ThereIsNoIteratorInRepetition { + type Output = HasIterator; + fn bitor(self, _rhs: HasIterator) -> HasIterator { + HasIterator + } +} + +#[unstable(feature = "proc_macro_quote", issue = "54722")] +impl BitOr 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. +#[doc(hidden)] +#[unstable(feature = "proc_macro_quote", issue = "54722")] +pub mod ext { + use core::slice; + use std::collections::btree_set::{self, BTreeSet}; + + use super::{ + HasIterator as HasIter, RepInterp, ThereIsNoIteratorInRepetition as DoesNotHaveIter, + }; + use crate::ToTokens; + + /// Extension trait providing the `quote_into_iter` method on iterators. + #[unstable(feature = "proc_macro_quote", issue = "54722")] + pub trait RepIteratorExt: Iterator + Sized { + fn quote_into_iter(self) -> (Self, HasIter) { + (self, HasIter) + } + } + + impl 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. + #[unstable(feature = "proc_macro_quote", issue = "54722")] + 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. + #[unstable(feature = "proc_macro_quote", issue = "54722")] + fn next(&self) -> Option<&Self> { + Some(self) + } + + #[unstable(feature = "proc_macro_quote", issue = "54722")] + fn quote_into_iter(&self) -> (&Self, DoesNotHaveIter) { + (self, DoesNotHaveIter) + } + } + + #[unstable(feature = "proc_macro_quote", issue = "54722")] + impl RepToTokensExt for T {} + + /// Extension trait providing the `quote_into_iter` method for types that + /// can be referenced as an iterator. + #[unstable(feature = "proc_macro_quote", issue = "54722")] + pub trait RepAsIteratorExt<'q> { + #[unstable(feature = "proc_macro_quote", issue = "54722")] + type Iter: Iterator; + + #[unstable(feature = "proc_macro_quote", issue = "54722")] + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter); + } + + #[unstable(feature = "proc_macro_quote", issue = "54722")] + impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &T { + type Iter = T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + ::quote_into_iter(*self) + } + } + + #[unstable(feature = "proc_macro_quote", issue = "54722")] + impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &mut T { + type Iter = T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + ::quote_into_iter(*self) + } + } + + #[unstable(feature = "proc_macro_quote", issue = "54722")] + 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) + } + } + + #[unstable(feature = "proc_macro_quote", issue = "54722")] + 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, HasIter) { + (self.iter(), HasIter) + } + } + + 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) + } + } + + #[unstable(feature = "proc_macro_quote", issue = "54722")] + 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) + } + } + + #[unstable(feature = "proc_macro_quote", issue = "54722")] + impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp { + 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)] +#[doc(hidden)] +#[unstable(feature = "proc_macro_quote", issue = "54722")] +pub struct RepInterp(pub T); + +#[unstable(feature = "proc_macro_quote", issue = "54722")] +impl RepInterp { + // 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. + #[unstable(feature = "proc_macro_quote", issue = "54722")] + pub fn next(self) -> Option { + Some(self.0) + } +} + +#[unstable(feature = "proc_macro_quote", issue = "54722")] +impl Iterator for RepInterp { + type Item = T::Item; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +#[unstable(feature = "proc_macro_quote", issue = "54722")] +impl ToTokens for RepInterp { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.0.to_tokens(tokens); + } +} diff --git a/library/proc_macro/src/quote.rs b/library/proc_macro/src/quote.rs index bcb15912bb65e..d242cf9b9cc69 100644 --- a/library/proc_macro/src/quote.rs +++ b/library/proc_macro/src/quote.rs @@ -20,7 +20,13 @@ macro_rules! minimal_quote_tt { (>) => { Punct::new('>', Spacing::Alone) }; (&) => { Punct::new('&', Spacing::Alone) }; (=) => { Punct::new('=', Spacing::Alone) }; + (#) => { Punct::new('#', Spacing::Alone) }; + (|) => { Punct::new('|', Spacing::Alone) }; + (:) => { Punct::new(':', Spacing::Alone) }; + (*) => { Punct::new('*', Spacing::Alone) }; + (_) => { Ident::new("_", Span::def_site()) }; ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; + ($lit:literal) => { stringify!($lit).parse::().unwrap() }; } macro_rules! minimal_quote_ts { @@ -36,6 +42,39 @@ macro_rules! minimal_quote_ts { [c.0, c.1].into_iter().collect::() } }; + (=>) => { + { + let mut c = ( + TokenTree::from(Punct::new('=', Spacing::Joint)), + TokenTree::from(Punct::new('>', Spacing::Alone)) + ); + c.0.set_span(Span::def_site()); + c.1.set_span(Span::def_site()); + [c.0, c.1].into_iter().collect::() + } + }; + (+=) => { + { + let mut c = ( + TokenTree::from(Punct::new('+', Spacing::Joint)), + TokenTree::from(Punct::new('=', Spacing::Alone)) + ); + c.0.set_span(Span::def_site()); + c.1.set_span(Span::def_site()); + [c.0, c.1].into_iter().collect::() + } + }; + (!=) => { + { + let mut c = ( + TokenTree::from(Punct::new('!', Spacing::Joint)), + TokenTree::from(Punct::new('=', Spacing::Alone)) + ); + c.0.set_span(Span::def_site()); + c.1.set_span(Span::def_site()); + [c.0, c.1].into_iter().collect::() + } + }; ($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) }; } @@ -71,10 +110,92 @@ pub fn quote(stream: TokenStream) -> TokenStream { let mut after_dollar = false; let mut tokens = crate::TokenStream::new(); - for tree in stream { + let mut iter = stream.into_iter().peekable(); + while let Some(tree) = iter.next() { if after_dollar { after_dollar = false; match tree { + TokenTree::Group(inner) => { + let content = inner.stream(); + + let sep_opt: Option = match (iter.next(), iter.peek()) { + (Some(TokenTree::Punct(sep)), Some(TokenTree::Punct(star))) + if sep.spacing() == Spacing::Joint && star.as_char() == '*' => + { + iter.next(); + Some(sep) + } + (Some(TokenTree::Punct(sep)), _) if sep.as_char() == '*' => None, + _ => panic!("`$(...)` must be followed by `*` in `quote!`"), + }; + + let meta_vars = collect_meta_vars(content.clone()); + + let mut content_tokens = TokenStream::new(); + minimal_quote!( + use crate::ext::*; + ) + .to_tokens(&mut content_tokens); + if sep_opt.is_some() { + minimal_quote!( + let mut _i = 0usize; + ) + .to_tokens(&mut content_tokens); + } + minimal_quote!( + let has_iter = crate::ThereIsNoIteratorInRepetition; + ) + .to_tokens(&mut content_tokens); + for meta_var in &meta_vars { + minimal_quote!( + #[allow(unused_mut)] + let (mut (@ meta_var), i) = (@ meta_var).quote_into_iter(); + let has_iter = has_iter | i; + ) + .to_tokens(&mut content_tokens); + } + minimal_quote!( + let _: crate::HasIterator = has_iter; + ) + .to_tokens(&mut content_tokens); + + let while_ident = TokenTree::Ident(Ident::new("while", Span::call_site())); + let true_literal = TokenTree::Ident(Ident::new("true", Span::call_site())); + + let mut inner_tokens = TokenStream::new(); + for meta_var in &meta_vars { + minimal_quote!( + let (@ meta_var) = match (@ meta_var).next() { + Some(_x) => crate::RepInterp(_x), + None => break, + }; + ) + .to_tokens(&mut inner_tokens); + } + if let Some(sep) = sep_opt { + minimal_quote!( + if _i > 0 { + (@ minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new( + (@ TokenTree::from(Literal::character(sep.as_char()))), + (@ minimal_quote!(crate::Spacing::Alone)), + )), &mut ts);)) + } + _i += 1; + ) + .to_tokens(&mut inner_tokens); + }; + minimal_quote!( + (@ quote(content.clone())).to_tokens(&mut ts); + ) + .to_tokens(&mut inner_tokens); + let while_block = TokenTree::Group(Group::new(Delimiter::Brace, inner_tokens)); + + content_tokens.extend(vec![while_ident, true_literal, while_block]); + let block = TokenTree::Group(Group::new(Delimiter::Brace, content_tokens)); + minimal_quote!((@ block)).to_tokens(&mut tokens); + + continue; + } TokenTree::Ident(_) => { minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);) .to_tokens(&mut tokens); @@ -155,6 +276,30 @@ pub fn quote(stream: TokenStream) -> TokenStream { } } +fn collect_meta_vars(stream: TokenStream) -> Vec { + fn helper(stream: TokenStream, out: &mut Vec) { + let mut iter = stream.into_iter().peekable(); + while let Some(tree) = iter.next() { + match &tree { + TokenTree::Punct(tt) if tt.as_char() == '$' => { + if let Some(TokenTree::Ident(id)) = iter.peek() { + out.push(id.clone()); + iter.next(); + } + } + TokenTree::Group(tt) => { + helper(tt.stream(), out); + } + _ => {} + } + } + } + + let mut vars = Vec::new(); + helper(stream, &mut vars); + vars +} + /// Quote a `Span` into a `TokenStream`. /// This is needed to implement a custom quoter. #[unstable(feature = "proc_macro_quote", issue = "54722")] diff --git a/tests/ui/proc-macro/quote/auxiliary/basic.rs b/tests/ui/proc-macro/quote/auxiliary/basic.rs index ef726bbfbe3a0..c50bb964eab06 100644 --- a/tests/ui/proc-macro/quote/auxiliary/basic.rs +++ b/tests/ui/proc-macro/quote/auxiliary/basic.rs @@ -4,6 +4,7 @@ extern crate proc_macro; use std::borrow::Cow; +use std::collections::BTreeSet; use std::ffi::{CStr, CString}; use proc_macro::*; @@ -12,6 +13,8 @@ use proc_macro::*; pub fn run_tests(_: TokenStream) -> TokenStream { test_quote_impl(); test_substitution(); + test_iter(); + test_array(); test_advanced(); test_integer(); test_floating(); @@ -24,6 +27,13 @@ pub fn run_tests(_: TokenStream) -> TokenStream { test_ident(); test_underscore(); test_duplicate(); + test_fancy_repetition(); + test_nested_fancy_repetition(); + test_duplicate_name_repetition(); + test_duplicate_name_repetition_no_copy(); + test_btreeset_repetition(); + test_variable_name_conflict(); + test_nonrep_in_repetition(); test_empty_quote(); test_box_str(); test_cow(); @@ -34,6 +44,7 @@ pub fn run_tests(_: TokenStream) -> TokenStream { test_inner_block_comment(); test_outer_attr(); test_inner_attr(); + test_star_after_repetition(); test_quote_raw_id(); TokenStream::new() @@ -49,20 +60,9 @@ pub fn run_tests(_: TokenStream) -> TokenStream { // - fn test_type_inference_for_span // - wrong-type-span.rs // - format_ident: +// - fn test_closure // - fn test_format_ident // - fn test_format_ident_strip_raw -// - repetition: -// - fn test_iter -// - fn test_array -// - fn test_fancy_repetition -// - fn test_nested_fancy_repetition -// - fn test_duplicate_name_repetition -// - fn test_duplicate_name_repetition_no_copy -// - fn test_btreeset_repetition -// - fn test_variable_name_conflict -// - fn test_nonrep_in_repetition -// - fn test_closure -// - fn test_star_after_repetition struct X; @@ -99,6 +99,39 @@ fn test_substitution() { assert_eq!(expected, tokens.to_string()); } +fn test_iter() { + let primes = &[X, X, X, X]; + + assert_eq!("X X X X", quote!($($primes)*).to_string()); + + assert_eq!("X, X, X, X,", quote!($($primes,)*).to_string()); + + assert_eq!("X, X, X, X", quote!($($primes),*).to_string()); +} + +fn test_array() { + let array: [u8; 40] = [0; 40]; + let _ = quote!($($array $array)*); + + let ref_array: &[u8; 40] = &[0; 40]; + let _ = quote!($($ref_array $ref_array)*); + + let ref_slice: &[u8] = &[0; 40]; + let _ = quote!($($ref_slice $ref_slice)*); + + let array: [X; 2] = [X, X]; // !Copy + let _ = quote!($($array $array)*); + + let ref_array: &[X; 2] = &[X, X]; + let _ = quote!($($ref_array $ref_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)*)*); +} + fn test_advanced() { let generics = quote!( <'a, T> ); @@ -279,6 +312,88 @@ fn test_duplicate() { assert_eq!(expected, tokens.to_string()); } +fn test_fancy_repetition() { + let foo = vec!["a", "b"]; + let bar = vec![true, false]; + + let tokens = quote! { + $($foo: $bar),* + }; + + let expected = r#""a" : true, "b" : false"#; + assert_eq!(expected, tokens.to_string()); +} + +fn test_nested_fancy_repetition() { + let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']]; + + let tokens = quote! { + $( + $($nested)* + ),* + }; + + let expected = "'a' 'b' 'c', 'x' 'y' 'z'"; + assert_eq!(expected, 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()); +} + +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()); +} + +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()); +} + +fn test_variable_name_conflict() { + // The implementation of `#(...),*` uses the variable `_i` but it should be + // fine, if a little confusing when debugging. + let _i = vec!['a', 'b']; + let tokens = quote! { $($_i),* }; + let expected = "'a', 'b'"; + assert_eq!(expected, tokens.to_string()); +} + +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()); +} + fn test_empty_quote() { let tokens = quote!(); assert_eq!("", tokens.to_string()); @@ -355,6 +470,19 @@ fn test_inner_attr() { assert_eq!(expected, tokens.to_string()); } +// https://github.com/dtolnay/quote/issues/130 +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()); +} + fn test_quote_raw_id() { let id = quote!(r#raw_id); assert_eq!(id.to_string(), "r#raw_id"); diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs index 2f67ae1bc6edc..418e3dd444dda 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -13,5 +9,5 @@ fn main() { // Without some protection against repetitions with no iterator somewhere // inside, this would loop infinitely. - quote!($($nonrep $nonrep)*); + quote!($($nonrep $nonrep)*); //~ ERROR mismatched types } diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr index 5f28a46f3181d..ecb12c1df3b6a 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr @@ -1,10 +1,13 @@ -error: proc macro panicked - --> $DIR/does-not-have-iter-interpolated-dup.rs:16:5 +error[E0308]: mismatched types + --> $DIR/does-not-have-iter-interpolated-dup.rs:12:5 | LL | quote!($($nonrep $nonrep)*); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: message: `$` must be followed by an ident or `$` in `quote!` + | | + | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected due to this + | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs index 1efb3eac64247..507936770aa4f 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -13,5 +9,5 @@ fn main() { // Without some protection against repetitions with no iterator somewhere // inside, this would loop infinitely. - quote!($($nonrep)*); + quote!($($nonrep)*); //~ ERROR mismatched types } diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr index 595aa8587634a..093e2ebc09858 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr @@ -1,10 +1,13 @@ -error: proc macro panicked - --> $DIR/does-not-have-iter-interpolated.rs:16:5 +error[E0308]: mismatched types + --> $DIR/does-not-have-iter-interpolated.rs:12:5 | LL | quote!($($nonrep)*); | ^^^^^^^^^^^^^^^^^^^ - | - = help: message: `$` must be followed by an ident or `$` in `quote!` + | | + | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected due to this + | here the type of `has_iter` is inferred to be `ThereIsNoIteratorInRepetition` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs index 5f2ddabc390da..7e41b08f2636d 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs +++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -9,5 +5,5 @@ extern crate proc_macro; use proc_macro::quote; fn main() { - quote!($(a b),*); + quote!($(a b),*); //~ ERROR mismatched types } diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr index f6f5d7e007d0f..937209e675ec8 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr @@ -1,10 +1,12 @@ -error: proc macro panicked - --> $DIR/does-not-have-iter-separated.rs:12:5 +error[E0308]: mismatched types + --> $DIR/does-not-have-iter-separated.rs:8:5 | LL | quote!($(a b),*); | ^^^^^^^^^^^^^^^^ - | - = help: message: `$` must be followed by an ident or `$` in `quote!` + | | + | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected due to this error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.rs b/tests/ui/proc-macro/quote/does-not-have-iter.rs index 25ffd786cc614..038851ff76ed6 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter.rs +++ b/tests/ui/proc-macro/quote/does-not-have-iter.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -9,5 +5,5 @@ extern crate proc_macro; use proc_macro::quote; fn main() { - quote!($(a b)*); + quote!($(a b)*); //~ ERROR mismatched types } diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.stderr b/tests/ui/proc-macro/quote/does-not-have-iter.stderr index 0ed1daffc8cdf..e74ea3348992f 100644 --- a/tests/ui/proc-macro/quote/does-not-have-iter.stderr +++ b/tests/ui/proc-macro/quote/does-not-have-iter.stderr @@ -1,10 +1,12 @@ -error: proc macro panicked - --> $DIR/does-not-have-iter.rs:12:5 +error[E0308]: mismatched types + --> $DIR/does-not-have-iter.rs:8:5 | LL | quote!($(a b)*); | ^^^^^^^^^^^^^^^ - | - = help: message: `$` must be followed by an ident or `$` in `quote!` + | | + | expected `HasIterator`, found `ThereIsNoIteratorInRepetition` + | expected due to this error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/proc-macro/quote/not-quotable.stderr b/tests/ui/proc-macro/quote/not-quotable.stderr index 62a02638e548b..d1c3d06f2b661 100644 --- a/tests/ui/proc-macro/quote/not-quotable.stderr +++ b/tests/ui/proc-macro/quote/not-quotable.stderr @@ -15,8 +15,8 @@ LL | let _ = quote! { $ip }; Cow<'_, T> Option Rc - bool - and 24 others + RepInterp + and 25 others error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/quote/not-repeatable.rs b/tests/ui/proc-macro/quote/not-repeatable.rs index d115da7318156..0291e4ddf88d6 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.rs +++ b/tests/ui/proc-macro/quote/not-repeatable.rs @@ -1,7 +1,3 @@ -// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is -// expected to be incorrect. -//@ known-bug: #54722 - #![feature(proc_macro_quote)] extern crate proc_macro; @@ -12,5 +8,5 @@ struct Ipv4Addr; fn main() { let ip = Ipv4Addr; - let _ = quote! { $($ip)* }; + let _ = quote! { $($ip)* }; //~ ERROR the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied } diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr index 18fbcd7379858..aeda08d7de68b 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.stderr +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -1,10 +1,25 @@ -error: proc macro panicked - --> $DIR/not-repeatable.rs:15:13 +error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its trait bounds were not satisfied + --> $DIR/not-repeatable.rs:11:13 | +LL | struct Ipv4Addr; + | --------------- method `quote_into_iter` not found for this struct because it doesn't satisfy `Ipv4Addr: Iterator`, `Ipv4Addr: ToTokens`, `Ipv4Addr: proc_macro::ext::RepIteratorExt` or `Ipv4Addr: proc_macro::ext::RepToTokensExt` +... LL | let _ = quote! { $($ip)* }; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds | - = help: message: `$` must be followed by an ident or `$` in `quote!` + = note: the following trait bounds were not satisfied: + `Ipv4Addr: Iterator` + which is required by `Ipv4Addr: proc_macro::ext::RepIteratorExt` + `&Ipv4Addr: Iterator` + which is required by `&Ipv4Addr: proc_macro::ext::RepIteratorExt` + `Ipv4Addr: ToTokens` + which is required by `Ipv4Addr: proc_macro::ext::RepToTokensExt` + `&mut Ipv4Addr: Iterator` + which is required by `&mut Ipv4Addr: proc_macro::ext::RepIteratorExt` +note: the traits `Iterator` and `ToTokens` must be implemented + --> $SRC_DIR/proc_macro/src/to_tokens.rs:LL:COL + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0599`.