Skip to content

Commit c55a2db

Browse files
committed
Fix more bugs
1 parent 299d97b commit c55a2db

File tree

8 files changed

+203
-24
lines changed

8 files changed

+203
-24
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_mbe/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ tt = { path = "../ra_tt", package = "ra_tt" }
1111
itertools = "0.8.0"
1212
rustc-hash = "1.0.0"
1313
smallvec = "0.6.9"
14+
log = "0.4.5"

crates/ra_mbe/src/lib.rs

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,30 @@ impl_froms!(TokenTree: Leaf, Subtree);
436436
assert_expansion(&rules, "foo! (fn baz {true true} )", "fn baz () -> bool {true &&true}");
437437
}
438438

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+
439463
#[test]
440464
fn test_expand_to_item_list() {
441465
let rules = create_rules(
@@ -1118,7 +1142,37 @@ macro_rules! impl_fn_for_zst {
11181142
|$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty
11191143
$body: block; )+
11201144
} => {
1121-
fn foo(){}
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+
}
11221176
}
11231177
}
11241178
"#,
@@ -1141,7 +1195,7 @@ impl_fn_for_zst ! {
11411195
} ;
11421196
}
11431197
"#,
1144-
"fn foo () {}");
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 ,))}}");
11451199
}
11461200

11471201
#[test]
@@ -1160,4 +1214,58 @@ impl_fn_for_zst ! {
11601214
assert_expansion(&rules, r#"impl_nonzero_fmt ! { # [ stable ( feature = "nonzero" , since = "1.28.0" ) ] ( Debug , Display , Binary , Octal , LowerHex , UpperHex ) for NonZeroU8 }"#,
11611215
"fn foo () {}");
11621216
}
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 :: * ;)) ,}");
1270+
}
11631271
}

crates/ra_mbe/src/mbe_expander.rs

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ fn expand_rule(rule: &crate::Rule, input: &tt::Subtree) -> Result<tt::Subtree, E
2121
if !input.is_eof() {
2222
return Err(ExpandError::UnexpectedToken);
2323
}
24-
expand_subtree(&rule.rhs, &bindings, &mut Vec::new())
24+
25+
let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new(), var_expanded: false };
26+
27+
expand_subtree(&rule.rhs, &mut ctx)
2528
}
2629

2730
/// The actual algorithm for expansion is not too hard, but is pretty tricky.
@@ -225,7 +228,7 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
225228
crate::TokenTree::Repeat(crate::Repeat { subtree, kind, separator }) => {
226229
// Dirty hack to make macro-expansion terminate.
227230
// This should be replaced by a propper macro-by-example implementation
228-
let mut limit = 128;
231+
let mut limit = 65536;
229232
let mut counter = 0;
230233

231234
let mut memento = input.save();
@@ -236,6 +239,7 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
236239
counter += 1;
237240
limit -= 1;
238241
if limit == 0 {
242+
log::warn!("match_lhs excced in repeat pattern exceed limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", subtree, input, kind, separator);
239243
break;
240244
}
241245

@@ -303,15 +307,21 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
303307
Ok(res)
304308
}
305309

310+
#[derive(Debug)]
311+
struct ExpandCtx<'a> {
312+
bindings: &'a Bindings,
313+
nesting: Vec<usize>,
314+
var_expanded: bool,
315+
}
316+
306317
fn expand_subtree(
307318
template: &crate::Subtree,
308-
bindings: &Bindings,
309-
nesting: &mut Vec<usize>,
319+
ctx: &mut ExpandCtx,
310320
) -> Result<tt::Subtree, ExpandError> {
311321
let token_trees = template
312322
.token_trees
313323
.iter()
314-
.map(|it| expand_tt(it, bindings, nesting))
324+
.map(|it| expand_tt(it, ctx))
315325
.collect::<Result<Vec<_>, ExpandError>>()?;
316326

317327
Ok(tt::Subtree { token_trees, delimiter: template.delimiter })
@@ -333,26 +343,43 @@ fn reduce_single_token(mut subtree: tt::Subtree) -> tt::TokenTree {
333343

334344
fn expand_tt(
335345
template: &crate::TokenTree,
336-
bindings: &Bindings,
337-
nesting: &mut Vec<usize>,
346+
ctx: &mut ExpandCtx,
338347
) -> Result<tt::TokenTree, ExpandError> {
339348
let res: tt::TokenTree = match template {
340-
crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, bindings, nesting)?.into(),
349+
crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, ctx)?.into(),
341350
crate::TokenTree::Repeat(repeat) => {
342351
let mut token_trees: Vec<tt::TokenTree> = Vec::new();
343-
nesting.push(0);
352+
ctx.nesting.push(0);
344353
// Dirty hack to make macro-expansion terminate.
345354
// This should be replaced by a propper macro-by-example implementation
346-
let mut limit = 128;
355+
let mut limit = 65536;
347356
let mut has_seps = 0;
357+
let mut counter = 0;
358+
359+
let mut some_var_expanded = false;
360+
ctx.var_expanded = false;
361+
362+
while let Ok(t) = expand_subtree(&repeat.subtree, ctx) {
363+
// if no var expaned in the child, we count it as a fail
364+
if !ctx.var_expanded {
365+
break;
366+
}
367+
some_var_expanded = true;
368+
ctx.var_expanded = false;
348369

349-
while let Ok(t) = expand_subtree(&repeat.subtree, bindings, nesting) {
370+
counter += 1;
350371
limit -= 1;
351372
if limit == 0 {
373+
log::warn!(
374+
"expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
375+
template,
376+
ctx
377+
);
352378
break;
353379
}
354-
let idx = nesting.pop().unwrap();
355-
nesting.push(idx + 1);
380+
381+
let idx = ctx.nesting.pop().unwrap();
382+
ctx.nesting.push(idx + 1);
356383
token_trees.push(reduce_single_token(t).into());
357384

358385
if let Some(ref sep) = repeat.separator {
@@ -374,12 +401,23 @@ fn expand_tt(
374401
}
375402
}
376403
}
404+
405+
if let crate::RepeatKind::ZeroOrOne = repeat.kind {
406+
break;
407+
}
377408
}
378-
nesting.pop().unwrap();
409+
410+
ctx.var_expanded = some_var_expanded;
411+
412+
ctx.nesting.pop().unwrap();
379413
for _ in 0..has_seps {
380414
token_trees.pop();
381415
}
382416

417+
if crate::RepeatKind::OneOrMore == repeat.kind && counter == 0 {
418+
return Err(ExpandError::UnexpectedToken);
419+
}
420+
383421
// Check if it is a singel token subtree without any delimiter
384422
// e.g {Delimiter:None> ['>'] /Delimiter:None>}
385423
reduce_single_token(tt::Subtree { token_trees, delimiter: tt::Delimiter::None })
@@ -396,7 +434,8 @@ fn expand_tt(
396434
tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() })
397435
.into()
398436
} else {
399-
let tkn = bindings.get(&v.text, nesting)?.clone();
437+
let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone();
438+
ctx.var_expanded = true;
400439

401440
if let tt::TokenTree::Subtree(subtree) = tkn {
402441
reduce_single_token(subtree)

crates/ra_mbe/src/subtree_source.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ impl<'a> SubTreeWalker<'a> {
212212
}
213213

214214
pub(crate) trait Querier {
215-
fn token(&self, uidx: usize) -> (SyntaxKind, SmolStr);
215+
fn token(&self, uidx: usize) -> (SyntaxKind, SmolStr, bool);
216216
}
217217

218218
// A wrapper class for ref cell
@@ -292,9 +292,10 @@ impl<'a> WalkerOwner<'a> {
292292
}
293293

294294
impl<'a> Querier for WalkerOwner<'a> {
295-
fn token(&self, uidx: usize) -> (SyntaxKind, SmolStr) {
296-
let tkn = self.get(uidx).unwrap();
297-
(tkn.kind, tkn.text)
295+
fn token(&self, uidx: usize) -> (SyntaxKind, SmolStr, bool) {
296+
self.get(uidx)
297+
.map(|tkn| (tkn.kind, tkn.text, tkn.is_joint_to_next))
298+
.unwrap_or_else(|| (SyntaxKind::EOF, "".into(), false))
298299
}
299300
}
300301

crates/ra_mbe/src/syntax_bridge.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ fn convert_tt(
123123
global_offset: TextUnit,
124124
tt: &SyntaxNode,
125125
) -> Option<tt::Subtree> {
126+
// This tree is empty
127+
if tt.first_child_or_token().is_none() {
128+
return Some(tt::Subtree { token_trees: vec![], delimiter: tt::Delimiter::None });
129+
}
130+
126131
let first_child = tt.first_child_or_token()?;
127132
let last_child = tt.last_child_or_token()?;
128133
let (delimiter, skip_first) = match (first_child.kind(), last_child.kind()) {
@@ -233,7 +238,16 @@ impl<'a, Q: Querier> TreeSink for TtTreeSink<'a, Q> {
233238
self.text_pos += TextUnit::of_str(&self.buf);
234239
let text = SmolStr::new(self.buf.as_str());
235240
self.buf.clear();
236-
self.inner.token(kind, text)
241+
self.inner.token(kind, text);
242+
243+
// // Add a white space to token
244+
// let (last_kind, _, last_joint_to_next ) = self.src_querier.token(self.token_pos-n_tokens as usize);
245+
// if !last_joint_to_next && last_kind.is_punct() {
246+
// let (cur_kind, _, _ ) = self.src_querier.token(self.token_pos);
247+
// if cur_kind.is_punct() {
248+
// self.inner.token(WHITESPACE, " ".into());
249+
// }
250+
// }
237251
}
238252

239253
fn start_node(&mut self, kind: SyntaxKind) {

crates/ra_mbe/src/tt_cursor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::subtree_parser::Parser;
33
use crate::subtree_source::TokenPeek;
44
use smallvec::{SmallVec, smallvec};
55

6-
#[derive(Clone)]
6+
#[derive(Debug, Clone)]
77
pub(crate) struct TtCursor<'a> {
88
subtree: &'a tt::Subtree,
99
pos: usize,

crates/ra_parser/src/grammar.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,22 @@ pub(crate) fn meta_item(p: &mut Parser) {
119119
items::token_tree(p);
120120
break;
121121
} else {
122-
p.bump();
122+
// https://doc.rust-lang.org/reference/attributes.html
123+
// https://doc.rust-lang.org/reference/paths.html#simple-paths
124+
// The start of an meta must be a simple path
125+
match p.current() {
126+
IDENT | COLONCOLON | SUPER_KW | SELF_KW | CRATE_KW => p.bump(),
127+
EQ => {
128+
p.bump();
129+
match p.current() {
130+
c if c.is_literal() => p.bump(),
131+
TRUE_KW | FALSE_KW => p.bump(),
132+
_ => {}
133+
}
134+
break;
135+
}
136+
_ => break,
137+
}
123138
}
124139
}
125140

0 commit comments

Comments
 (0)