aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent RouillĂ© <[email protected]>2019-12-28 21:26:24 +0000
committerVincent RouillĂ© <[email protected]>2019-12-28 21:48:49 +0000
commit6d84dee4e7d70f329df71365f98b2421652b2432 (patch)
treee3c58befd8d46c5f994126313ca7daf41b82dd69
parentc5a48bea1218afb63d7932a6816f34c810bbab6b (diff)
fix #2520: change expand_repeat loop stop condition
-rw-r--r--crates/ra_mbe/src/mbe_expander/transcriber.rs67
-rw-r--r--crates/ra_mbe/src/tests.rs46
2 files changed, 77 insertions, 36 deletions
diff --git a/crates/ra_mbe/src/mbe_expander/transcriber.rs b/crates/ra_mbe/src/mbe_expander/transcriber.rs
index eda66cd50..8bb1a25a3 100644
--- a/crates/ra_mbe/src/mbe_expander/transcriber.rs
+++ b/crates/ra_mbe/src/mbe_expander/transcriber.rs
@@ -14,21 +14,24 @@ impl Bindings {
14 self.inner.contains_key(name) 14 self.inner.contains_key(name)
15 } 15 }
16 16
17 fn get(&self, name: &str, nesting: &[usize]) -> Result<&Fragment, ExpandError> { 17 fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
18 let mut b = self.inner.get(name).ok_or_else(|| { 18 let mut b = self.inner.get(name).ok_or_else(|| {
19 ExpandError::BindingError(format!("could not find binding `{}`", name)) 19 ExpandError::BindingError(format!("could not find binding `{}`", name))
20 })?; 20 })?;
21 for &idx in nesting.iter() { 21 for s in nesting.iter_mut() {
22 s.hit = true;
22 b = match b { 23 b = match b {
23 Binding::Fragment(_) => break, 24 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;
25 ExpandError::BindingError(format!("could not find nested binding `{}`", name)) 27 ExpandError::BindingError(format!("could not find nested binding `{}`", name))
26 })?, 28 })?,
27 Binding::Empty => { 29 Binding::Empty => {
30 s.at_end = true;
28 return Err(ExpandError::BindingError(format!( 31 return Err(ExpandError::BindingError(format!(
29 "could not find empty binding `{}`", 32 "could not find empty binding `{}`",
30 name 33 name
31 ))) 34 )));
32 } 35 }
33 }; 36 };
34 } 37 }
@@ -51,15 +54,21 @@ pub(super) fn transcribe(
51 bindings: &Bindings, 54 bindings: &Bindings,
52) -> Result<tt::Subtree, ExpandError> { 55) -> Result<tt::Subtree, ExpandError> {
53 assert!(template.delimiter == None); 56 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() };
55 expand_subtree(&mut ctx, template) 58 expand_subtree(&mut ctx, template)
56} 59}
57 60
58#[derive(Debug)] 61#[derive(Debug)]
62struct NestingState {
63 idx: usize,
64 hit: bool,
65 at_end: bool,
66}
67
68#[derive(Debug)]
59struct ExpandCtx<'a> { 69struct ExpandCtx<'a> {
60 bindings: &'a Bindings, 70 bindings: &'a Bindings,
61 nesting: Vec<usize>, 71 nesting: Vec<NestingState>,
62 var_expanded: bool,
63} 72}
64 73
65fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> Result<tt::Subtree, ExpandError> { 74fn 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>
121 .into(); 130 .into();
122 Fragment::Tokens(tt) 131 Fragment::Tokens(tt)
123 } else { 132 } else {
124 let fragment = ctx.bindings.get(&v, &ctx.nesting)?.clone(); 133 let fragment = ctx.bindings.get(&v, &mut ctx.nesting)?.clone();
125 ctx.var_expanded = true;
126 fragment 134 fragment
127 }; 135 };
128 Ok(res) 136 Ok(res)
@@ -135,37 +143,24 @@ fn expand_repeat(
135 separator: Option<Separator>, 143 separator: Option<Separator>,
136) -> Result<Fragment, ExpandError> { 144) -> Result<Fragment, ExpandError> {
137 let mut buf: Vec<tt::TokenTree> = Vec::new(); 145 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 });
139 // Dirty hack to make macro-expansion terminate. 147 // Dirty hack to make macro-expansion terminate.
140 // This should be replaced by a propper macro-by-example implementation 148 // This should be replaced by a propper macro-by-example implementation
141 let mut limit = 65536; 149 let limit = 65536;
142 let mut has_seps = 0; 150 let mut has_seps = 0;
143 let mut counter = 0; 151 let mut counter = 0;
144 152
145 // We store the old var expanded value, and restore it later 153 loop {
146 // It is because before this `$repeat`, 154 let res = expand_subtree(ctx, template);
147 // it is possible some variables already expanad in the same subtree 155 let nesting_state = ctx.nesting.last_mut().unwrap();
148 // 156 if nesting_state.at_end || !nesting_state.hit {
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 {
158 break; 157 break;
159 } 158 }
160 159 nesting_state.idx += 1;
161 // Reset `ctx.var_expandeded` to see if there is other expanded variable 160 nesting_state.hit = false;
162 // in the next matching
163 some_var_expanded = true;
164 ctx.var_expanded = false;
165 161
166 counter += 1; 162 counter += 1;
167 limit -= 1; 163 if counter == limit {
168 if limit == 0 {
169 log::warn!( 164 log::warn!(
170 "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}", 165 "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
171 template, 166 template,
@@ -174,8 +169,11 @@ fn expand_repeat(
174 break; 169 break;
175 } 170 }
176 171
177 let idx = ctx.nesting.pop().unwrap(); 172 let mut t = match res {
178 ctx.nesting.push(idx + 1); 173 Ok(t) => t,
174 Err(_) => continue,
175 };
176 t.delimiter = None;
179 push_subtree(&mut buf, t); 177 push_subtree(&mut buf, t);
180 178
181 if let Some(ref sep) = separator { 179 if let Some(ref sep) = separator {
@@ -203,9 +201,6 @@ fn expand_repeat(
203 } 201 }
204 } 202 }
205 203
206 // Restore the `var_expanded` by combining old one and the new one
207 ctx.var_expanded = some_var_expanded || old_var_expanded;
208
209 ctx.nesting.pop().unwrap(); 204 ctx.nesting.pop().unwrap();
210 for _ in 0..has_seps { 205 for _ in 0..has_seps {
211 buf.pop(); 206 buf.pop();
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index e640d115b..288366cca 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -1491,3 +1491,49 @@ fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String {
1491 1491
1492 buf 1492 buf
1493} 1493}
1494
1495#[test]
1496fn 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]
1525fn 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}