1 //! This is an NFA-based parser, which calls out to the main Rust parser for named non-terminals
2 //! (which it commits to fully when it hits one in a grammar). There's a set of current NFA threads
3 //! and a set of next ones. Instead of NTs, we have a special case for Kleene star. The big-O, in
4 //! pathological cases, is worse than traditional use of NFA or Earley parsing, but it's an easier
5 //! fit for Macro-by-Example-style rules.
7 //! (In order to prevent the pathological case, we'd need to lazily construct the resulting
8 //! `NamedMatch`es at the very end. It'd be a pain, and require more memory to keep around old
9 //! matcher positions, but it would also save overhead)
11 //! We don't say this parser uses the Earley algorithm, because it's unnecessarily inaccurate.
12 //! The macro parser restricts itself to the features of finite state automata. Earley parsers
13 //! can be described as an extension of NFAs with completion rules, prediction rules, and recursion.
15 //! Quick intro to how the parser works:
17 //! A "matcher position" (a.k.a. "position" or "mp") is a dot in the middle of a matcher, usually
18 //! written as a `·`. For example `· a $( a )* a b` is one, as is `a $( · a )* a b`.
20 //! The parser walks through the input a token at a time, maintaining a list
21 //! of threads consistent with the current position in the input string: `cur_mps`.
23 //! As it processes them, it fills up `eof_mps` with threads that would be valid if
24 //! the macro invocation is now over, `bb_mps` with threads that are waiting on
25 //! a Rust non-terminal like `$e:expr`, and `next_mps` with threads that are waiting
26 //! on a particular token. Most of the logic concerns moving the · through the
27 //! repetitions indicated by Kleene stars. The rules for moving the · without
28 //! consuming any input are called epsilon transitions. It only advances or calls
29 //! out to the real Rust parser when no `cur_mps` threads remain.
34 //! Start parsing a a a a b against [· a $( a )* a b].
36 //! Remaining input: a a a a b
37 //! next: [· a $( a )* a b]
39 //! - - - Advance over an a. - - -
41 //! Remaining input: a a a b
42 //! cur: [a · $( a )* a b]
43 //! Descend/Skip (first position).
44 //! next: [a $( · a )* a b] [a $( a )* · a b].
46 //! - - - Advance over an a. - - -
48 //! Remaining input: a a b
49 //! cur: [a $( a · )* a b] [a $( a )* a · b]
50 //! Follow epsilon transition: Finish/Repeat (first position)
51 //! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b]
53 //! - - - Advance over an a. - - - (this looks exactly like the last step)
55 //! Remaining input: a b
56 //! cur: [a $( a · )* a b] [a $( a )* a · b]
57 //! Follow epsilon transition: Finish/Repeat (first position)
58 //! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b]
60 //! - - - Advance over an a. - - - (this looks exactly like the last step)
62 //! Remaining input: b
63 //! cur: [a $( a · )* a b] [a $( a )* a · b]
64 //! Follow epsilon transition: Finish/Repeat (first position)
65 //! next: [a $( a )* · a b] [a $( · a )* a b] [a $( a )* a · b]
67 //! - - - Advance over a b. - - -
69 //! Remaining input: ''
70 //! eof: [a $( a )* a b ·]
73 pub(crate) use NamedMatch::*;
74 pub(crate) use ParseResult::*;
76 use crate::mbe::{KleeneOp, TokenTree};
78 use rustc_ast::token::{self, DocComment, Nonterminal, NonterminalKind, Token};
79 use rustc_lint_defs::pluralize;
80 use rustc_parse::parser::{NtOrTt, Parser};
81 use rustc_span::symbol::MacroRulesNormalizedIdent;
84 use rustc_data_structures::fx::FxHashMap;
85 use rustc_data_structures::sync::Lrc;
86 use rustc_span::symbol::Ident;
88 use std::collections::hash_map::Entry::{Occupied, Vacant};
90 /// A unit within a matcher that a `MatcherPos` can refer to. Similar to (and derived from)
91 /// `mbe::TokenTree`, but designed specifically for fast and easy traversal during matching.
92 /// Notable differences to `mbe::TokenTree`:
93 /// - It is non-recursive, i.e. there is no nesting.
94 /// - The end pieces of each sequence (the separator, if present, and the Kleene op) are
95 /// represented explicitly, as is the very end of the matcher.
97 /// This means a matcher can be represented by `&[MatcherLoc]`, and traversal mostly involves
98 /// simply incrementing the current matcher position index by one.
99 pub(super) enum MatcherLoc {
106 num_metavar_decls: usize,
107 idx_first_after: usize,
111 SequenceKleeneOpNoSep {
118 SequenceKleeneOpAfterSep {
124 kind: Option<NonterminalKind>,
131 pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
134 locs: &mut Vec<MatcherLoc>,
135 next_metavar: &mut usize,
140 TokenTree::Token(token) => {
141 locs.push(MatcherLoc::Token { token: token.clone() });
143 TokenTree::Delimited(span, delimited) => {
144 let open_token = Token::new(token::OpenDelim(delimited.delim), span.open);
145 let close_token = Token::new(token::CloseDelim(delimited.delim), span.close);
147 locs.push(MatcherLoc::Delimited);
148 locs.push(MatcherLoc::Token { token: open_token });
149 inner(&delimited.tts, locs, next_metavar, seq_depth);
150 locs.push(MatcherLoc::Token { token: close_token });
152 TokenTree::Sequence(_, seq) => {
153 // We can't determine `idx_first_after` and construct the final
154 // `MatcherLoc::Sequence` until after `inner()` is called and the sequence end
155 // pieces are processed. So we push a dummy value (`Eof` is cheapest to
156 // construct) now, and overwrite it with the proper value below.
157 let dummy = MatcherLoc::Eof;
160 let next_metavar_orig = *next_metavar;
161 let op = seq.kleene.op;
162 let idx_first = locs.len();
163 let idx_seq = idx_first - 1;
164 inner(&seq.tts, locs, next_metavar, seq_depth + 1);
166 if let Some(separator) = &seq.separator {
167 locs.push(MatcherLoc::SequenceSep { separator: separator.clone() });
168 locs.push(MatcherLoc::SequenceKleeneOpAfterSep { idx_first });
170 locs.push(MatcherLoc::SequenceKleeneOpNoSep { op, idx_first });
173 // Overwrite the dummy value pushed above with the proper value.
174 locs[idx_seq] = MatcherLoc::Sequence {
176 num_metavar_decls: seq.num_captures,
177 idx_first_after: locs.len(),
178 next_metavar: next_metavar_orig,
182 &TokenTree::MetaVarDecl(span, bind, kind) => {
183 locs.push(MatcherLoc::MetaVarDecl {
187 next_metavar: *next_metavar,
192 TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) => unreachable!(),
197 let mut locs = vec![];
198 let mut next_metavar = 0;
199 inner(matcher, &mut locs, &mut next_metavar, /* seq_depth */ 0);
201 // A final entry is needed for eof.
202 locs.push(MatcherLoc::Eof);
207 /// A single matcher position, representing the state of matching.
209 /// The index into `TtParser::locs`, which represents the "dot".
212 /// The matches made against metavar decls so far. On a successful match, this vector ends up
213 /// with one element per metavar decl in the matcher. Each element records token trees matched
214 /// against the relevant metavar by the black box parser. An element will be a `MatchedSeq` if
215 /// the corresponding metavar decl is within a sequence.
217 /// It is critical to performance that this is an `Lrc`, because it gets cloned frequently when
218 /// processing sequences. Mostly for sequence-ending possibilities that must be tried but end
220 matches: Lrc<Vec<NamedMatch>>,
223 // This type is used a lot. Make sure it doesn't unintentionally get bigger.
224 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
225 rustc_data_structures::static_assert_size!(MatcherPos, 16);
228 /// Adds `m` as a named match for the `metavar_idx`-th metavar. There are only two call sites,
229 /// and both are hot enough to be always worth inlining.
231 fn push_match(&mut self, metavar_idx: usize, seq_depth: usize, m: NamedMatch) {
232 let matches = Lrc::make_mut(&mut self.matches);
235 // We are not within a sequence. Just append `m`.
236 assert_eq!(metavar_idx, matches.len());
240 // We are within a sequence. Find the final `MatchedSeq` at the appropriate depth
241 // and append `m` to its vector.
242 let mut curr = &mut matches[metavar_idx];
243 for _ in 0..seq_depth - 1 {
245 MatchedSeq(seq) => curr = seq.last_mut().unwrap(),
250 MatchedSeq(seq) => seq.push(m),
258 enum EofMatcherPositions {
264 /// Represents the possible results of an attempted parse.
265 pub(crate) enum ParseResult<T> {
266 /// Parsed successfully.
268 /// Arm failed to match. If the second parameter is `token::Eof`, it indicates an unexpected
269 /// end of macro invocation. Otherwise, it indicates that no rules expected the given token.
270 Failure(Token, &'static str),
271 /// Fatal error (malformed macro?). Abort compilation.
272 Error(rustc_span::Span, String),
276 /// A `ParseResult` where the `Success` variant contains a mapping of
277 /// `MacroRulesNormalizedIdent`s to `NamedMatch`es. This represents the mapping
278 /// of metavars to the token trees they bind to.
279 pub(crate) type NamedParseResult = ParseResult<FxHashMap<MacroRulesNormalizedIdent, NamedMatch>>;
281 /// Count how many metavars declarations are in `matcher`.
282 pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize {
286 TokenTree::MetaVarDecl(..) => 1,
287 TokenTree::Sequence(_, seq) => seq.num_captures,
288 TokenTree::Delimited(_, delim) => count_metavar_decls(&delim.tts),
289 TokenTree::Token(..) => 0,
290 TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) => unreachable!(),
295 /// `NamedMatch` is a pattern-match result for a single metavar. All
296 /// `MatchedNonterminal`s in the `NamedMatch` have the same non-terminal type
297 /// (expr, item, etc).
299 /// The in-memory structure of a particular `NamedMatch` represents the match
300 /// that occurred when a particular subset of a matcher was applied to a
301 /// particular token tree.
303 /// The width of each `MatchedSeq` in the `NamedMatch`, and the identity of
304 /// the `MatchedNtNonTts`s, will depend on the token tree it was applied
305 /// to: each `MatchedSeq` corresponds to a single repetition in the originating
306 /// token tree. The depth of the `NamedMatch` structure will therefore depend
307 /// only on the nesting depth of repetitions in the originating token tree it
308 /// was derived from.
310 /// In layperson's terms: `NamedMatch` will form a tree representing nested matches of a particular
311 /// meta variable. For example, if we are matching the following macro against the following
315 /// macro_rules! foo {
316 /// ($($($x:ident),+);+) => {}
319 /// foo!(a, b, c, d; a, b, c, d, e);
322 /// Then, the tree will have the following shape:
324 /// ```ignore (private-internal)
325 /// # use NamedMatch::*;
328 /// MatchedNonterminal(a),
329 /// MatchedNonterminal(b),
330 /// MatchedNonterminal(c),
331 /// MatchedNonterminal(d),
334 /// MatchedNonterminal(a),
335 /// MatchedNonterminal(b),
336 /// MatchedNonterminal(c),
337 /// MatchedNonterminal(d),
338 /// MatchedNonterminal(e),
342 #[derive(Debug, Clone)]
343 pub(crate) enum NamedMatch {
344 MatchedSeq(Vec<NamedMatch>),
346 // A metavar match of type `tt`.
347 MatchedTokenTree(rustc_ast::tokenstream::TokenTree),
349 // A metavar match of any type other than `tt`.
350 MatchedNonterminal(Lrc<Nonterminal>),
353 /// Performs a token equality check, ignoring syntax context (that is, an unhygienic comparison)
354 fn token_name_eq(t1: &Token, t2: &Token) -> bool {
355 if let (Some((ident1, is_raw1)), Some((ident2, is_raw2))) = (t1.ident(), t2.ident()) {
356 ident1.name == ident2.name && is_raw1 == is_raw2
357 } else if let (Some(ident1), Some(ident2)) = (t1.lifetime(), t2.lifetime()) {
358 ident1.name == ident2.name
364 // Note: the vectors could be created and dropped within `parse_tt`, but to avoid excess
365 // allocations we have a single vector for each kind that is cleared and reused repeatedly.
366 pub struct TtParser {
369 /// The set of current mps to be processed. This should be empty by the end of a successful
370 /// execution of `parse_tt_inner`.
371 cur_mps: Vec<MatcherPos>,
373 /// The set of newly generated mps. These are used to replenish `cur_mps` in the function
375 next_mps: Vec<MatcherPos>,
377 /// The set of mps that are waiting for the black-box parser.
378 bb_mps: Vec<MatcherPos>,
380 /// Pre-allocate an empty match array, so it can be cloned cheaply for macros with many rules
381 /// that have no metavars.
382 empty_matches: Lrc<Vec<NamedMatch>>,
386 pub(super) fn new(macro_name: Ident) -> TtParser {
392 empty_matches: Lrc::new(vec![]),
396 /// Process the matcher positions of `cur_mps` until it is empty. In the process, this will
397 /// produce more mps in `next_mps` and `bb_mps`.
401 /// `Some(result)` if everything is finished, `None` otherwise. Note that matches are kept
402 /// track of through the mps generated.
405 matcher: &[MatcherLoc],
407 ) -> Option<NamedParseResult> {
408 // Matcher positions that would be valid if the macro invocation was over now. Only
409 // modified if `token == Eof`.
410 let mut eof_mps = EofMatcherPositions::None;
412 while let Some(mut mp) = self.cur_mps.pop() {
413 match &matcher[mp.idx] {
414 MatcherLoc::Token { token: t } => {
415 // If it's a doc comment, we just ignore it and move on to the next tt in the
416 // matcher. This is a bug, but #95267 showed that existing programs rely on
417 // this behaviour, and changing it would require some care and a transition
420 // If the token matches, we can just advance the parser.
422 // Otherwise, this match has failed, there is nothing to do, and hopefully
423 // another mp in `cur_mps` will match.
424 if matches!(t, Token { kind: DocComment(..), .. }) {
426 self.cur_mps.push(mp);
427 } else if token_name_eq(&t, token) {
429 self.next_mps.push(mp);
432 MatcherLoc::Delimited => {
433 // Entering the delimiter is trivial.
435 self.cur_mps.push(mp);
437 &MatcherLoc::Sequence {
444 // Install an empty vec for each metavar within the sequence.
445 for metavar_idx in next_metavar..next_metavar + num_metavar_decls {
446 mp.push_match(metavar_idx, seq_depth, MatchedSeq(vec![]));
449 if op == KleeneOp::ZeroOrMore || op == KleeneOp::ZeroOrOne {
450 // Try zero matches of this sequence, by skipping over it.
451 self.cur_mps.push(MatcherPos {
452 idx: idx_first_after,
453 matches: mp.matches.clone(), // a cheap clone
457 // Try one or more matches of this sequence, by entering it.
459 self.cur_mps.push(mp);
461 &MatcherLoc::SequenceKleeneOpNoSep { op, idx_first } => {
462 // We are past the end of a sequence with no separator. Try ending the
463 // sequence. If that's not possible, `ending_mp` will fail quietly when it is
464 // processed next time around the loop.
465 let ending_mp = MatcherPos {
466 idx: mp.idx + 1, // +1 skips the Kleene op
467 matches: mp.matches.clone(), // a cheap clone
469 self.cur_mps.push(ending_mp);
471 if op != KleeneOp::ZeroOrOne {
472 // Try another repetition.
474 self.cur_mps.push(mp);
477 MatcherLoc::SequenceSep { separator } => {
478 // We are past the end of a sequence with a separator but we haven't seen the
479 // separator yet. Try ending the sequence. If that's not possible, `ending_mp`
480 // will fail quietly when it is processed next time around the loop.
481 let ending_mp = MatcherPos {
482 idx: mp.idx + 2, // +2 skips the separator and the Kleene op
483 matches: mp.matches.clone(), // a cheap clone
485 self.cur_mps.push(ending_mp);
487 if token_name_eq(token, separator) {
488 // The separator matches the current token. Advance past it.
490 self.next_mps.push(mp);
493 &MatcherLoc::SequenceKleeneOpAfterSep { idx_first } => {
494 // We are past the sequence separator. This can't be a `?` Kleene op, because
495 // they don't permit separators. Try another repetition.
497 self.cur_mps.push(mp);
499 &MatcherLoc::MetaVarDecl { span, kind, .. } => {
500 // Built-in nonterminals never start with these tokens, so we can eliminate
501 // them from consideration. We use the span of the metavariable declaration
502 // to determine any edition-specific matching behavior for non-terminals.
503 if let Some(kind) = kind {
504 if Parser::nonterminal_may_begin_with(kind, token) {
505 self.bb_mps.push(mp);
508 // E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used.
509 // Both this check and the one in `nameize` are necessary, surprisingly.
510 return Some(Error(span, "missing fragment specifier".to_string()));
514 // We are past the matcher's end, and not in a sequence. Try to end things.
515 debug_assert_eq!(mp.idx, matcher.len() - 1);
516 if *token == token::Eof {
517 eof_mps = match eof_mps {
518 EofMatcherPositions::None => EofMatcherPositions::One(mp),
519 EofMatcherPositions::One(_) | EofMatcherPositions::Multiple => {
520 EofMatcherPositions::Multiple
528 // If we reached the end of input, check that there is EXACTLY ONE possible matcher.
529 // Otherwise, either the parse is ambiguous (which is an error) or there is a syntax error.
530 if *token == token::Eof {
532 EofMatcherPositions::One(mut eof_mp) => {
533 // Need to take ownership of the matches from within the `Lrc`.
534 Lrc::make_mut(&mut eof_mp.matches);
535 let matches = Lrc::try_unwrap(eof_mp.matches).unwrap().into_iter();
536 self.nameize(matcher, matches)
538 EofMatcherPositions::Multiple => {
539 Error(token.span, "ambiguity: multiple successful parses".to_string())
541 EofMatcherPositions::None => Failure(
544 if token.span.is_dummy() { token.span } else { token.span.shrink_to_hi() },
546 "missing tokens in macro arguments",
554 /// Match the token stream from `parser` against `matcher`.
555 pub(super) fn parse_tt(
557 parser: &mut Cow<'_, Parser<'_>>,
558 matcher: &[MatcherLoc],
559 ) -> NamedParseResult {
560 // A queue of possible matcher positions. We initialize it with the matcher position in
561 // which the "dot" is before the first token of the first token tree in `matcher`.
562 // `parse_tt_inner` then processes all of these possible matcher positions and produces
563 // possible next positions into `next_mps`. After some post-processing, the contents of
564 // `next_mps` replenish `cur_mps` and we start over again.
565 self.cur_mps.clear();
566 self.cur_mps.push(MatcherPos { idx: 0, matches: self.empty_matches.clone() });
569 self.next_mps.clear();
572 // Process `cur_mps` until either we have finished the input or we need to get some
573 // parsing from the black-box parser done.
574 if let Some(res) = self.parse_tt_inner(matcher, &parser.token) {
578 // `parse_tt_inner` handled all of `cur_mps`, so it's empty.
579 assert!(self.cur_mps.is_empty());
581 // Error messages here could be improved with links to original rules.
582 match (self.next_mps.len(), self.bb_mps.len()) {
584 // There are no possible next positions AND we aren't waiting for the black-box
585 // parser: syntax error.
587 parser.token.clone(),
588 "no rules expected this token in macro call",
593 // Dump all possible `next_mps` into `cur_mps` for the next iteration. Then
594 // process the next token.
595 self.cur_mps.append(&mut self.next_mps);
596 parser.to_mut().bump();
600 // We need to call the black-box parser to get some nonterminal.
601 let mut mp = self.bb_mps.pop().unwrap();
602 let loc = &matcher[mp.idx];
603 if let &MatcherLoc::MetaVarDecl {
611 // We use the span of the metavariable declaration to determine any
612 // edition-specific matching behavior for non-terminals.
613 let nt = match parser.to_mut().parse_nonterminal(kind) {
618 "while parsing argument for this `{kind}` macro fragment"
622 return ErrorReported;
627 NtOrTt::Nt(nt) => MatchedNonterminal(Lrc::new(nt)),
628 NtOrTt::Tt(tt) => MatchedTokenTree(tt),
630 mp.push_match(next_metavar, seq_depth, m);
635 self.cur_mps.push(mp);
639 // Too many possibilities!
640 return self.ambiguity_error(matcher, parser.token.span);
644 assert!(!self.cur_mps.is_empty());
650 matcher: &[MatcherLoc],
651 token_span: rustc_span::Span,
652 ) -> NamedParseResult {
656 .map(|mp| match &matcher[mp.idx] {
657 MatcherLoc::MetaVarDecl { bind, kind: Some(kind), .. } => {
658 format!("{} ('{}')", kind, bind)
662 .collect::<Vec<String>>()
668 "local ambiguity when calling macro `{}`: multiple parsing options: {}",
670 match self.next_mps.len() {
671 0 => format!("built-in NTs {}.", nts),
672 n => format!("built-in NTs {} or {n} other option{s}.", nts, s = pluralize!(n)),
678 fn nameize<I: Iterator<Item = NamedMatch>>(
680 matcher: &[MatcherLoc],
682 ) -> NamedParseResult {
683 // Make that each metavar has _exactly one_ binding. If so, insert the binding into the
684 // `NamedParseResult`. Otherwise, it's an error.
685 let mut ret_val = FxHashMap::default();
687 if let &MatcherLoc::MetaVarDecl { span, bind, kind, .. } = loc {
689 match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) {
690 Vacant(spot) => spot.insert(res.next().unwrap()),
692 return Error(span, format!("duplicated bind name: {}", bind));
696 // E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used.
697 // Both this check and the one in `parse_tt_inner` are necessary, surprisingly.
698 return Error(span, "missing fragment specifier".to_string());