Skip to content

Commit 3b30d7d

Browse files
authored
Rollup merge of rust-lang#94798 - nnethercote:parse_tt-refactor, r=petrochenkov
`parse_tt` refactorings Some readability improvements. r? `@petrochenkov`
2 parents 9f2986b + 95d13fa commit 3b30d7d

File tree

1 file changed

+128
-118
lines changed

1 file changed

+128
-118
lines changed

compiler/rustc_expand/src/mbe/macro_parser.rs

+128-118
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ impl<'tt> TokenTreeOrTokenTreeSlice<'tt> {
122122

123123
/// An unzipping of `TokenTree`s... see the `stack` field of `MatcherPos`.
124124
///
125-
/// This is used by `inner_parse_loop` to keep track of delimited submatchers that we have
125+
/// This is used by `parse_tt_inner` to keep track of delimited submatchers that we have
126126
/// descended into.
127127
#[derive(Clone)]
128128
struct MatcherTtFrame<'tt> {
@@ -439,9 +439,8 @@ fn nameize<I: Iterator<Item = NamedMatch>>(
439439
}
440440
Occupied(..) => return Err((sp, format!("duplicated bind name: {}", bind_name))),
441441
},
442-
// FIXME(c410-f3r) MetaVar and MetaVarExpr should be handled instead of being ignored
443-
// https://github.com/rust-lang/rust/issues/9390
444-
TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) | TokenTree::Token(..) => {}
442+
TokenTree::Token(..) => (),
443+
TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) => unreachable!(),
445444
}
446445

447446
Ok(())
@@ -481,21 +480,24 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool {
481480
/// successful execution of this function.
482481
/// - `next_items`: the set of newly generated items. These are used to replenish `cur_items` in
483482
/// the function `parse`.
484-
/// - `eof_items`: the set of items that would be valid if this was the EOF.
485483
/// - `bb_items`: the set of items that are waiting for the black-box parser.
486484
/// - `token`: the current token of the parser.
487485
///
488486
/// # Returns
489487
///
490-
/// A `ParseResult`. Note that matches are kept track of through the items generated.
491-
fn inner_parse_loop<'root, 'tt>(
488+
/// `Some(result)` if everything is finished, `None` otherwise. Note that matches are kept track of
489+
/// through the items generated.
490+
fn parse_tt_inner<'root, 'tt>(
492491
sess: &ParseSess,
492+
ms: &[TokenTree],
493493
cur_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>,
494-
next_items: &mut Vec<MatcherPosHandle<'root, 'tt>>,
494+
next_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>,
495495
bb_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>,
496-
eof_items: &mut EofItems<'root, 'tt>,
497496
token: &Token,
498-
) -> Result<(), (rustc_span::Span, String)> {
497+
) -> Option<NamedParseResult> {
498+
// Matcher positions that would be valid if the macro invocation was over now
499+
let mut eof_items = EofItems::None;
500+
499501
// Pop items from `cur_items` until it is empty.
500502
while let Some(mut item) = cur_items.pop() {
501503
// When unzipped trees end, remove them. This corresponds to backtracking out of a
@@ -522,6 +524,8 @@ fn inner_parse_loop<'root, 'tt>(
522524
// then we could be at the end of a sequence or at the beginning of the next
523525
// repetition.
524526
if let Some(repetition) = &item.repetition {
527+
debug_assert!(matches!(item.top_elts, Tt(TokenTree::Sequence(..))));
528+
525529
// At this point, regardless of whether there is a separator, we should add all
526530
// matches from the complete repetition of the sequence to the shared, top-level
527531
// `matches` list (actually, `up.matches`, which could itself not be the top-level,
@@ -565,7 +569,7 @@ fn inner_parse_loop<'root, 'tt>(
565569
} else {
566570
// If we are not in a repetition, then being at the end of a matcher means that we
567571
// have reached the potential end of the input.
568-
*eof_items = match eof_items {
572+
eof_items = match eof_items {
569573
EofItems::None => EofItems::One(item),
570574
EofItems::One(_) | EofItems::Multiple => EofItems::Multiple,
571575
}
@@ -613,7 +617,7 @@ fn inner_parse_loop<'root, 'tt>(
613617
// We need to match a metavar (but the identifier is invalid)... this is an error
614618
TokenTree::MetaVarDecl(span, _, None) => {
615619
if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() {
616-
return Err((span, "missing fragment specifier".to_string()));
620+
return Some(Error(span, "missing fragment specifier".to_string()));
617621
}
618622
}
619623

@@ -655,13 +659,36 @@ fn inner_parse_loop<'root, 'tt>(
655659
// rules. NOTE that this is not necessarily an error unless _all_ items in
656660
// `cur_items` end up doing this. There may still be some other matchers that do
657661
// end up working out.
658-
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) => {}
662+
TokenTree::Token(..) => {}
663+
664+
TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) => unreachable!(),
659665
}
660666
}
661667
}
662668

663-
// Yay a successful parse (so far)!
664-
Ok(())
669+
// If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise,
670+
// either the parse is ambiguous (which should never happen) or there is a syntax error.
671+
if *token == token::Eof {
672+
Some(match eof_items {
673+
EofItems::One(mut eof_item) => {
674+
let matches =
675+
eof_item.matches.iter_mut().map(|dv| Lrc::make_mut(dv).pop().unwrap());
676+
nameize(sess, ms, matches)
677+
}
678+
EofItems::Multiple => {
679+
Error(token.span, "ambiguity: multiple successful parses".to_string())
680+
}
681+
EofItems::None => Failure(
682+
Token::new(
683+
token::Eof,
684+
if token.span.is_dummy() { token.span } else { token.span.shrink_to_hi() },
685+
),
686+
"missing tokens in macro arguments",
687+
),
688+
})
689+
} else {
690+
None
691+
}
665692
}
666693

667694
/// Use the given sequence of token trees (`ms`) as a matcher. Match the token
@@ -672,7 +699,7 @@ pub(super) fn parse_tt(
672699
macro_name: Ident,
673700
) -> NamedParseResult {
674701
// A queue of possible matcher positions. We initialize it with the matcher position in which
675-
// the "dot" is before the first token of the first token tree in `ms`. `inner_parse_loop` then
702+
// the "dot" is before the first token of the first token tree in `ms`. `parse_tt_inner` then
676703
// processes all of these possible matcher positions and produces possible next positions into
677704
// `next_items`. After some post-processing, the contents of `next_items` replenish `cur_items`
678705
// and we start over again.
@@ -681,135 +708,118 @@ pub(super) fn parse_tt(
681708
// there are frequently *no* others! -- are allocated on the heap.
682709
let mut initial = MatcherPos::new(ms);
683710
let mut cur_items = smallvec![MatcherPosHandle::Ref(&mut initial)];
684-
let mut next_items = Vec::new();
685711

686712
loop {
687-
assert!(next_items.is_empty());
713+
let mut next_items = SmallVec::new();
688714

689715
// Matcher positions black-box parsed by parser.rs (`parser`)
690716
let mut bb_items = SmallVec::new();
691717

692-
// Matcher positions that would be valid if the macro invocation was over now
693-
let mut eof_items = EofItems::None;
694-
695718
// Process `cur_items` until either we have finished the input or we need to get some
696719
// parsing from the black-box parser done. The result is that `next_items` will contain a
697720
// bunch of possible next matcher positions in `next_items`.
698-
match inner_parse_loop(
721+
if let Some(result) = parse_tt_inner(
699722
parser.sess,
723+
ms,
700724
&mut cur_items,
701725
&mut next_items,
702726
&mut bb_items,
703-
&mut eof_items,
704727
&parser.token,
705728
) {
706-
Ok(()) => {}
707-
Err((sp, msg)) => return Error(sp, msg),
729+
return result;
708730
}
709731

710-
// inner parse loop handled all cur_items, so it's empty
732+
// `parse_tt_inner` handled all cur_items, so it's empty.
711733
assert!(cur_items.is_empty());
712734

713-
// We need to do some post processing after the `inner_parse_loop`.
735+
// We need to do some post processing after the `parse_tt_inner`.
714736
//
715737
// Error messages here could be improved with links to original rules.
716738

717-
// If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise,
718-
// either the parse is ambiguous (which should never happen) or there is a syntax error.
719-
if parser.token == token::Eof {
720-
return match eof_items {
721-
EofItems::One(mut eof_item) => {
722-
let matches =
723-
eof_item.matches.iter_mut().map(|dv| Lrc::make_mut(dv).pop().unwrap());
724-
nameize(parser.sess, ms, matches)
725-
}
726-
EofItems::Multiple => {
727-
Error(parser.token.span, "ambiguity: multiple successful parses".to_string())
728-
}
729-
EofItems::None => Failure(
730-
Token::new(
731-
token::Eof,
732-
if parser.token.span.is_dummy() {
733-
parser.token.span
734-
} else {
735-
parser.token.span.shrink_to_hi()
736-
},
737-
),
738-
"missing tokens in macro arguments",
739-
),
740-
};
741-
}
742-
// Performance hack: `eof_items` may share matchers via `Rc` with other things that we want
743-
// to modify. Dropping `eof_items` now may drop these refcounts to 1, preventing an
744-
// unnecessary implicit clone later in `Rc::make_mut`.
745-
drop(eof_items);
746-
747-
// If there are no possible next positions AND we aren't waiting for the black-box parser,
748-
// then there is a syntax error.
749-
if bb_items.is_empty() && next_items.is_empty() {
750-
return Failure(parser.token.clone(), "no rules expected this token in macro call");
751-
}
739+
match (next_items.len(), bb_items.len()) {
740+
(0, 0) => {
741+
// There are no possible next positions AND we aren't waiting for the black-box
742+
// parser: syntax error.
743+
return Failure(parser.token.clone(), "no rules expected this token in macro call");
744+
}
752745

753-
if (!bb_items.is_empty() && !next_items.is_empty()) || bb_items.len() > 1 {
754-
// We need to call out to parse some rust nonterminal (black-box) parser. But something
755-
// is wrong, because there is not EXACTLY ONE of these.
756-
let nts = bb_items
757-
.iter()
758-
.map(|item| match item.top_elts.get_tt(item.idx) {
759-
TokenTree::MetaVarDecl(_, bind, Some(kind)) => format!("{} ('{}')", kind, bind),
760-
_ => panic!(),
761-
})
762-
.collect::<Vec<String>>()
763-
.join(" or ");
764-
765-
return Error(
766-
parser.token.span,
767-
format!(
768-
"local ambiguity when calling macro `{macro_name}`: multiple parsing options: {}",
769-
match next_items.len() {
770-
0 => format!("built-in NTs {}.", nts),
771-
1 => format!("built-in NTs {} or 1 other option.", nts),
772-
n => format!("built-in NTs {} or {} other options.", nts, n),
773-
}
774-
),
775-
);
776-
}
746+
(_, 0) => {
747+
// Dump all possible `next_items` into `cur_items` for the next iteration. Then
748+
// process the next token.
749+
cur_items.extend(next_items.drain(..));
750+
parser.to_mut().bump();
751+
}
777752

778-
if !next_items.is_empty() {
779-
// Dump all possible `next_items` into `cur_items` for the next iteration. Then process
780-
// the next token.
781-
cur_items.extend(next_items.drain(..));
782-
parser.to_mut().bump();
783-
} else {
784-
// Finally, we have the case where we need to call the black-box parser to get some
785-
// nonterminal.
786-
assert_eq!(bb_items.len(), 1);
787-
788-
let mut item = bb_items.pop().unwrap();
789-
if let TokenTree::MetaVarDecl(span, _, Some(kind)) = item.top_elts.get_tt(item.idx) {
790-
let match_cur = item.match_cur;
791-
// We use the span of the metavariable declaration to determine any
792-
// edition-specific matching behavior for non-terminals.
793-
let nt = match parser.to_mut().parse_nonterminal(kind) {
794-
Err(mut err) => {
795-
err.span_label(
796-
span,
797-
format!("while parsing argument for this `{}` macro fragment", kind),
798-
)
799-
.emit();
800-
return ErrorReported;
801-
}
802-
Ok(nt) => nt,
803-
};
804-
item.push_match(match_cur, MatchedNonterminal(Lrc::new(nt)));
805-
item.idx += 1;
806-
item.match_cur += 1;
807-
} else {
808-
unreachable!()
753+
(0, 1) => {
754+
// We need to call the black-box parser to get some nonterminal.
755+
let mut item = bb_items.pop().unwrap();
756+
if let TokenTree::MetaVarDecl(span, _, Some(kind)) = item.top_elts.get_tt(item.idx)
757+
{
758+
let match_cur = item.match_cur;
759+
// We use the span of the metavariable declaration to determine any
760+
// edition-specific matching behavior for non-terminals.
761+
let nt = match parser.to_mut().parse_nonterminal(kind) {
762+
Err(mut err) => {
763+
err.span_label(
764+
span,
765+
format!("while parsing argument for this `{kind}` macro fragment"),
766+
)
767+
.emit();
768+
return ErrorReported;
769+
}
770+
Ok(nt) => nt,
771+
};
772+
item.push_match(match_cur, MatchedNonterminal(Lrc::new(nt)));
773+
item.idx += 1;
774+
item.match_cur += 1;
775+
} else {
776+
unreachable!()
777+
}
778+
cur_items.push(item);
779+
}
780+
781+
(_, _) => {
782+
// We need to call the black-box parser to get some nonterminal, but something is
783+
// wrong.
784+
return bb_items_ambiguity_error(
785+
macro_name,
786+
next_items,
787+
bb_items,
788+
parser.token.span,
789+
);
809790
}
810-
cur_items.push(item);
811791
}
812792

813793
assert!(!cur_items.is_empty());
814794
}
815795
}
796+
797+
fn bb_items_ambiguity_error<'root, 'tt>(
798+
macro_name: Ident,
799+
next_items: SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>,
800+
bb_items: SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>,
801+
token_span: rustc_span::Span,
802+
) -> NamedParseResult {
803+
let nts = bb_items
804+
.iter()
805+
.map(|item| match item.top_elts.get_tt(item.idx) {
806+
TokenTree::MetaVarDecl(_, bind, Some(kind)) => {
807+
format!("{} ('{}')", kind, bind)
808+
}
809+
_ => panic!(),
810+
})
811+
.collect::<Vec<String>>()
812+
.join(" or ");
813+
814+
Error(
815+
token_span,
816+
format!(
817+
"local ambiguity when calling macro `{macro_name}`: multiple parsing options: {}",
818+
match next_items.len() {
819+
0 => format!("built-in NTs {}.", nts),
820+
1 => format!("built-in NTs {} or 1 other option.", nts),
821+
n => format!("built-in NTs {} or {} other options.", nts, n),
822+
}
823+
),
824+
)
825+
}

0 commit comments

Comments
 (0)