diff options
Diffstat (limited to 'crates/ra_mbe/src/lib.rs')
-rw-r--r-- | crates/ra_mbe/src/lib.rs | 216 |
1 files changed, 209 insertions, 7 deletions
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index eedc0c5dd..7817232d6 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -24,6 +24,7 @@ mod subtree_source; | |||
24 | mod subtree_parser; | 24 | mod subtree_parser; |
25 | 25 | ||
26 | use ra_syntax::SmolStr; | 26 | use ra_syntax::SmolStr; |
27 | use smallvec::SmallVec; | ||
27 | 28 | ||
28 | pub use tt::{Delimiter, Punct}; | 29 | pub use tt::{Delimiter, Punct}; |
29 | 30 | ||
@@ -99,10 +100,17 @@ pub(crate) struct Subtree { | |||
99 | } | 100 | } |
100 | 101 | ||
101 | #[derive(Clone, Debug, PartialEq, Eq)] | 102 | #[derive(Clone, Debug, PartialEq, Eq)] |
103 | pub(crate) enum Separator { | ||
104 | Literal(tt::Literal), | ||
105 | Ident(tt::Ident), | ||
106 | Puncts(SmallVec<[tt::Punct; 3]>), | ||
107 | } | ||
108 | |||
109 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
102 | pub(crate) struct Repeat { | 110 | pub(crate) struct Repeat { |
103 | pub(crate) subtree: Subtree, | 111 | pub(crate) subtree: Subtree, |
104 | pub(crate) kind: RepeatKind, | 112 | pub(crate) kind: RepeatKind, |
105 | pub(crate) separator: Option<char>, | 113 | pub(crate) separator: Option<Separator>, |
106 | } | 114 | } |
107 | 115 | ||
108 | #[derive(Clone, Debug, PartialEq, Eq)] | 116 | #[derive(Clone, Debug, PartialEq, Eq)] |
@@ -175,8 +183,8 @@ impl_froms!(TokenTree: Leaf, Subtree); | |||
175 | let expansion = rules.expand(&invocation_tt).unwrap(); | 183 | let expansion = rules.expand(&invocation_tt).unwrap(); |
176 | assert_eq!( | 184 | assert_eq!( |
177 | expansion.to_string(), | 185 | expansion.to_string(), |
178 | "impl From < Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree :: Leaf (it)}} \ | 186 | "impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ |
179 | impl From < Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree :: Subtree (it)}}" | 187 | impl From <Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}" |
180 | ) | 188 | ) |
181 | } | 189 | } |
182 | 190 | ||
@@ -384,7 +392,7 @@ impl_froms!(TokenTree: Leaf, Subtree); | |||
384 | "#, | 392 | "#, |
385 | ); | 393 | ); |
386 | 394 | ||
387 | assert_expansion(&rules, "foo! { foo, bar }", "fn baz {foo () ; bar () ;}"); | 395 | assert_expansion(&rules, "foo! { foo, bar }", "fn baz {foo () ;bar ()}"); |
388 | } | 396 | } |
389 | 397 | ||
390 | #[test] | 398 | #[test] |
@@ -417,6 +425,42 @@ impl_froms!(TokenTree: Leaf, Subtree); | |||
417 | } | 425 | } |
418 | 426 | ||
419 | #[test] | 427 | #[test] |
428 | fn test_match_group_with_multichar_sep() { | ||
429 | let rules = create_rules( | ||
430 | r#" | ||
431 | macro_rules! foo { | ||
432 | (fn $name:ident {$($i:literal)*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
433 | }"#, | ||
434 | ); | ||
435 | |||
436 | assert_expansion(&rules, "foo! (fn baz {true true} )", "fn baz () -> bool {true &&true}"); | ||
437 | } | ||
438 | |||
439 | #[test] | ||
440 | fn test_match_group_zero_match() { | ||
441 | let rules = create_rules( | ||
442 | r#" | ||
443 | macro_rules! foo { | ||
444 | ( $($i:ident)* ) => (); | ||
445 | }"#, | ||
446 | ); | ||
447 | |||
448 | assert_expansion(&rules, "foo! ()", ""); | ||
449 | } | ||
450 | |||
451 | #[test] | ||
452 | fn test_match_group_in_group() { | ||
453 | let rules = create_rules( | ||
454 | r#" | ||
455 | macro_rules! foo { | ||
456 | { $( ( $($i:ident)* ) )* } => ( $( ( $($i)* ) )* ); | ||
457 | }"#, | ||
458 | ); | ||
459 | |||
460 | assert_expansion(&rules, "foo! ( (a b) )", "(a b)"); | ||
461 | } | ||
462 | |||
463 | #[test] | ||
420 | fn test_expand_to_item_list() { | 464 | fn test_expand_to_item_list() { |
421 | let rules = create_rules( | 465 | let rules = create_rules( |
422 | " | 466 | " |
@@ -597,7 +641,7 @@ MACRO_ITEMS@[0; 40) | |||
597 | assert_expansion( | 641 | assert_expansion( |
598 | &rules, | 642 | &rules, |
599 | "foo! { bar::<u8>::baz::<u8> }", | 643 | "foo! { bar::<u8>::baz::<u8> }", |
600 | "fn foo () {let a = bar :: < u8 > :: baz :: < u8 > ;}", | 644 | "fn foo () {let a = bar ::< u8 >:: baz ::< u8 > ;}", |
601 | ); | 645 | ); |
602 | } | 646 | } |
603 | 647 | ||
@@ -891,7 +935,7 @@ MACRO_ITEMS@[0; 40) | |||
891 | } | 935 | } |
892 | "#, | 936 | "#, |
893 | ); | 937 | ); |
894 | assert_expansion(&rules, r#"foo!{'a}"#, r#"struct Ref < 'a > {s : & 'a str}"#); | 938 | assert_expansion(&rules, r#"foo!{'a}"#, r#"struct Ref <'a > {s : &'a str}"#); |
895 | } | 939 | } |
896 | 940 | ||
897 | #[test] | 941 | #[test] |
@@ -1063,7 +1107,165 @@ macro_rules! int_base { | |||
1063 | ); | 1107 | ); |
1064 | 1108 | ||
1065 | assert_expansion(&rules, r#" int_base!{Binary for isize as usize -> Binary}"#, | 1109 | assert_expansion(&rules, r#" int_base!{Binary for isize as usize -> Binary}"#, |
1066 | "# [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)}}" | 1110 | "# [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)}}" |
1111 | ); | ||
1112 | } | ||
1113 | |||
1114 | #[test] | ||
1115 | fn test_generate_pattern_iterators() { | ||
1116 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs | ||
1117 | let rules = create_rules( | ||
1118 | r#" | ||
1119 | macro_rules! generate_pattern_iterators { | ||
1120 | { double ended; with $(#[$common_stability_attribute:meta])*, | ||
1121 | $forward_iterator:ident, | ||
1122 | $reverse_iterator:ident, $iterty:ty | ||
1123 | } => { | ||
1124 | fn foo(){} | ||
1125 | } | ||
1126 | } | ||
1127 | "#, | ||
1128 | ); | ||
1129 | |||
1130 | assert_expansion(&rules, r#"generate_pattern_iterators ! ( double ended ; with # [ stable ( feature = "rust1" , since = "1.0.0" ) ] , Split , RSplit , & 'a str )"#, | ||
1131 | "fn foo () {}"); | ||
1132 | } | ||
1133 | |||
1134 | #[test] | ||
1135 | fn test_impl_fn_for_zst() { | ||
1136 | // from https://github.com/rust-lang/rust/blob/5d20ff4d2718c820632b38c1e49d4de648a9810b/src/libcore/internal_macros.rs | ||
1137 | let rules = create_rules( | ||
1138 | r#" | ||
1139 | macro_rules! impl_fn_for_zst { | ||
1140 | { $( $( #[$attr: meta] )* | ||
1141 | struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = | ||
1142 | |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty | ||
1143 | $body: block; )+ | ||
1144 | } => { | ||
1145 | $( | ||
1146 | $( #[$attr] )* | ||
1147 | struct $Name; | ||
1148 | |||
1149 | impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { | ||
1150 | #[inline] | ||
1151 | extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1152 | $body | ||
1153 | } | ||
1154 | } | ||
1155 | |||
1156 | impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { | ||
1157 | #[inline] | ||
1158 | extern "rust-call" fn call_mut( | ||
1159 | &mut self, | ||
1160 | ($( $arg, )*): ($( $ArgTy, )*) | ||
1161 | ) -> $ReturnTy { | ||
1162 | Fn::call(&*self, ($( $arg, )*)) | ||
1163 | } | ||
1164 | } | ||
1165 | |||
1166 | impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { | ||
1167 | type Output = $ReturnTy; | ||
1168 | |||
1169 | #[inline] | ||
1170 | extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1171 | Fn::call(&self, ($( $arg, )*)) | ||
1172 | } | ||
1173 | } | ||
1174 | )+ | ||
1175 | } | ||
1176 | } | ||
1177 | } | ||
1178 | "#, | ||
1179 | ); | ||
1180 | |||
1181 | assert_expansion(&rules, r#" | ||
1182 | impl_fn_for_zst ! { | ||
1183 | # [ derive ( Clone ) ] | ||
1184 | struct CharEscapeDebugContinue impl Fn = | c : char | -> char :: EscapeDebug { | ||
1185 | c . escape_debug_ext ( false ) | ||
1186 | } ; | ||
1187 | |||
1188 | # [ derive ( Clone ) ] | ||
1189 | struct CharEscapeUnicode impl Fn = | c : char | -> char :: EscapeUnicode { | ||
1190 | c . escape_unicode ( ) | ||
1191 | } ; | ||
1192 | # [ derive ( Clone ) ] | ||
1193 | struct CharEscapeDefault impl Fn = | c : char | -> char :: EscapeDefault { | ||
1194 | c . escape_default ( ) | ||
1195 | } ; | ||
1196 | } | ||
1197 | "#, | ||
1198 | "# [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 ,))}}"); | ||
1199 | } | ||
1200 | |||
1201 | #[test] | ||
1202 | fn test_impl_nonzero_fmt() { | ||
1203 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/num/mod.rs#L12 | ||
1204 | let rules = create_rules( | ||
1205 | r#" | ||
1206 | macro_rules! impl_nonzero_fmt { | ||
1207 | ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { | ||
1208 | fn foo() {} | ||
1209 | } | ||
1210 | } | ||
1211 | "#, | ||
1067 | ); | 1212 | ); |
1213 | |||
1214 | assert_expansion(&rules, r#"impl_nonzero_fmt ! { # [ stable ( feature = "nonzero" , since = "1.28.0" ) ] ( Debug , Display , Binary , Octal , LowerHex , UpperHex ) for NonZeroU8 }"#, | ||
1215 | "fn foo () {}"); | ||
1216 | } | ||
1217 | |||
1218 | #[test] | ||
1219 | fn test_cfg_if_items() { | ||
1220 | // from https://github.com/rust-lang/rust/blob/33fe1131cadba69d317156847be9a402b89f11bb/src/libstd/macros.rs#L986 | ||
1221 | let rules = create_rules( | ||
1222 | r#" | ||
1223 | macro_rules! __cfg_if_items { | ||
1224 | (($($not:meta,)*) ; ) => {}; | ||
1225 | (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { | ||
1226 | __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } | ||
1227 | } | ||
1228 | } | ||
1229 | "#, | ||
1230 | ); | ||
1231 | |||
1232 | assert_expansion(&rules, r#"__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 ; ) ) , }"#, | ||
1233 | "__cfg_if_items ! {(rustdoc , ) ; }"); | ||
1234 | } | ||
1235 | |||
1236 | #[test] | ||
1237 | fn test_cfg_if_main() { | ||
1238 | // from https://github.com/rust-lang/rust/blob/3d211248393686e0f73851fc7548f6605220fbe1/src/libpanic_unwind/macros.rs#L9 | ||
1239 | let rules = create_rules( | ||
1240 | r#" | ||
1241 | macro_rules! cfg_if { | ||
1242 | ($( | ||
1243 | if #[cfg($($meta:meta),*)] { $($it:item)* } | ||
1244 | ) else * else { | ||
1245 | $($it2:item)* | ||
1246 | }) => { | ||
1247 | __cfg_if_items! { | ||
1248 | () ; | ||
1249 | $( ( ($($meta),*) ($($it)*) ), )* | ||
1250 | ( () ($($it2)*) ), | ||
1251 | } | ||
1252 | } | ||
1253 | } | ||
1254 | "#, | ||
1255 | ); | ||
1256 | |||
1257 | assert_expansion(&rules, r#" | ||
1258 | cfg_if ! { | ||
1259 | if # [ cfg ( target_env = "msvc" ) ] { | ||
1260 | // no extra unwinder support needed | ||
1261 | } else if # [ cfg ( all ( target_arch = "wasm32" , not ( target_os = "emscripten" ) ) ) ] { | ||
1262 | // no unwinder on the system! | ||
1263 | } else { | ||
1264 | mod libunwind ; | ||
1265 | pub use libunwind :: * ; | ||
1266 | } | ||
1267 | } | ||
1268 | "#, | ||
1269 | "__cfg_if_items ! {() ; (() (mod libunwind ; pub use libunwind :: * ;)) ,}"); | ||
1068 | } | 1270 | } |
1069 | } | 1271 | } |