Skip to content

Commit 6d84dee

Browse files
committed
fix #2520: change expand_repeat loop stop condition
1 parent c5a48be commit 6d84dee

File tree

2 files changed

+77
-36
lines changed

2 files changed

+77
-36
lines changed

crates/ra_mbe/src/mbe_expander/transcriber.rs

Lines changed: 31 additions & 36 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 s in nesting.iter_mut() {
22+
s.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(s.idx).ok_or_else(|| {
26+
s.at_end = true;
2527
ExpandError::BindingError(format!("could not find nested binding `{}`", name))
2628
})?,
2729
Binding::Empty => {
30+
s.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,21 @@ 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: bool,
65+
at_end: bool,
66+
}
67+
5868
#[derive(Debug)]
5969
struct ExpandCtx<'a> {
6070
bindings: &'a Bindings,
61-
nesting: Vec<usize>,
62-
var_expanded: bool,
71+
nesting: Vec<NestingState>,
6372
}
6473

6574
fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> Result<tt::Subtree, ExpandError> {
@@ -121,8 +130,7 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> Result<Fragment, ExpandError>
121130
.into();
122131
Fragment::Tokens(tt)
123132
} else {
124-
let fragment = ctx.bindings.get(&v, &ctx.nesting)?.clone();
125-
ctx.var_expanded = true;
133+
let fragment = ctx.bindings.get(&v, &mut ctx.nesting)?.clone();
126134
fragment
127135
};
128136
Ok(res)
@@ -135,37 +143,24 @@ fn expand_repeat(
135143
separator: Option<Separator>,
136144
) -> Result<Fragment, ExpandError> {
137145
let mut buf: Vec<tt::TokenTree> = Vec::new();
138-
ctx.nesting.push(0);
146+
ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false });
139147
// Dirty hack to make macro-expansion terminate.
140148
// This should be replaced by a propper macro-by-example implementation
141-
let mut limit = 65536;
149+
let limit = 65536;
142150
let mut has_seps = 0;
143151
let mut counter = 0;
144152

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 {
153+
loop {
154+
let res = expand_subtree(ctx, template);
155+
let nesting_state = ctx.nesting.last_mut().unwrap();
156+
if nesting_state.at_end || !nesting_state.hit {
158157
break;
159158
}
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;
159+
nesting_state.idx += 1;
160+
nesting_state.hit = false;
165161

166162
counter += 1;
167-
limit -= 1;
168-
if limit == 0 {
163+
if counter == limit {
169164
log::warn!(
170165
"expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
171166
template,
@@ -174,8 +169,11 @@ fn expand_repeat(
174169
break;
175170
}
176171

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

181179
if let Some(ref sep) = separator {
@@ -203,9 +201,6 @@ fn expand_repeat(
203201
}
204202
}
205203

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

crates/ra_mbe/src/tests.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,3 +1491,49 @@ 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+
parse_macro(
1527+
r#"
1528+
macro_rules! foo {
1529+
($( $b:ident )+) => {
1530+
$( $c )+
1531+
};
1532+
($( $b:ident )+) => {
1533+
$( $b )+
1534+
}
1535+
}
1536+
"#,
1537+
)
1538+
.assert_expand_items("foo!(b0 b1);", "b0 b1");
1539+
}

0 commit comments

Comments
 (0)