aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_mbe/src/mbe_expander/transcriber.rs72
-rw-r--r--crates/ra_mbe/src/tests.rs48
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)]
62struct 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)]
59struct ExpandCtx<'a> { 73struct 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
65fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> Result<tt::Subtree, ExpandError> { 78fn 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]
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 // 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}