Skip to content

Commit c3a8632

Browse files
authored
Merge pull request #2672 from Speedy37/master
fix #2520: change expand_repeat loop stop condition
2 parents e4d2170 + dc98930 commit c3a8632

File tree

2 files changed

+83
-37
lines changed

2 files changed

+83
-37
lines changed

crates/ra_mbe/src/mbe_expander/transcriber.rs

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,24 @@ impl Bindings {
1414
self.inner.contains_key(name)
1515
}
1616

17-
fn get(&self, name: &str, nesting: &[usize]) -> Result<&Fragment, ExpandError> {
17+
fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
1818
let mut b = self.inner.get(name).ok_or_else(|| {
1919
ExpandError::BindingError(format!("could not find binding `{}`", name))
2020
})?;
21-
for &idx in nesting.iter() {
21+
for nesting_state in nesting.iter_mut() {
22+
nesting_state.hit = true;
2223
b = match b {
2324
Binding::Fragment(_) => break,
24-
Binding::Nested(bs) => bs.get(idx).ok_or_else(|| {
25+
Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
26+
nesting_state.at_end = true;
2527
ExpandError::BindingError(format!("could not find nested binding `{}`", name))
2628
})?,
2729
Binding::Empty => {
30+
nesting_state.at_end = true;
2831
return Err(ExpandError::BindingError(format!(
2932
"could not find empty binding `{}`",
3033
name
31-
)))
34+
)));
3235
}
3336
};
3437
}
@@ -51,15 +54,25 @@ pub(super) fn transcribe(
5154
bindings: &Bindings,
5255
) -> Result<tt::Subtree, ExpandError> {
5356
assert!(template.delimiter == None);
54-
let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new(), var_expanded: false };
57+
let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() };
5558
expand_subtree(&mut ctx, template)
5659
}
5760

61+
#[derive(Debug)]
62+
struct NestingState {
63+
idx: usize,
64+
/// `hit` is currently necessary to tell `expand_repeat` if it should stop
65+
/// because there is no variable in use by the current repetition
66+
hit: bool,
67+
/// `at_end` is currently necessary to tell `expand_repeat` if it should stop
68+
/// because there is no more value avaible for the current repetition
69+
at_end: bool,
70+
}
71+
5872
#[derive(Debug)]
5973
struct ExpandCtx<'a> {
6074
bindings: &'a Bindings,
61-
nesting: Vec<usize>,
62-
var_expanded: bool,
75+
nesting: Vec<NestingState>,
6376
}
6477

6578
fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> Result<tt::Subtree, ExpandError> {
@@ -121,9 +134,7 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> Result<Fragment, ExpandError>
121134
.into();
122135
Fragment::Tokens(tt)
123136
} else {
124-
let fragment = ctx.bindings.get(&v, &ctx.nesting)?.clone();
125-
ctx.var_expanded = true;
126-
fragment
137+
ctx.bindings.get(&v, &mut ctx.nesting)?.clone()
127138
};
128139
Ok(res)
129140
}
@@ -135,37 +146,24 @@ fn expand_repeat(
135146
separator: Option<Separator>,
136147
) -> Result<Fragment, ExpandError> {
137148
let mut buf: Vec<tt::TokenTree> = Vec::new();
138-
ctx.nesting.push(0);
149+
ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false });
139150
// Dirty hack to make macro-expansion terminate.
140151
// This should be replaced by a propper macro-by-example implementation
141-
let mut limit = 65536;
152+
let limit = 65536;
142153
let mut has_seps = 0;
143154
let mut counter = 0;
144155

145-
// We store the old var expanded value, and restore it later
146-
// It is because before this `$repeat`,
147-
// it is possible some variables already expanad in the same subtree
148-
//
149-
// `some_var_expanded` keep check if the deeper subtree has expanded variables
150-
let mut some_var_expanded = false;
151-
let old_var_expanded = ctx.var_expanded;
152-
ctx.var_expanded = false;
153-
154-
while let Ok(mut t) = expand_subtree(ctx, template) {
155-
t.delimiter = None;
156-
// if no var expanded in the child, we count it as a fail
157-
if !ctx.var_expanded {
156+
loop {
157+
let res = expand_subtree(ctx, template);
158+
let nesting_state = ctx.nesting.last_mut().unwrap();
159+
if nesting_state.at_end || !nesting_state.hit {
158160
break;
159161
}
160-
161-
// Reset `ctx.var_expandeded` to see if there is other expanded variable
162-
// in the next matching
163-
some_var_expanded = true;
164-
ctx.var_expanded = false;
162+
nesting_state.idx += 1;
163+
nesting_state.hit = false;
165164

166165
counter += 1;
167-
limit -= 1;
168-
if limit == 0 {
166+
if counter == limit {
169167
log::warn!(
170168
"expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
171169
template,
@@ -174,8 +172,11 @@ fn expand_repeat(
174172
break;
175173
}
176174

177-
let idx = ctx.nesting.pop().unwrap();
178-
ctx.nesting.push(idx + 1);
175+
let mut t = match res {
176+
Ok(t) => t,
177+
Err(_) => continue,
178+
};
179+
t.delimiter = None;
179180
push_subtree(&mut buf, t);
180181

181182
if let Some(ref sep) = separator {
@@ -203,9 +204,6 @@ fn expand_repeat(
203204
}
204205
}
205206

206-
// Restore the `var_expanded` by combining old one and the new one
207-
ctx.var_expanded = some_var_expanded || old_var_expanded;
208-
209207
ctx.nesting.pop().unwrap();
210208
for _ in 0..has_seps {
211209
buf.pop();

crates/ra_mbe/src/tests.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,3 +1491,51 @@ fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String {
14911491

14921492
buf
14931493
}
1494+
1495+
#[test]
1496+
fn test_issue_2520() {
1497+
let macro_fixture = parse_macro(
1498+
r#"
1499+
macro_rules! my_macro {
1500+
{
1501+
( $(
1502+
$( [] $sname:ident : $stype:ty )?
1503+
$( [$expr:expr] $nname:ident : $ntype:ty )?
1504+
),* )
1505+
} => {
1506+
Test {
1507+
$(
1508+
$( $sname, )?
1509+
)*
1510+
}
1511+
};
1512+
}
1513+
"#,
1514+
);
1515+
1516+
macro_fixture.assert_expand_items(
1517+
r#"my_macro ! {
1518+
([] p1 : u32 , [|_| S0K0] s : S0K0 , [] k0 : i32)
1519+
}"#,
1520+
"Test {p1 , k0 ,}",
1521+
);
1522+
}
1523+
1524+
#[test]
1525+
fn test_repeat_bad_var() {
1526+
// FIXME: the second rule of the macro should be removed and an error about
1527+
// `$( $c )+` raised
1528+
parse_macro(
1529+
r#"
1530+
macro_rules! foo {
1531+
($( $b:ident )+) => {
1532+
$( $c )+
1533+
};
1534+
($( $b:ident )+) => {
1535+
$( $b )+
1536+
}
1537+
}
1538+
"#,
1539+
)
1540+
.assert_expand_items("foo!(b0 b1);", "b0 b1");
1541+
}

0 commit comments

Comments
 (0)