|
1 | 1 | use crate::base::ExtCtxt;
|
2 |
| -use crate::mbe; |
3 | 2 | use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
|
4 |
| - |
| 3 | +use crate::mbe::{self, MetaVarExpr}; |
5 | 4 | use rustc_ast::mut_visit::{self, MutVisitor};
|
6 |
| -use rustc_ast::token::{self, NtTT, Token}; |
| 5 | +use rustc_ast::token::{self, NtTT, Token, TokenKind}; |
7 | 6 | use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
|
8 | 7 | use rustc_data_structures::fx::FxHashMap;
|
9 | 8 | use rustc_data_structures::sync::Lrc;
|
10 | 9 | use rustc_errors::{pluralize, PResult};
|
| 10 | +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; |
11 | 11 | use rustc_span::hygiene::{LocalExpnId, Transparency};
|
12 |
| -use rustc_span::symbol::MacroRulesNormalizedIdent; |
| 12 | +use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; |
13 | 13 | use rustc_span::Span;
|
14 | 14 |
|
15 | 15 | use smallvec::{smallvec, SmallVec};
|
@@ -411,13 +411,150 @@ fn lockstep_iter_size(
|
411 | 411 | }
|
412 | 412 | }
|
413 | 413 |
|
| 414 | +/// Used solely by the `count` meta-variable expression, counts the outer-most repetitions at a |
| 415 | +/// given optional nested depth. |
| 416 | +/// |
| 417 | +/// For example, a macro parameter of `$( { $( $foo:ident ),* } )*` called with `{ a, b } { c }`: |
| 418 | +/// |
| 419 | +/// * `[ $( ${count(foo)} ),* ]` will return [2, 1] with a, b = 2 and c = 1 |
| 420 | +/// * `[ $( ${count(foo, 0)} ),* ]` will be the same as `[ $( ${count(foo)} ),* ]` |
| 421 | +/// * `[ $( ${count(foo, 1)} ),* ]` will return an error because `${count(foo, 1)}` is |
| 422 | +/// declared inside a single repetition and the index `1` implies two nested repetitions. |
| 423 | +fn count_repetitions<'a>( |
| 424 | + cx: &ExtCtxt<'a>, |
| 425 | + depth_opt: Option<usize>, |
| 426 | + mut matched: &NamedMatch, |
| 427 | + repeats: &[(usize, usize)], |
| 428 | + sp: &DelimSpan, |
| 429 | +) -> PResult<'a, usize> { |
| 430 | + // Recursively count the number of matches in `matched` at given depth |
| 431 | + // (or at the top-level of `matched` if no depth is given). |
| 432 | + fn count<'a>( |
| 433 | + cx: &ExtCtxt<'a>, |
| 434 | + declared_lhs_depth: usize, |
| 435 | + depth_opt: Option<usize>, |
| 436 | + matched: &NamedMatch, |
| 437 | + sp: &DelimSpan, |
| 438 | + ) -> PResult<'a, usize> { |
| 439 | + match matched { |
| 440 | + MatchedNonterminal(_) => { |
| 441 | + if declared_lhs_depth == 0 { |
| 442 | + return Err(cx.struct_span_err( |
| 443 | + sp.entire(), |
| 444 | + "`count` can not be placed inside the inner-most repetition", |
| 445 | + )); |
| 446 | + } |
| 447 | + match depth_opt { |
| 448 | + None => Ok(1), |
| 449 | + Some(_) => Err(out_of_bounds_err(cx, declared_lhs_depth, sp.entire(), "count")), |
| 450 | + } |
| 451 | + } |
| 452 | + MatchedSeq(ref named_matches) => { |
| 453 | + let new_declared_lhs_depth = declared_lhs_depth + 1; |
| 454 | + match depth_opt { |
| 455 | + None => named_matches |
| 456 | + .iter() |
| 457 | + .map(|elem| count(cx, new_declared_lhs_depth, None, elem, sp)) |
| 458 | + .sum(), |
| 459 | + Some(0) => Ok(named_matches.len()), |
| 460 | + Some(depth) => named_matches |
| 461 | + .iter() |
| 462 | + .map(|elem| count(cx, new_declared_lhs_depth, Some(depth - 1), elem, sp)) |
| 463 | + .sum(), |
| 464 | + } |
| 465 | + } |
| 466 | + } |
| 467 | + } |
| 468 | + // `repeats` records all of the nested levels at which we are currently |
| 469 | + // matching meta-variables. The meta-var-expr `count($x)` only counts |
| 470 | + // matches that occur in this "subtree" of the `NamedMatch` where we |
| 471 | + // are currently transcribing, so we need to descend to that subtree |
| 472 | + // before we start counting. `matched` contains the various levels of the |
| 473 | + // tree as we descend, and its final value is the subtree we are currently at. |
| 474 | + for &(idx, _) in repeats { |
| 475 | + if let MatchedSeq(ref ads) = matched { |
| 476 | + matched = &ads[idx]; |
| 477 | + } |
| 478 | + } |
| 479 | + count(cx, 0, depth_opt, matched, sp) |
| 480 | +} |
| 481 | + |
| 482 | +/// Returns a `NamedMatch` item declared on the RHS given an arbitrary [Ident] |
| 483 | +fn matched_from_ident<'ctx, 'interp, 'rslt>( |
| 484 | + cx: &ExtCtxt<'ctx>, |
| 485 | + ident: Ident, |
| 486 | + interp: &'interp FxHashMap<MacroRulesNormalizedIdent, NamedMatch>, |
| 487 | +) -> PResult<'ctx, &'rslt NamedMatch> |
| 488 | +where |
| 489 | + 'interp: 'rslt, |
| 490 | +{ |
| 491 | + let span = ident.span; |
| 492 | + let key = MacroRulesNormalizedIdent::new(ident); |
| 493 | + interp.get(&key).ok_or_else(|| { |
| 494 | + cx.struct_span_err( |
| 495 | + span, |
| 496 | + &format!("variable `{}` is not recognized in meta-variable expression", key), |
| 497 | + ) |
| 498 | + }) |
| 499 | +} |
| 500 | + |
| 501 | +/// Used by meta-variable expressions when an user input is out of the actual declared bounds. For |
| 502 | +/// example, index(999999) in an repetition of only three elements. |
| 503 | +fn out_of_bounds_err<'a>( |
| 504 | + cx: &ExtCtxt<'a>, |
| 505 | + max: usize, |
| 506 | + span: Span, |
| 507 | + ty: &str, |
| 508 | +) -> DiagnosticBuilder<'a, ErrorGuaranteed> { |
| 509 | + cx.struct_span_err(span, &format!("{ty} depth must be less than {max}")) |
| 510 | +} |
| 511 | + |
414 | 512 | fn transcribe_metavar_expr<'a>(
|
415 |
| - _cx: &ExtCtxt<'a>, |
416 |
| - _expr: mbe::MetaVarExpr, |
417 |
| - _interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>, |
418 |
| - _repeats: &[(usize, usize)], |
419 |
| - _result: &mut Vec<TreeAndSpacing>, |
420 |
| - _sp: &DelimSpan, |
| 513 | + cx: &ExtCtxt<'a>, |
| 514 | + expr: MetaVarExpr, |
| 515 | + interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>, |
| 516 | + repeats: &[(usize, usize)], |
| 517 | + result: &mut Vec<TreeAndSpacing>, |
| 518 | + sp: &DelimSpan, |
421 | 519 | ) -> PResult<'a, ()> {
|
| 520 | + match expr { |
| 521 | + MetaVarExpr::Count(original_ident, depth_opt) => { |
| 522 | + let matched = matched_from_ident(cx, original_ident, interp)?; |
| 523 | + let count = count_repetitions(cx, depth_opt, matched, &repeats, sp)?; |
| 524 | + let tt = TokenTree::token( |
| 525 | + TokenKind::lit(token::Integer, sym::integer(count), None), |
| 526 | + sp.entire(), |
| 527 | + ); |
| 528 | + result.push(tt.into()); |
| 529 | + } |
| 530 | + MetaVarExpr::Ignore(original_ident) => { |
| 531 | + // Used to ensure that `original_ident` is present in the LHS |
| 532 | + let _ = matched_from_ident(cx, original_ident, interp)?; |
| 533 | + } |
| 534 | + MetaVarExpr::Index(depth) => match repeats.iter().nth_back(depth) { |
| 535 | + Some((index, _)) => { |
| 536 | + result.push( |
| 537 | + TokenTree::token( |
| 538 | + TokenKind::lit(token::Integer, sym::integer(*index), None), |
| 539 | + sp.entire(), |
| 540 | + ) |
| 541 | + .into(), |
| 542 | + ); |
| 543 | + } |
| 544 | + None => return Err(out_of_bounds_err(cx, repeats.len(), sp.entire(), "index")), |
| 545 | + }, |
| 546 | + MetaVarExpr::Length(depth) => match repeats.iter().nth_back(depth) { |
| 547 | + Some((_, length)) => { |
| 548 | + result.push( |
| 549 | + TokenTree::token( |
| 550 | + TokenKind::lit(token::Integer, sym::integer(*length), None), |
| 551 | + sp.entire(), |
| 552 | + ) |
| 553 | + .into(), |
| 554 | + ); |
| 555 | + } |
| 556 | + None => return Err(out_of_bounds_err(cx, repeats.len(), sp.entire(), "length")), |
| 557 | + }, |
| 558 | + } |
422 | 559 | Ok(())
|
423 | 560 | }
|
0 commit comments