| //! Real world regressions and issues, not particularly minimized. |
| //! |
| //! While it's OK to just dump large macros here, it's preferable to come up |
| //! with a minimal example for the program and put a specific test to the parent |
| //! directory. |
| |
| use expect_test::expect; |
| |
| use crate::macro_expansion_tests::check; |
| |
| #[test] |
| fn test_vec() { |
| check( |
| r#" |
| macro_rules! vec { |
| () => ( |
| $crate::__rust_force_expr!($crate::vec::Vec::new()) |
| ); |
| ($elem:expr; $n:expr) => ( |
| $crate::__rust_force_expr!($crate::vec::from_elem($elem, $n)) |
| ); |
| ($($x:expr),+ $(,)?) => ( |
| $crate::__rust_force_expr!(<[_]>::into_vec( |
| // This rustc_box is not required, but it produces a dramatic improvement in compile |
| // time when constructing arrays with many elements. |
| #[rustc_box] |
| $crate::boxed::Box::new([$($x),+]) |
| )) |
| ); |
| } |
| |
| macro_rules! __rust_force_expr { |
| ($e:expr) => { |
| $e |
| }; |
| } |
| |
| fn main() { |
| vec!(); |
| vec![1u32,2]; |
| vec![a.]; |
| } |
| "#, |
| expect![[r#" |
| macro_rules! vec { |
| () => ( |
| $crate::__rust_force_expr!($crate::vec::Vec::new()) |
| ); |
| ($elem:expr; $n:expr) => ( |
| $crate::__rust_force_expr!($crate::vec::from_elem($elem, $n)) |
| ); |
| ($($x:expr),+ $(,)?) => ( |
| $crate::__rust_force_expr!(<[_]>::into_vec( |
| // This rustc_box is not required, but it produces a dramatic improvement in compile |
| // time when constructing arrays with many elements. |
| #[rustc_box] |
| $crate::boxed::Box::new([$($x),+]) |
| )) |
| ); |
| } |
| |
| macro_rules! __rust_force_expr { |
| ($e:expr) => { |
| $e |
| }; |
| } |
| |
| fn main() { |
| $crate::__rust_force_expr!($crate:: vec:: Vec:: new()); |
| $crate::__rust_force_expr!(<[_]>:: into_vec(#[rustc_box]$crate:: boxed:: Box:: new([1u32, 2]))); |
| /* error: expected Expr */$crate::__rust_force_expr!($crate:: vec:: from_elem((a.), $n)); |
| } |
| "#]], |
| ); |
| // FIXME we should have testing infra for multi level expansion tests |
| check( |
| r#" |
| macro_rules! __rust_force_expr { |
| ($e:expr) => { |
| $e |
| }; |
| } |
| |
| fn main() { |
| __rust_force_expr!(crate:: vec:: Vec:: new()); |
| __rust_force_expr!(<[_]>:: into_vec(#[rustc_box] crate:: boxed:: Box:: new([1u32, 2]))); |
| __rust_force_expr/*+errors*/!(crate:: vec:: from_elem((a.), $n)); |
| } |
| "#, |
| expect![[r#" |
| macro_rules! __rust_force_expr { |
| ($e:expr) => { |
| $e |
| }; |
| } |
| |
| fn main() { |
| (crate ::vec::Vec::new()); |
| (<[_]>::into_vec(#[rustc_box] crate ::boxed::Box::new([1u32, 2]))); |
| /* error: expected Expr *//* parse error: expected field name or number */ |
| /* parse error: expected expression */ |
| /* parse error: expected R_PAREN */ |
| /* parse error: expected COMMA */ |
| /* parse error: expected expression, item or let statement */ |
| (crate ::vec::from_elem((a.), $n)); |
| } |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_winapi_struct() { |
| // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366 |
| |
| check( |
| r#" |
| macro_rules! STRUCT { |
| ($(#[$attrs:meta])* struct $name:ident { |
| $($field:ident: $ftype:ty,)+ |
| }) => ( |
| #[repr(C)] #[derive(Copy)] $(#[$attrs])* |
| pub struct $name { |
| $(pub $field: $ftype,)+ |
| } |
| impl Clone for $name { |
| #[inline] |
| fn clone(&self) -> $name { *self } |
| } |
| #[cfg(feature = "impl-default")] |
| impl Default for $name { |
| #[inline] |
| fn default() -> $name { unsafe { $crate::_core::mem::zeroed() } } |
| } |
| ); |
| } |
| |
| // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs |
| STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}} |
| |
| STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}} |
| "#, |
| expect![[r#" |
| macro_rules! STRUCT { |
| ($(#[$attrs:meta])* struct $name:ident { |
| $($field:ident: $ftype:ty,)+ |
| }) => ( |
| #[repr(C)] #[derive(Copy)] $(#[$attrs])* |
| pub struct $name { |
| $(pub $field: $ftype,)+ |
| } |
| impl Clone for $name { |
| #[inline] |
| fn clone(&self) -> $name { *self } |
| } |
| #[cfg(feature = "impl-default")] |
| impl Default for $name { |
| #[inline] |
| fn default() -> $name { unsafe { $crate::_core::mem::zeroed() } } |
| } |
| ); |
| } |
| |
| #[repr(C)] |
| #[derive(Copy)] pub struct D3DVSHADERCAPS2_0 { |
| pub Caps: u8, |
| } |
| impl Clone for D3DVSHADERCAPS2_0 { |
| #[inline] fn clone(&self ) -> D3DVSHADERCAPS2_0 { |
| *self |
| } |
| } |
| #[cfg(feature = "impl-default")] impl Default for D3DVSHADERCAPS2_0 { |
| #[inline] fn default() -> D3DVSHADERCAPS2_0 { |
| unsafe { |
| $crate::_core::mem::zeroed() |
| } |
| } |
| } |
| |
| #[repr(C)] |
| #[derive(Copy)] |
| #[cfg_attr(target_arch = "x86", repr(packed))] pub struct D3DCONTENTPROTECTIONCAPS { |
| pub Caps: u8, |
| } |
| impl Clone for D3DCONTENTPROTECTIONCAPS { |
| #[inline] fn clone(&self ) -> D3DCONTENTPROTECTIONCAPS { |
| *self |
| } |
| } |
| #[cfg(feature = "impl-default")] impl Default for D3DCONTENTPROTECTIONCAPS { |
| #[inline] fn default() -> D3DCONTENTPROTECTIONCAPS { |
| unsafe { |
| $crate::_core::mem::zeroed() |
| } |
| } |
| } |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_int_base() { |
| check( |
| r#" |
| macro_rules! int_base { |
| ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl fmt::$Trait for $T { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| $Radix.fmt_int(*self as $U, f) |
| } |
| } |
| } |
| } |
| int_base!{Binary for isize as usize -> Binary} |
| "#, |
| expect![[r#" |
| macro_rules! int_base { |
| ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { |
| #[stable(feature = "rust1", since = "1.0.0")] |
| impl fmt::$Trait for $T { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| $Radix.fmt_int(*self as $U, f) |
| } |
| } |
| } |
| } |
| #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Binary for isize { |
| fn fmt(&self , f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| Binary.fmt_int(*self as usize, f) |
| } |
| } |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_generate_pattern_iterators() { |
| // From <https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs>. |
| check( |
| r#" |
| macro_rules! generate_pattern_iterators { |
| { double ended; with $(#[$common_stability_attribute:meta])*, |
| $forward_iterator:ident, |
| $reverse_iterator:ident, $iterty:ty |
| } => { ok!(); } |
| } |
| generate_pattern_iterators ! ( double ended ; with # [ stable ( feature = "rust1" , since = "1.0.0" ) ] , Split , RSplit , & 'a str ); |
| "#, |
| expect![[r##" |
| macro_rules! generate_pattern_iterators { |
| { double ended; with $(#[$common_stability_attribute:meta])*, |
| $forward_iterator:ident, |
| $reverse_iterator:ident, $iterty:ty |
| } => { ok!(); } |
| } |
| ok!(); |
| "##]], |
| ); |
| } |
| |
| #[test] |
| fn test_impl_fn_for_zst() { |
| // From <https://github.com/rust-lang/rust/blob/5d20ff4d2718c820632b38c1e49d4de648a9810b/src/libcore/internal_macros.rs>. |
| check( |
| r#" |
| macro_rules! impl_fn_for_zst { |
| {$( $( #[$attr: meta] )* |
| struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = |
| |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty $body: block; |
| )+} => {$( |
| $( #[$attr] )* |
| struct $Name; |
| |
| impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { |
| #[inline] |
| extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { |
| $body |
| } |
| } |
| |
| impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { |
| #[inline] |
| extern "rust-call" fn call_mut( |
| &mut self, |
| ($( $arg, )*): ($( $ArgTy, )*) |
| ) -> $ReturnTy { |
| Fn::call(&*self, ($( $arg, )*)) |
| } |
| } |
| |
| impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { |
| type Output = $ReturnTy; |
| |
| #[inline] |
| extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { |
| Fn::call(&self, ($( $arg, )*)) |
| } |
| } |
| )+} |
| } |
| |
| impl_fn_for_zst ! { |
| #[derive(Clone)] |
| struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { |
| c.escape_debug_ext(false) |
| }; |
| |
| #[derive(Clone)] |
| struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode { |
| c.escape_unicode() |
| }; |
| |
| #[derive(Clone)] |
| struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault { |
| c.escape_default() |
| }; |
| } |
| |
| "#, |
| expect![[r#" |
| macro_rules! impl_fn_for_zst { |
| {$( $( #[$attr: meta] )* |
| struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = |
| |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty $body: block; |
| )+} => {$( |
| $( #[$attr] )* |
| struct $Name; |
| |
| impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { |
| #[inline] |
| extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { |
| $body |
| } |
| } |
| |
| impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { |
| #[inline] |
| extern "rust-call" fn call_mut( |
| &mut self, |
| ($( $arg, )*): ($( $ArgTy, )*) |
| ) -> $ReturnTy { |
| Fn::call(&*self, ($( $arg, )*)) |
| } |
| } |
| |
| impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { |
| type Output = $ReturnTy; |
| |
| #[inline] |
| extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { |
| Fn::call(&self, ($( $arg, )*)) |
| } |
| } |
| )+} |
| } |
| |
| #[derive(Clone)] struct CharEscapeDebugContinue; |
| impl Fn<(char, )> for CharEscapeDebugContinue { |
| #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDebug { { |
| c.escape_debug_ext(false ) |
| } |
| } |
| } |
| impl FnMut<(char, )> for CharEscapeDebugContinue { |
| #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug { |
| Fn::call(&*self , (c, )) |
| } |
| } |
| impl FnOnce<(char, )> for CharEscapeDebugContinue { |
| type Output = char::EscapeDebug; |
| #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDebug { |
| Fn::call(&self , (c, )) |
| } |
| } |
| #[derive(Clone)] struct CharEscapeUnicode; |
| impl Fn<(char, )> for CharEscapeUnicode { |
| #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { { |
| c.escape_unicode() |
| } |
| } |
| } |
| impl FnMut<(char, )> for CharEscapeUnicode { |
| #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode { |
| Fn::call(&*self , (c, )) |
| } |
| } |
| impl FnOnce<(char, )> for CharEscapeUnicode { |
| type Output = char::EscapeUnicode; |
| #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode { |
| Fn::call(&self , (c, )) |
| } |
| } |
| #[derive(Clone)] struct CharEscapeDefault; |
| impl Fn<(char, )> for CharEscapeDefault { |
| #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDefault { { |
| c.escape_default() |
| } |
| } |
| } |
| impl FnMut<(char, )> for CharEscapeDefault { |
| #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault { |
| Fn::call(&*self , (c, )) |
| } |
| } |
| impl FnOnce<(char, )> for CharEscapeDefault { |
| type Output = char::EscapeDefault; |
| #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDefault { |
| Fn::call(&self , (c, )) |
| } |
| } |
| |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_impl_nonzero_fmt() { |
| // From <https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/num/mod.rs#L12>. |
| check( |
| r#" |
| macro_rules! impl_nonzero_fmt { |
| ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { ok!(); } |
| } |
| impl_nonzero_fmt! { |
| #[stable(feature= "nonzero",since="1.28.0")] |
| (Debug, Display, Binary, Octal, LowerHex, UpperHex) for NonZeroU8 |
| } |
| "#, |
| expect![[r##" |
| macro_rules! impl_nonzero_fmt { |
| ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { ok!(); } |
| } |
| ok!(); |
| "##]], |
| ); |
| } |
| |
| #[test] |
| fn test_cfg_if_items() { |
| // From <https://github.com/rust-lang/rust/blob/33fe1131cadba69d317156847be9a402b89f11bb/src/libstd/macros.rs#L986>. |
| check( |
| r#" |
| macro_rules! __cfg_if_items { |
| (($($not:meta,)*) ; ) => {}; |
| (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { |
| __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } |
| } |
| } |
| __cfg_if_items! { |
| (rustdoc,); |
| ( () ( |
| #[ cfg(any(target_os = "redox", unix))] |
| #[ stable(feature = "rust1", since = "1.0.0")] |
| pub use sys::ext as unix; |
| |
| #[cfg(windows)] |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub use sys::ext as windows; |
| |
| #[cfg(any(target_os = "linux", target_os = "l4re"))] |
| pub mod linux; |
| )), |
| } |
| "#, |
| expect![[r#" |
| macro_rules! __cfg_if_items { |
| (($($not:meta,)*) ; ) => {}; |
| (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { |
| __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } |
| } |
| } |
| __cfg_if_items! { |
| (rustdoc, ); |
| } |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_cfg_if_main() { |
| // From <https://github.com/rust-lang/rust/blob/3d211248393686e0f73851fc7548f6605220fbe1/src/libpanic_unwind/macros.rs#L9>. |
| check( |
| r#" |
| macro_rules! cfg_if { |
| ($(if #[cfg($($meta:meta),*)] { $($it:item)* } )else* else { $($it2:item)* }) |
| => { |
| __cfg_if_items! { |
| () ; |
| $( ( ($($meta),*) ($($it)*) ), )* |
| ( () ($($it2)*) ), |
| } |
| }; |
| |
| // Internal macro to Apply a cfg attribute to a list of items |
| (@__apply $m:meta, $($it:item)*) => { $(#[$m] $it)* }; |
| } |
| |
| cfg_if! { |
| if #[cfg(target_env = "msvc")] { |
| // no extra unwinder support needed |
| } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { |
| // no unwinder on the system! |
| } else { |
| mod libunwind; |
| pub use libunwind::*; |
| } |
| } |
| |
| cfg_if! { |
| @__apply cfg(all(not(any(not(any(target_os = "solaris", target_os = "illumos")))))), |
| } |
| "#, |
| expect![[r#" |
| macro_rules! cfg_if { |
| ($(if #[cfg($($meta:meta),*)] { $($it:item)* } )else* else { $($it2:item)* }) |
| => { |
| __cfg_if_items! { |
| () ; |
| $( ( ($($meta),*) ($($it)*) ), )* |
| ( () ($($it2)*) ), |
| } |
| }; |
| |
| // Internal macro to Apply a cfg attribute to a list of items |
| (@__apply $m:meta, $($it:item)*) => { $(#[$m] $it)* }; |
| } |
| |
| __cfg_if_items! { |
| (); |
| ((target_env = "msvc")()), ((all(target_arch = "wasm32", not(target_os = "emscripten")))()), (()(mod libunwind; |
| pub use libunwind::*; |
| )), |
| } |
| |
| |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_proptest_arbitrary() { |
| // From <https://github.com/AltSysrq/proptest/blob/d1c4b049337d2f75dd6f49a095115f7c532e5129/proptest/src/arbitrary/macros.rs#L16>. |
| check( |
| r#" |
| macro_rules! arbitrary { |
| ([$($bounds : tt)*] $typ: ty, $strategy: ty, $params: ty; |
| $args: ident => $logic: expr) => { |
| impl<$($bounds)*> $crate::arbitrary::Arbitrary for $typ { |
| type Parameters = $params; |
| type Strategy = $strategy; |
| fn arbitrary_with($args: Self::Parameters) -> Self::Strategy { |
| $logic |
| } |
| } |
| }; |
| } |
| |
| arbitrary!( |
| [A:Arbitrary] |
| Vec<A> , |
| VecStrategy<A::Strategy>, |
| RangedParams1<A::Parameters>; |
| args => { |
| let product_unpack![range, a] = args; |
| vec(any_with::<A>(a), range) |
| } |
| ); |
| "#, |
| expect![[r#" |
| macro_rules! arbitrary { |
| ([$($bounds : tt)*] $typ: ty, $strategy: ty, $params: ty; |
| $args: ident => $logic: expr) => { |
| impl<$($bounds)*> $crate::arbitrary::Arbitrary for $typ { |
| type Parameters = $params; |
| type Strategy = $strategy; |
| fn arbitrary_with($args: Self::Parameters) -> Self::Strategy { |
| $logic |
| } |
| } |
| }; |
| } |
| |
| impl <A: Arbitrary> $crate::arbitrary::Arbitrary for Vec<A> { |
| type Parameters = RangedParams1<A::Parameters>; |
| type Strategy = VecStrategy<A::Strategy>; |
| fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { { |
| let product_unpack![range, a] = args; |
| vec(any_with::<A>(a), range) |
| } |
| } |
| } |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_old_ridl() { |
| // This is from winapi 2.8, which do not have a link from github. |
| check( |
| r#" |
| #[macro_export] |
| macro_rules! RIDL { |
| (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) |
| {$( |
| fn $method:ident(&mut self $(,$p:ident : $t:ty)*) -> $rtr:ty |
| ),+} |
| ) => { |
| impl $interface { |
| $(pub unsafe fn $method(&mut self) -> $rtr { |
| ((*self.lpVtbl).$method)(self $(,$p)*) |
| })+ |
| } |
| }; |
| } |
| |
| RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID3D11DeviceChildVtbl) { |
| fn GetDataSize(&mut self) -> UINT |
| }} |
| "#, |
| expect![[r#" |
| #[macro_export] |
| macro_rules! RIDL { |
| (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) |
| {$( |
| fn $method:ident(&mut self $(,$p:ident : $t:ty)*) -> $rtr:ty |
| ),+} |
| ) => { |
| impl $interface { |
| $(pub unsafe fn $method(&mut self) -> $rtr { |
| ((*self.lpVtbl).$method)(self $(,$p)*) |
| })+ |
| } |
| }; |
| } |
| |
| impl ID3D11Asynchronous { |
| pub unsafe fn GetDataSize(&mut self ) -> UINT { |
| ((*self .lpVtbl).GetDataSize)(self ) |
| } |
| } |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_quick_error() { |
| check( |
| r#" |
| macro_rules! quick_error { |
| (SORT [enum $name:ident $( #[$meta:meta] )*] |
| items [$($( #[$imeta:meta] )* |
| => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] |
| {$( $ifuncs:tt )*} )* ] |
| buf [ ] |
| queue [ ] |
| ) => { |
| quick_error!(ENUMINITION [enum $name $( #[$meta] )*] |
| body [] |
| queue [$( |
| $( #[$imeta] )* |
| => |
| $iitem: $imode [$( $ivar: $ityp ),*] |
| )*] |
| ); |
| }; |
| } |
| quick_error ! ( |
| SORT |
| [enum Wrapped #[derive(Debug)]] |
| items [ |
| => One: UNIT [] {} |
| => Two: TUPLE [s :String] {display ("two: {}" , s) from ()} ] |
| buf [ ] |
| queue [ ] |
| ); |
| |
| "#, |
| expect![[r#" |
| macro_rules! quick_error { |
| (SORT [enum $name:ident $( #[$meta:meta] )*] |
| items [$($( #[$imeta:meta] )* |
| => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] |
| {$( $ifuncs:tt )*} )* ] |
| buf [ ] |
| queue [ ] |
| ) => { |
| quick_error!(ENUMINITION [enum $name $( #[$meta] )*] |
| body [] |
| queue [$( |
| $( #[$imeta] )* |
| => |
| $iitem: $imode [$( $ivar: $ityp ),*] |
| )*] |
| ); |
| }; |
| } |
| quick_error!(ENUMINITION[enum Wrapped#[derive(Debug)]]body[]queue[ = > One: UNIT[] = > Two: TUPLE[s: String]]); |
| |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn test_empty_repeat_vars_in_empty_repeat_vars() { |
| check( |
| r#" |
| macro_rules! delegate_impl { |
| ([$self_type:ident, $self_wrap:ty, $self_map:ident] |
| pub trait $name:ident $(: $sup:ident)* $(+ $more_sup:ident)* { |
| |
| $( |
| @escape [type $assoc_name_ext:ident] |
| )* |
| $( |
| @section type |
| $( |
| $(#[$_assoc_attr:meta])* |
| type $assoc_name:ident $(: $assoc_bound:ty)*; |
| )+ |
| )* |
| $( |
| @section self |
| $( |
| $(#[$_method_attr:meta])* |
| fn $method_name:ident(self $(: $self_selftype:ty)* $(,$marg:ident : $marg_ty:ty)*) -> $mret:ty; |
| )+ |
| )* |
| $( |
| @section nodelegate |
| $($tail:tt)* |
| )* |
| }) => { |
| impl<> $name for $self_wrap where $self_type: $name { |
| $( |
| $( |
| fn $method_name(self $(: $self_selftype)* $(,$marg: $marg_ty)*) -> $mret { |
| $self_map!(self).$method_name($($marg),*) |
| } |
| )* |
| )* |
| } |
| } |
| } |
| delegate_impl ! { |
| [G, &'a mut G, deref] pub trait Data: GraphBase {@section type type NodeWeight;} |
| } |
| "#, |
| expect![[r#" |
| macro_rules! delegate_impl { |
| ([$self_type:ident, $self_wrap:ty, $self_map:ident] |
| pub trait $name:ident $(: $sup:ident)* $(+ $more_sup:ident)* { |
| |
| $( |
| @escape [type $assoc_name_ext:ident] |
| )* |
| $( |
| @section type |
| $( |
| $(#[$_assoc_attr:meta])* |
| type $assoc_name:ident $(: $assoc_bound:ty)*; |
| )+ |
| )* |
| $( |
| @section self |
| $( |
| $(#[$_method_attr:meta])* |
| fn $method_name:ident(self $(: $self_selftype:ty)* $(,$marg:ident : $marg_ty:ty)*) -> $mret:ty; |
| )+ |
| )* |
| $( |
| @section nodelegate |
| $($tail:tt)* |
| )* |
| }) => { |
| impl<> $name for $self_wrap where $self_type: $name { |
| $( |
| $( |
| fn $method_name(self $(: $self_selftype)* $(,$marg: $marg_ty)*) -> $mret { |
| $self_map!(self).$method_name($($marg),*) |
| } |
| )* |
| )* |
| } |
| } |
| } |
| impl <> Data for &'amut G where G: Data {} |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_issue_2520() { |
| check( |
| r#" |
| macro_rules! my_macro { |
| { |
| ( $( |
| $( [] $sname:ident : $stype:ty )? |
| $( [$expr:expr] $nname:ident : $ntype:ty )? |
| ),* ) |
| } => {ok!( |
| Test { |
| $( |
| $( $sname, )? |
| )* |
| } |
| );}; |
| } |
| |
| my_macro! { |
| ([] p1: u32, [|_| S0K0] s: S0K0, [] k0: i32) |
| } |
| "#, |
| expect![[r#" |
| macro_rules! my_macro { |
| { |
| ( $( |
| $( [] $sname:ident : $stype:ty )? |
| $( [$expr:expr] $nname:ident : $ntype:ty )? |
| ),* ) |
| } => {ok!( |
| Test { |
| $( |
| $( $sname, )? |
| )* |
| } |
| );}; |
| } |
| |
| ok!(Test { |
| p1, k0, |
| } |
| ); |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_repeat_bad_var() { |
| // FIXME: the second rule of the macro should be removed and an error about |
| // `$( $c )+` raised |
| check( |
| r#" |
| macro_rules! foo { |
| ($( $b:ident )+) => { ok!($( $c )+); }; |
| ($( $b:ident )+) => { ok!($( $b )+); } |
| } |
| |
| foo!(b0 b1); |
| "#, |
| expect![[r#" |
| macro_rules! foo { |
| ($( $b:ident )+) => { ok!($( $c )+); }; |
| ($( $b:ident )+) => { ok!($( $b )+); } |
| } |
| |
| ok!(b0 b1); |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_issue_3861() { |
| // This is should (and does) produce a parse error. It used to infinite loop |
| // instead. |
| check( |
| r#" |
| macro_rules! rgb_color { |
| ($p:expr, $t:ty) => { |
| pub fn new() { |
| let _ = 0 as $t << $p; |
| } |
| }; |
| } |
| // +tree +errors |
| rgb_color!(8 + 8, u32); |
| "#, |
| expect![[r#" |
| macro_rules! rgb_color { |
| ($p:expr, $t:ty) => { |
| pub fn new() { |
| let _ = 0 as $t << $p; |
| } |
| }; |
| } |
| /* parse error: expected type */ |
| /* parse error: expected R_PAREN */ |
| /* parse error: expected R_ANGLE */ |
| /* parse error: expected `::` */ |
| /* parse error: expected COMMA */ |
| /* parse error: expected R_ANGLE */ |
| /* parse error: expected SEMICOLON */ |
| /* parse error: expected expression, item or let statement */ |
| pub fn new() { |
| let _ = 0 as u32<<(8+8); |
| } |
| // MACRO_ITEMS@0..31 |
| // FN@0..31 |
| // VISIBILITY@0..3 |
| // PUB_KW@0..3 "pub" |
| // FN_KW@3..5 "fn" |
| // NAME@5..8 |
| // IDENT@5..8 "new" |
| // PARAM_LIST@8..10 |
| // L_PAREN@8..9 "(" |
| // R_PAREN@9..10 ")" |
| // BLOCK_EXPR@10..31 |
| // STMT_LIST@10..31 |
| // L_CURLY@10..11 "{" |
| // LET_STMT@11..28 |
| // LET_KW@11..14 "let" |
| // WILDCARD_PAT@14..15 |
| // UNDERSCORE@14..15 "_" |
| // EQ@15..16 "=" |
| // CAST_EXPR@16..28 |
| // LITERAL@16..17 |
| // INT_NUMBER@16..17 "0" |
| // AS_KW@17..19 "as" |
| // PATH_TYPE@19..28 |
| // PATH@19..28 |
| // PATH_SEGMENT@19..28 |
| // NAME_REF@19..22 |
| // IDENT@19..22 "u32" |
| // GENERIC_ARG_LIST@22..28 |
| // L_ANGLE@22..23 "<" |
| // TYPE_ARG@23..27 |
| // DYN_TRAIT_TYPE@23..27 |
| // TYPE_BOUND_LIST@23..27 |
| // TYPE_BOUND@23..26 |
| // PATH_TYPE@23..26 |
| // PATH@23..26 |
| // PATH_SEGMENT@23..26 |
| // L_ANGLE@23..24 "<" |
| // PAREN_TYPE@24..26 |
| // L_PAREN@24..25 "(" |
| // ERROR@25..26 |
| // INT_NUMBER@25..26 "8" |
| // PLUS@26..27 "+" |
| // CONST_ARG@27..28 |
| // LITERAL@27..28 |
| // INT_NUMBER@27..28 "8" |
| // ERROR@28..29 |
| // R_PAREN@28..29 ")" |
| // SEMICOLON@29..30 ";" |
| // R_CURLY@30..31 "}" |
| |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn test_no_space_after_semi_colon() { |
| check( |
| r#" |
| macro_rules! with_std { |
| ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*) |
| } |
| |
| with_std! {mod m;mod f;} |
| "#, |
| expect![[r#" |
| macro_rules! with_std { |
| ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*) |
| } |
| |
| #[cfg(feature = "std")] mod m; |
| #[cfg(feature = "std")] mod f; |
| "#]], |
| ) |
| } |
| |
| #[test] |
| fn eager_regression_15403() { |
| check( |
| r#" |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! format_args {} |
| |
| fn main() { |
| format_args /* +errors */ !("{}", line.1.); |
| } |
| |
| "#, |
| expect![[r##" |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! format_args {} |
| |
| fn main() { |
| /* parse error: expected field name or number */ |
| builtin #format_args ("{}", line.1.); |
| } |
| |
| "##]], |
| ); |
| } |
| |
| #[test] |
| fn eager_regression_154032() { |
| check( |
| r#" |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! format_args {} |
| |
| fn main() { |
| format_args /* +errors */ !("{}", &[0 2]); |
| } |
| |
| "#, |
| expect![[r##" |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! format_args {} |
| |
| fn main() { |
| /* parse error: expected COMMA */ |
| /* parse error: expected R_BRACK */ |
| /* parse error: expected COMMA */ |
| /* parse error: expected COMMA */ |
| /* parse error: expected expression */ |
| /* parse error: expected R_PAREN */ |
| /* parse error: expected expression, item or let statement */ |
| /* parse error: expected expression, item or let statement */ |
| builtin #format_args ("{}", &[0 2]); |
| } |
| |
| "##]], |
| ); |
| } |
| |
| #[test] |
| fn eager_concat_line() { |
| check( |
| r#" |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! concat {} |
| |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! line {} |
| |
| fn main() { |
| concat!("event ", line!()); |
| } |
| |
| "#, |
| expect![[r##" |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! concat {} |
| |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! line {} |
| |
| fn main() { |
| "event 0"; |
| } |
| |
| "##]], |
| ); |
| } |
| |
| #[test] |
| fn eager_concat_bytes_panic() { |
| check( |
| r#" |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! concat_bytes {} |
| |
| fn main() { |
| let x = concat_bytes!(2); |
| } |
| |
| "#, |
| expect![[r#" |
| #[rustc_builtin_macro] |
| #[macro_export] |
| macro_rules! concat_bytes {} |
| |
| fn main() { |
| let x = /* error: unexpected token */b""; |
| } |
| |
| "#]], |
| ); |
| } |
| |
| #[test] |
| fn regression_16529() { |
| check( |
| r#" |
| mod any { |
| #[macro_export] |
| macro_rules! nameable { |
| { |
| struct $name:ident[$a:lifetime] |
| } => { |
| $crate::any::nameable! { |
| struct $name[$a] |
| a |
| } |
| }; |
| { |
| struct $name:ident[$a:lifetime] |
| a |
| } => {}; |
| } |
| pub use nameable; |
| |
| nameable! { |
| Name['a] |
| } |
| } |
| "#, |
| expect![[r#" |
| mod any { |
| #[macro_export] |
| macro_rules! nameable { |
| { |
| struct $name:ident[$a:lifetime] |
| } => { |
| $crate::any::nameable! { |
| struct $name[$a] |
| a |
| } |
| }; |
| { |
| struct $name:ident[$a:lifetime] |
| a |
| } => {}; |
| } |
| pub use nameable; |
| |
| /* error: unexpected token in input */$crate::any::nameable! { |
| struct $name[$a]a |
| } |
| } |
| "#]], |
| ); |
| } |