diff options
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander.rs')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 69 |
1 files changed, 54 insertions, 15 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index 7411dd8b1..d5189b537 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -21,7 +21,10 @@ fn expand_rule(rule: &crate::Rule, input: &tt::Subtree) -> Result<tt::Subtree, E | |||
21 | if !input.is_eof() { | 21 | if !input.is_eof() { |
22 | return Err(ExpandError::UnexpectedToken); | 22 | return Err(ExpandError::UnexpectedToken); |
23 | } | 23 | } |
24 | expand_subtree(&rule.rhs, &bindings, &mut Vec::new()) | 24 | |
25 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new(), var_expanded: false }; | ||
26 | |||
27 | expand_subtree(&rule.rhs, &mut ctx) | ||
25 | } | 28 | } |
26 | 29 | ||
27 | /// The actual algorithm for expansion is not too hard, but is pretty tricky. | 30 | /// The actual algorithm for expansion is not too hard, but is pretty tricky. |
@@ -225,7 +228,7 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
225 | crate::TokenTree::Repeat(crate::Repeat { subtree, kind, separator }) => { | 228 | crate::TokenTree::Repeat(crate::Repeat { subtree, kind, separator }) => { |
226 | // Dirty hack to make macro-expansion terminate. | 229 | // Dirty hack to make macro-expansion terminate. |
227 | // This should be replaced by a propper macro-by-example implementation | 230 | // This should be replaced by a propper macro-by-example implementation |
228 | let mut limit = 128; | 231 | let mut limit = 65536; |
229 | let mut counter = 0; | 232 | let mut counter = 0; |
230 | 233 | ||
231 | let mut memento = input.save(); | 234 | let mut memento = input.save(); |
@@ -236,6 +239,7 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
236 | counter += 1; | 239 | counter += 1; |
237 | limit -= 1; | 240 | limit -= 1; |
238 | if limit == 0 { | 241 | if limit == 0 { |
242 | log::warn!("match_lhs excced in repeat pattern exceed limit => {:#?}\n{:#?}\n{:#?}\n{:#?}", subtree, input, kind, separator); | ||
239 | break; | 243 | break; |
240 | } | 244 | } |
241 | 245 | ||
@@ -303,15 +307,21 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
303 | Ok(res) | 307 | Ok(res) |
304 | } | 308 | } |
305 | 309 | ||
310 | #[derive(Debug)] | ||
311 | struct ExpandCtx<'a> { | ||
312 | bindings: &'a Bindings, | ||
313 | nesting: Vec<usize>, | ||
314 | var_expanded: bool, | ||
315 | } | ||
316 | |||
306 | fn expand_subtree( | 317 | fn expand_subtree( |
307 | template: &crate::Subtree, | 318 | template: &crate::Subtree, |
308 | bindings: &Bindings, | 319 | ctx: &mut ExpandCtx, |
309 | nesting: &mut Vec<usize>, | ||
310 | ) -> Result<tt::Subtree, ExpandError> { | 320 | ) -> Result<tt::Subtree, ExpandError> { |
311 | let token_trees = template | 321 | let token_trees = template |
312 | .token_trees | 322 | .token_trees |
313 | .iter() | 323 | .iter() |
314 | .map(|it| expand_tt(it, bindings, nesting)) | 324 | .map(|it| expand_tt(it, ctx)) |
315 | .collect::<Result<Vec<_>, ExpandError>>()?; | 325 | .collect::<Result<Vec<_>, ExpandError>>()?; |
316 | 326 | ||
317 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) | 327 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) |
@@ -333,26 +343,43 @@ fn reduce_single_token(mut subtree: tt::Subtree) -> tt::TokenTree { | |||
333 | 343 | ||
334 | fn expand_tt( | 344 | fn expand_tt( |
335 | template: &crate::TokenTree, | 345 | template: &crate::TokenTree, |
336 | bindings: &Bindings, | 346 | ctx: &mut ExpandCtx, |
337 | nesting: &mut Vec<usize>, | ||
338 | ) -> Result<tt::TokenTree, ExpandError> { | 347 | ) -> Result<tt::TokenTree, ExpandError> { |
339 | let res: tt::TokenTree = match template { | 348 | let res: tt::TokenTree = match template { |
340 | crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, bindings, nesting)?.into(), | 349 | crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, ctx)?.into(), |
341 | crate::TokenTree::Repeat(repeat) => { | 350 | crate::TokenTree::Repeat(repeat) => { |
342 | let mut token_trees: Vec<tt::TokenTree> = Vec::new(); | 351 | let mut token_trees: Vec<tt::TokenTree> = Vec::new(); |
343 | nesting.push(0); | 352 | ctx.nesting.push(0); |
344 | // Dirty hack to make macro-expansion terminate. | 353 | // Dirty hack to make macro-expansion terminate. |
345 | // This should be replaced by a propper macro-by-example implementation | 354 | // This should be replaced by a propper macro-by-example implementation |
346 | let mut limit = 128; | 355 | let mut limit = 65536; |
347 | let mut has_seps = 0; | 356 | let mut has_seps = 0; |
357 | let mut counter = 0; | ||
358 | |||
359 | let mut some_var_expanded = false; | ||
360 | ctx.var_expanded = false; | ||
361 | |||
362 | while let Ok(t) = expand_subtree(&repeat.subtree, ctx) { | ||
363 | // if no var expaned in the child, we count it as a fail | ||
364 | if !ctx.var_expanded { | ||
365 | break; | ||
366 | } | ||
367 | some_var_expanded = true; | ||
368 | ctx.var_expanded = false; | ||
348 | 369 | ||
349 | while let Ok(t) = expand_subtree(&repeat.subtree, bindings, nesting) { | 370 | counter += 1; |
350 | limit -= 1; | 371 | limit -= 1; |
351 | if limit == 0 { | 372 | if limit == 0 { |
373 | log::warn!( | ||
374 | "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}", | ||
375 | template, | ||
376 | ctx | ||
377 | ); | ||
352 | break; | 378 | break; |
353 | } | 379 | } |
354 | let idx = nesting.pop().unwrap(); | 380 | |
355 | nesting.push(idx + 1); | 381 | let idx = ctx.nesting.pop().unwrap(); |
382 | ctx.nesting.push(idx + 1); | ||
356 | token_trees.push(reduce_single_token(t).into()); | 383 | token_trees.push(reduce_single_token(t).into()); |
357 | 384 | ||
358 | if let Some(ref sep) = repeat.separator { | 385 | if let Some(ref sep) = repeat.separator { |
@@ -374,12 +401,23 @@ fn expand_tt( | |||
374 | } | 401 | } |
375 | } | 402 | } |
376 | } | 403 | } |
404 | |||
405 | if let crate::RepeatKind::ZeroOrOne = repeat.kind { | ||
406 | break; | ||
407 | } | ||
377 | } | 408 | } |
378 | nesting.pop().unwrap(); | 409 | |
410 | ctx.var_expanded = some_var_expanded; | ||
411 | |||
412 | ctx.nesting.pop().unwrap(); | ||
379 | for _ in 0..has_seps { | 413 | for _ in 0..has_seps { |
380 | token_trees.pop(); | 414 | token_trees.pop(); |
381 | } | 415 | } |
382 | 416 | ||
417 | if crate::RepeatKind::OneOrMore == repeat.kind && counter == 0 { | ||
418 | return Err(ExpandError::UnexpectedToken); | ||
419 | } | ||
420 | |||
383 | // Check if it is a singel token subtree without any delimiter | 421 | // Check if it is a singel token subtree without any delimiter |
384 | // e.g {Delimiter:None> ['>'] /Delimiter:None>} | 422 | // e.g {Delimiter:None> ['>'] /Delimiter:None>} |
385 | reduce_single_token(tt::Subtree { token_trees, delimiter: tt::Delimiter::None }) | 423 | reduce_single_token(tt::Subtree { token_trees, delimiter: tt::Delimiter::None }) |
@@ -396,7 +434,8 @@ fn expand_tt( | |||
396 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) | 434 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) |
397 | .into() | 435 | .into() |
398 | } else { | 436 | } else { |
399 | let tkn = bindings.get(&v.text, nesting)?.clone(); | 437 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); |
438 | ctx.var_expanded = true; | ||
400 | 439 | ||
401 | if let tt::TokenTree::Subtree(subtree) = tkn { | 440 | if let tt::TokenTree::Subtree(subtree) = tkn { |
402 | reduce_single_token(subtree) | 441 | reduce_single_token(subtree) |