diff options
Diffstat (limited to 'crates/ra_mbe')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander/transcriber.rs | 67 | ||||
-rw-r--r-- | crates/ra_mbe/src/tests.rs | 46 |
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)] |
62 | struct NestingState { | ||
63 | idx: usize, | ||
64 | hit: bool, | ||
65 | at_end: bool, | ||
66 | } | ||
67 | |||
68 | #[derive(Debug)] | ||
59 | struct ExpandCtx<'a> { | 69 | struct 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 | ||
65 | fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> Result<tt::Subtree, ExpandError> { | 74 | 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> | |||
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] | ||
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 | } | ||