diff options
-rw-r--r-- | crates/ra_mbe/src/mbe_expander/transcriber.rs | 72 | ||||
-rw-r--r-- | crates/ra_mbe/src/tests.rs | 48 |
2 files changed, 83 insertions, 37 deletions
diff --git a/crates/ra_mbe/src/mbe_expander/transcriber.rs b/crates/ra_mbe/src/mbe_expander/transcriber.rs index eda66cd50..7662020f3 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 nesting_state in nesting.iter_mut() { |
22 | nesting_state.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(nesting_state.idx).ok_or_else(|| { |
26 | nesting_state.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 | nesting_state.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,25 @@ 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)] |
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 | |||
72 | #[derive(Debug)] | ||
59 | struct ExpandCtx<'a> { | 73 | struct ExpandCtx<'a> { |
60 | bindings: &'a Bindings, | 74 | bindings: &'a Bindings, |
61 | nesting: Vec<usize>, | 75 | nesting: Vec<NestingState>, |
62 | var_expanded: bool, | ||
63 | } | 76 | } |
64 | 77 | ||
65 | fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> Result<tt::Subtree, ExpandError> { | 78 | 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> | |||
121 | .into(); | 134 | .into(); |
122 | Fragment::Tokens(tt) | 135 | Fragment::Tokens(tt) |
123 | } else { | 136 | } else { |
124 | let fragment = ctx.bindings.get(&v, &ctx.nesting)?.clone(); | 137 | ctx.bindings.get(&v, &mut ctx.nesting)?.clone() |
125 | ctx.var_expanded = true; | ||
126 | fragment | ||
127 | }; | 138 | }; |
128 | Ok(res) | 139 | Ok(res) |
129 | } | 140 | } |
@@ -135,37 +146,24 @@ fn expand_repeat( | |||
135 | separator: Option<Separator>, | 146 | separator: Option<Separator>, |
136 | ) -> Result<Fragment, ExpandError> { | 147 | ) -> Result<Fragment, ExpandError> { |
137 | let mut buf: Vec<tt::TokenTree> = Vec::new(); | 148 | 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 }); |
139 | // Dirty hack to make macro-expansion terminate. | 150 | // Dirty hack to make macro-expansion terminate. |
140 | // This should be replaced by a propper macro-by-example implementation | 151 | // This should be replaced by a propper macro-by-example implementation |
141 | let mut limit = 65536; | 152 | let limit = 65536; |
142 | let mut has_seps = 0; | 153 | let mut has_seps = 0; |
143 | let mut counter = 0; | 154 | let mut counter = 0; |
144 | 155 | ||
145 | // We store the old var expanded value, and restore it later | 156 | loop { |
146 | // It is because before this `$repeat`, | 157 | let res = expand_subtree(ctx, template); |
147 | // it is possible some variables already expanad in the same subtree | 158 | let nesting_state = ctx.nesting.last_mut().unwrap(); |
148 | // | 159 | 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; | 160 | break; |
159 | } | 161 | } |
160 | 162 | nesting_state.idx += 1; | |
161 | // Reset `ctx.var_expandeded` to see if there is other expanded variable | 163 | nesting_state.hit = false; |
162 | // in the next matching | ||
163 | some_var_expanded = true; | ||
164 | ctx.var_expanded = false; | ||
165 | 164 | ||
166 | counter += 1; | 165 | counter += 1; |
167 | limit -= 1; | 166 | if counter == limit { |
168 | if limit == 0 { | ||
169 | log::warn!( | 167 | log::warn!( |
170 | "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}", | 168 | "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}", |
171 | template, | 169 | template, |
@@ -174,8 +172,11 @@ fn expand_repeat( | |||
174 | break; | 172 | break; |
175 | } | 173 | } |
176 | 174 | ||
177 | let idx = ctx.nesting.pop().unwrap(); | 175 | let mut t = match res { |
178 | ctx.nesting.push(idx + 1); | 176 | Ok(t) => t, |
177 | Err(_) => continue, | ||
178 | }; | ||
179 | t.delimiter = None; | ||
179 | push_subtree(&mut buf, t); | 180 | push_subtree(&mut buf, t); |
180 | 181 | ||
181 | if let Some(ref sep) = separator { | 182 | if let Some(ref sep) = separator { |
@@ -203,9 +204,6 @@ fn expand_repeat( | |||
203 | } | 204 | } |
204 | } | 205 | } |
205 | 206 | ||
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(); | 207 | ctx.nesting.pop().unwrap(); |
210 | for _ in 0..has_seps { | 208 | for _ in 0..has_seps { |
211 | buf.pop(); | 209 | buf.pop(); |
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index e640d115b..e0d689704 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -1491,3 +1491,51 @@ fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String { | |||
1491 | 1491 | ||
1492 | buf | 1492 | buf |
1493 | } | 1493 | } |
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 | } | ||