Skip to content

Commit 42c4e0f

Browse files
Merge #1209
1209: Bugs fixes And Improvements of MBE r=matklad a=edwin0cheng This PR fixed / improve followings things: * Add `token` `$repeat` separator support: Previously $repeat only support single punct separator. * Fixed a bug which expand infinite pattern, see `test_match_group_in_group` * Correctly handle +,*,? case of $repeat patterns * Increase the limit of $repeat patterns (128 => 65536), personally i think we could remove this limit as we seem to fix all major loop bugs * **Re-enable tt matcher** * Better meta item parsing. * Add related tests and add some real world test cases. * Add more debug information. Co-authored-by: Edwin Cheng <[email protected]>
2 parents 5bbd9f4 + 1908819 commit 42c4e0f

File tree

10 files changed

+543
-87
lines changed

10 files changed

+543
-87
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_hir/src/ids.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,14 @@ pub struct MacroDefId(pub(crate) AstId<ast::MacroCall>);
128128
pub(crate) fn macro_def_query(db: &impl DefDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
129129
let macro_call = id.0.to_node(db);
130130
let arg = macro_call.token_tree()?;
131-
let (tt, _) = mbe::ast_to_token_tree(arg)?;
132-
let rules = MacroRules::parse(&tt).ok()?;
131+
let (tt, _) = mbe::ast_to_token_tree(arg).or_else(|| {
132+
log::warn!("fail on macro_def to token tree: {:#?}", arg);
133+
None
134+
})?;
135+
let rules = MacroRules::parse(&tt).ok().or_else(|| {
136+
log::warn!("fail on macro_def parse: {:#?}", tt);
137+
None
138+
})?;
133139
Some(Arc::new(rules))
134140
}
135141

crates/ra_mbe/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ ra_parser = { path = "../ra_parser" }
1010
tt = { path = "../ra_tt", package = "ra_tt" }
1111
itertools = "0.8.0"
1212
rustc-hash = "1.0.0"
13+
smallvec = "0.6.9"
14+
log = "0.4.5"

crates/ra_mbe/src/lib.rs

+209-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod subtree_source;
2424
mod subtree_parser;
2525

2626
use ra_syntax::SmolStr;
27+
use smallvec::SmallVec;
2728

2829
pub use tt::{Delimiter, Punct};
2930

@@ -98,11 +99,18 @@ pub(crate) struct Subtree {
9899
pub(crate) token_trees: Vec<TokenTree>,
99100
}
100101

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+
101109
#[derive(Clone, Debug, PartialEq, Eq)]
102110
pub(crate) struct Repeat {
103111
pub(crate) subtree: Subtree,
104112
pub(crate) kind: RepeatKind,
105-
pub(crate) separator: Option<char>,
113+
pub(crate) separator: Option<Separator>,
106114
}
107115

108116
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -175,8 +183,8 @@ impl_froms!(TokenTree: Leaf, Subtree);
175183
let expansion = rules.expand(&invocation_tt).unwrap();
176184
assert_eq!(
177185
expansion.to_string(),
178-
"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)}}"
186+
"impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \
187+
impl From <Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}"
180188
)
181189
}
182190

@@ -384,7 +392,7 @@ impl_froms!(TokenTree: Leaf, Subtree);
384392
"#,
385393
);
386394

387-
assert_expansion(&rules, "foo! { foo, bar }", "fn baz {foo () ; bar () ;}");
395+
assert_expansion(&rules, "foo! { foo, bar }", "fn baz {foo () ;bar ()}");
388396
}
389397

390398
#[test]
@@ -416,6 +424,42 @@ impl_froms!(TokenTree: Leaf, Subtree);
416424
assert_expansion(&rules, "foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}");
417425
}
418426

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+
419463
#[test]
420464
fn test_expand_to_item_list() {
421465
let rules = create_rules(
@@ -597,7 +641,7 @@ MACRO_ITEMS@[0; 40)
597641
assert_expansion(
598642
&rules,
599643
"foo! { bar::<u8>::baz::<u8> }",
600-
"fn foo () {let a = bar :: < u8 > :: baz :: < u8 > ;}",
644+
"fn foo () {let a = bar ::< u8 >:: baz ::< u8 > ;}",
601645
);
602646
}
603647

@@ -891,7 +935,7 @@ MACRO_ITEMS@[0; 40)
891935
}
892936
"#,
893937
);
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}"#);
895939
}
896940

897941
#[test]
@@ -1063,7 +1107,165 @@ macro_rules! int_base {
10631107
);
10641108

10651109
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+
"#,
10671212
);
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 :: * ;)) ,}");
10681270
}
10691271
}

0 commit comments

Comments
 (0)