| author | bors <bors@rust-lang.org> 2026-07-06 22:08:27 UTC |
| committer | bors <bors@rust-lang.org> 2026-07-06 22:08:27 UTC |
| log | b960fcf2ff0f04967b30b947be8fc155fb067901 |
| tree | d5f061ec720f0f6e8cce82db0b49d7fc75b14b86 |
| parent | c4af71034e89a431eeee91125a31ad001379faac |
| parent | c1aa8d71a6d35cfa53e80b7a0e76c0fb601ae25a |
Polish some macro parsing code
I'm working on some optimizations to `macro_parser.rs` and the surrounding code (specifically, changing the current BFS approach into a DFS), and these changes fell out during that effort. I think they make the code more accessible in addition to laying some foundations for my future work (in particular, the handling of ambiguities).
This is my first proper entrance into rustc code, please let me know if I'm holding something wrong :)
The commits are organized and individually reviewable.
r? @nnethercote3 files changed, 212 insertions(+), 140 deletions(-)
compiler/rustc_expand/src/mbe/diagnostics.rs+78-24| ... | ... | @@ -2,9 +2,10 @@ use std::borrow::Cow; |
| 2 | 2 | |
| 3 | 3 | use rustc_ast::token::{self, Token}; |
| 4 | 4 | use rustc_ast::tokenstream::TokenStream; |
| 5 | use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage}; | |
| 5 | use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage, pluralize}; | |
| 6 | 6 | use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; |
| 7 | 7 | use rustc_macros::Subdiagnostic; |
| 8 | use rustc_middle::bug; | |
| 8 | 9 | use rustc_parse::parser::{Parser, Recovery, token_descr}; |
| 9 | 10 | use rustc_session::parse::ParseSess; |
| 10 | 11 | use rustc_span::source_map::SourceMap; |
| ... | ... | @@ -16,7 +17,7 @@ use crate::expand::{AstFragmentKind, parse_ast_fragment}; |
| 16 | 17 | use crate::mbe::macro_parser::ParseResult::*; |
| 17 | 18 | use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser}; |
| 18 | 19 | use crate::mbe::macro_rules::{ |
| 19 | Tracker, try_match_macro, try_match_macro_attr, try_match_macro_derive, | |
| 20 | Tracker, WhichMatcher, try_match_macro, try_match_macro_attr, try_match_macro_derive, | |
| 20 | 21 | }; |
| 21 | 22 | |
| 22 | 23 | pub(super) enum FailedMacro<'a> { |
| ... | ... | @@ -44,7 +45,7 @@ pub(super) fn failed_to_match_macro( |
| 44 | 45 | |
| 45 | 46 | // An error occurred, try the expansion again, tracking the expansion closely for better |
| 46 | 47 | // diagnostics. |
| 47 | let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp); | |
| 48 | let mut tracker = CollectTrackerAndEmitter::new(name, psess.dcx(), sp); | |
| 48 | 49 | |
| 49 | 50 | let try_success_result = match args { |
| 50 | 51 | FailedMacro::Func => try_match_macro(psess, name, body, rules, &mut tracker), |
| ... | ... | @@ -121,7 +122,7 @@ pub(super) fn failed_to_match_macro( |
| 121 | 122 | for rule in rules { |
| 122 | 123 | let MacroRule::Func { lhs, .. } = rule else { continue }; |
| 123 | 124 | let parser = parser_from_cx(psess, body.clone(), Recovery::Allowed); |
| 124 | let mut tt_parser = TtParser::new(name); | |
| 125 | let mut tt_parser = TtParser::new(); | |
| 125 | 126 | |
| 126 | 127 | if let Success(_) = |
| 127 | 128 | tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker) |
| ... | ... | @@ -145,6 +146,7 @@ pub(super) fn failed_to_match_macro( |
| 145 | 146 | |
| 146 | 147 | /// The tracker used for the slow error path that collects useful info for diagnostics. |
| 147 | 148 | struct CollectTrackerAndEmitter<'dcx, 'matcher> { |
| 149 | macro_name: Ident, | |
| 148 | 150 | dcx: DiagCtxtHandle<'dcx>, |
| 149 | 151 | remaining_matcher: Option<&'matcher MatcherLoc>, |
| 150 | 152 | /// Which arm's failure should we report? (the one furthest along) |
| ... | ... | @@ -155,14 +157,22 @@ struct CollectTrackerAndEmitter<'dcx, 'matcher> { |
| 155 | 157 | |
| 156 | 158 | struct BestFailure { |
| 157 | 159 | token: Token, |
| 158 | position_in_tokenstream: (bool, u32), | |
| 160 | ||
| 161 | /// The matcher in which the failure occurred. | |
| 162 | matcher: WhichMatcher, | |
| 163 | ||
| 164 | /// The approximate (parser) position of the failure. | |
| 165 | /// | |
| 166 | /// This is relative to [`Self::matcher`]. | |
| 167 | position: u32, | |
| 168 | ||
| 159 | 169 | msg: &'static str, |
| 160 | 170 | remaining_matcher: MatcherLoc, |
| 161 | 171 | } |
| 162 | 172 | |
| 163 | 173 | impl BestFailure { |
| 164 | fn is_better_position(&self, position: (bool, u32)) -> bool { | |
| 165 | position > self.position_in_tokenstream | |
| 174 | fn is_better_position(&self, matcher: WhichMatcher, position: u32) -> bool { | |
| 175 | (matcher, position) > (self.matcher, self.position) | |
| 166 | 176 | } |
| 167 | 177 | } |
| 168 | 178 | |
| ... | ... | @@ -181,8 +191,8 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match |
| 181 | 191 | } |
| 182 | 192 | } |
| 183 | 193 | |
| 184 | fn after_arm(&mut self, in_body: bool, result: &NamedParseResult<Self::Failure>) { | |
| 185 | match result { | |
| 194 | fn after_arm(&mut self, which_matcher: WhichMatcher, result: &NamedParseResult<Self::Failure>) { | |
| 195 | match *result { | |
| 186 | 196 | Success(_) => { |
| 187 | 197 | // Nonterminal parser recovery might turn failed matches into successful ones, |
| 188 | 198 | // but for that it must have emitted an error already |
| ... | ... | @@ -194,15 +204,13 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match |
| 194 | 204 | Failure((token, approx_position, msg)) => { |
| 195 | 205 | debug!(?token, ?msg, "a new failure of an arm"); |
| 196 | 206 | |
| 197 | let position_in_tokenstream = (in_body, *approx_position); | |
| 198 | if self | |
| 199 | .best_failure | |
| 200 | .as_ref() | |
| 201 | .is_none_or(|failure| failure.is_better_position(position_in_tokenstream)) | |
| 202 | { | |
| 207 | if self.best_failure.as_ref().is_none_or(|failure| { | |
| 208 | failure.is_better_position(which_matcher, approx_position) | |
| 209 | }) { | |
| 203 | 210 | self.best_failure = Some(BestFailure { |
| 204 | token: *token, | |
| 205 | position_in_tokenstream, | |
| 211 | token, | |
| 212 | matcher: which_matcher, | |
| 213 | position: approx_position, | |
| 206 | 214 | msg, |
| 207 | 215 | remaining_matcher: self |
| 208 | 216 | .remaining_matcher |
| ... | ... | @@ -211,15 +219,54 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match |
| 211 | 219 | }) |
| 212 | 220 | } |
| 213 | 221 | } |
| 214 | Error(err_sp, msg) => { | |
| 215 | let span = err_sp.substitute_dummy(self.root_span); | |
| 216 | let guar = self.dcx.span_err(span, msg.clone()); | |
| 217 | self.result = Some((span, guar)); | |
| 222 | Ambiguity => { | |
| 223 | if self.result.is_none() { | |
| 224 | bug!("`Error(..)` is only constructed through `Self::ambiguity()`"); | |
| 225 | } | |
| 218 | 226 | } |
| 219 | ErrorReported(guar) => self.result = Some((self.root_span, *guar)), | |
| 227 | ErrorReported(guar) => self.result = Some((self.root_span, guar)), | |
| 220 | 228 | } |
| 221 | 229 | } |
| 222 | 230 | |
| 231 | fn ambiguity( | |
| 232 | &mut self, | |
| 233 | parser: &Parser<'_>, | |
| 234 | bb_locs: impl IntoIterator<Item = &'matcher MatcherLoc>, | |
| 235 | next_locs: impl IntoIterator<Item = &'matcher MatcherLoc>, | |
| 236 | ) { | |
| 237 | let span = parser.token.span.substitute_dummy(self.root_span); | |
| 238 | ||
| 239 | if parser.token == token::Eof { | |
| 240 | let msg = "ambiguity: multiple successful parses".to_string(); | |
| 241 | let guar = self.dcx.span_err(span, msg); | |
| 242 | self.result = Some((span, guar)); | |
| 243 | return; | |
| 244 | } | |
| 245 | ||
| 246 | let nts = bb_locs | |
| 247 | .into_iter() | |
| 248 | .map(|loc| match loc { | |
| 249 | MatcherLoc::MetaVarDecl { bind, kind, .. } => { | |
| 250 | format!("{kind} ('{bind}')") | |
| 251 | } | |
| 252 | _ => unreachable!(), | |
| 253 | }) | |
| 254 | .collect::<Vec<String>>() | |
| 255 | .join(" or "); | |
| 256 | ||
| 257 | let msg = format!( | |
| 258 | "local ambiguity when calling macro `{}`: multiple parsing options: {}", | |
| 259 | self.macro_name, | |
| 260 | match next_locs.into_iter().count() { | |
| 261 | 0 => format!("built-in NTs {nts}."), | |
| 262 | n => format!("built-in NTs {nts} or {n} other option{s}.", s = pluralize!(n)), | |
| 263 | } | |
| 264 | ); | |
| 265 | ||
| 266 | let guar = self.dcx.span_err(span, msg); | |
| 267 | self.result = Some((span, guar)); | |
| 268 | } | |
| 269 | ||
| 223 | 270 | fn description() -> &'static str { |
| 224 | 271 | "detailed" |
| 225 | 272 | } |
| ... | ... | @@ -230,8 +277,15 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match |
| 230 | 277 | } |
| 231 | 278 | |
| 232 | 279 | impl<'dcx> CollectTrackerAndEmitter<'dcx, '_> { |
| 233 | fn new(dcx: DiagCtxtHandle<'dcx>, root_span: Span) -> Self { | |
| 234 | Self { dcx, remaining_matcher: None, best_failure: None, root_span, result: None } | |
| 280 | fn new(macro_name: Ident, dcx: DiagCtxtHandle<'dcx>, root_span: Span) -> Self { | |
| 281 | Self { | |
| 282 | macro_name, | |
| 283 | dcx, | |
| 284 | remaining_matcher: None, | |
| 285 | best_failure: None, | |
| 286 | root_span, | |
| 287 | result: None, | |
| 288 | } | |
| 235 | 289 | } |
| 236 | 290 | } |
| 237 | 291 |
compiler/rustc_expand/src/mbe/macro_parser.rs+65-99| ... | ... | @@ -71,7 +71,6 @@ |
| 71 | 71 | //! ``` |
| 72 | 72 | |
| 73 | 73 | use std::borrow::Cow; |
| 74 | use std::collections::hash_map::Entry::{Occupied, Vacant}; | |
| 75 | 74 | use std::fmt::Display; |
| 76 | 75 | use std::rc::Rc; |
| 77 | 76 | |
| ... | ... | @@ -79,10 +78,11 @@ pub(crate) use NamedMatch::*; |
| 79 | 78 | pub(crate) use ParseResult::*; |
| 80 | 79 | use rustc_ast::token::{self, DocComment, NonterminalKind, Token, TokenKind}; |
| 81 | 80 | use rustc_data_structures::fx::FxHashMap; |
| 82 | use rustc_errors::ErrorGuaranteed; | |
| 83 | use rustc_lint_defs::pluralize; | |
| 81 | use rustc_errors::{Diag, ErrorGuaranteed}; | |
| 82 | use rustc_middle::span_bug; | |
| 84 | 83 | use rustc_parse::parser::{ParseNtResult, Parser, token_descr}; |
| 85 | 84 | use rustc_span::{Ident, MacroRulesNormalizedIdent, Span}; |
| 85 | use smallvec::SmallVec; | |
| 86 | 86 | |
| 87 | 87 | use crate::mbe::macro_rules::Tracker; |
| 88 | 88 | use crate::mbe::{KleeneOp, TokenTree}; |
| ... | ... | @@ -292,12 +292,6 @@ impl MatcherPos { |
| 292 | 292 | } |
| 293 | 293 | } |
| 294 | 294 | |
| 295 | enum EofMatcherPositions { | |
| 296 | None, | |
| 297 | One(MatcherPos), | |
| 298 | Multiple, | |
| 299 | } | |
| 300 | ||
| 301 | 295 | /// Represents the possible results of an attempted parse. |
| 302 | 296 | #[derive(Debug)] |
| 303 | 297 | pub(crate) enum ParseResult<T, F> { |
| ... | ... | @@ -307,8 +301,8 @@ pub(crate) enum ParseResult<T, F> { |
| 307 | 301 | /// end of macro invocation. Otherwise, it indicates that no rules expected the given token. |
| 308 | 302 | /// The usize is the approximate position of the token in the input token stream. |
| 309 | 303 | Failure(F), |
| 310 | /// Fatal error (malformed macro?). Abort compilation. | |
| 311 | Error(rustc_span::Span, String), | |
| 304 | /// The input could be parsed in multiple distinct ways. | |
| 305 | Ambiguity, | |
| 312 | 306 | ErrorReported(ErrorGuaranteed), |
| 313 | 307 | } |
| 314 | 308 | |
| ... | ... | @@ -429,8 +423,6 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { |
| 429 | 423 | // Note: the vectors could be created and dropped within `parse_tt`, but to avoid excess |
| 430 | 424 | // allocations we have a single vector for each kind that is cleared and reused repeatedly. |
| 431 | 425 | pub(crate) struct TtParser { |
| 432 | macro_name: Ident, | |
| 433 | ||
| 434 | 426 | /// The set of current mps to be processed. This should be empty by the end of a successful |
| 435 | 427 | /// execution of `parse_tt_inner`. |
| 436 | 428 | cur_mps: Vec<MatcherPos>, |
| ... | ... | @@ -448,9 +440,8 @@ pub(crate) struct TtParser { |
| 448 | 440 | } |
| 449 | 441 | |
| 450 | 442 | impl TtParser { |
| 451 | pub(super) fn new(macro_name: Ident) -> TtParser { | |
| 443 | pub(super) fn new() -> TtParser { | |
| 452 | 444 | TtParser { |
| 453 | macro_name, | |
| 454 | 445 | cur_mps: vec![], |
| 455 | 446 | next_mps: vec![], |
| 456 | 447 | bb_mps: vec![], |
| ... | ... | @@ -471,14 +462,14 @@ impl TtParser { |
| 471 | 462 | /// track of through the mps generated. |
| 472 | 463 | fn parse_tt_inner<'matcher, T: Tracker<'matcher>>( |
| 473 | 464 | &mut self, |
| 465 | parser: &Parser<'_>, | |
| 474 | 466 | matcher: &'matcher [MatcherLoc], |
| 475 | token: &Token, | |
| 476 | approx_position: u32, | |
| 477 | 467 | track: &mut T, |
| 478 | 468 | ) -> Option<NamedParseResult<T::Failure>> { |
| 479 | 469 | // Matcher positions that would be valid if the macro invocation was over now. Only |
| 480 | 470 | // modified if `token == Eof`. |
| 481 | let mut eof_mps = EofMatcherPositions::None; | |
| 471 | let token = &parser.token; | |
| 472 | let mut eof_mps = SmallVec::<[MatcherPos; 1]>::new(); | |
| 482 | 473 | |
| 483 | 474 | while let Some(mut mp) = self.cur_mps.pop() { |
| 484 | 475 | let matcher_loc = &matcher[mp.idx]; |
| ... | ... | @@ -582,12 +573,7 @@ impl TtParser { |
| 582 | 573 | // We are past the matcher's end, and not in a sequence. Try to end things. |
| 583 | 574 | debug_assert_eq!(mp.idx, matcher.len() - 1); |
| 584 | 575 | if *token == token::Eof { |
| 585 | eof_mps = match eof_mps { | |
| 586 | EofMatcherPositions::None => EofMatcherPositions::One(mp), | |
| 587 | EofMatcherPositions::One(_) | EofMatcherPositions::Multiple => { | |
| 588 | EofMatcherPositions::Multiple | |
| 589 | } | |
| 590 | } | |
| 576 | eof_mps.push(mp); | |
| 591 | 577 | } |
| 592 | 578 | } |
| 593 | 579 | } |
| ... | ... | @@ -596,24 +582,23 @@ impl TtParser { |
| 596 | 582 | // If we reached the end of input, check that there is EXACTLY ONE possible matcher. |
| 597 | 583 | // Otherwise, either the parse is ambiguous (which is an error) or there is a syntax error. |
| 598 | 584 | if *token == token::Eof { |
| 599 | Some(match eof_mps { | |
| 600 | EofMatcherPositions::One(mut eof_mp) => { | |
| 585 | Some(match *eof_mps { | |
| 586 | [_] => { | |
| 587 | let mut eof_mp = eof_mps.pop().unwrap(); | |
| 601 | 588 | // Need to take ownership of the matches from within the `Rc`. |
| 602 | 589 | Rc::make_mut(&mut eof_mp.matches); |
| 603 | 590 | let matches = Rc::try_unwrap(eof_mp.matches).unwrap().into_iter(); |
| 604 | self.nameize(matcher, matches) | |
| 591 | Success(self.nameize(matcher, matches)) | |
| 605 | 592 | } |
| 606 | EofMatcherPositions::Multiple => { | |
| 607 | Error(token.span, "ambiguity: multiple successful parses".to_string()) | |
| 608 | } | |
| 609 | EofMatcherPositions::None => Failure(T::build_failure( | |
| 593 | [] => Failure(T::build_failure( | |
| 610 | 594 | Token::new( |
| 611 | 595 | token::Eof, |
| 612 | 596 | if token.span.is_dummy() { token.span } else { token.span.shrink_to_hi() }, |
| 613 | 597 | ), |
| 614 | approx_position, | |
| 598 | parser.approx_token_stream_pos(), | |
| 615 | 599 | "missing tokens in macro arguments", |
| 616 | 600 | )), |
| 601 | _ => self.ambiguity_error(parser, matcher, track), | |
| 617 | 602 | }) |
| 618 | 603 | } else { |
| 619 | 604 | None |
| ... | ... | @@ -641,12 +626,7 @@ impl TtParser { |
| 641 | 626 | |
| 642 | 627 | // Process `cur_mps` until either we have finished the input or we need to get some |
| 643 | 628 | // parsing from the black-box parser done. |
| 644 | let res = self.parse_tt_inner( | |
| 645 | matcher, | |
| 646 | &parser.token, | |
| 647 | parser.approx_token_stream_pos(), | |
| 648 | track, | |
| 649 | ); | |
| 629 | let res = self.parse_tt_inner(parser, matcher, track); | |
| 650 | 630 | |
| 651 | 631 | if let Some(res) = res { |
| 652 | 632 | return res; |
| ... | ... | @@ -678,36 +658,25 @@ impl TtParser { |
| 678 | 658 | // We need to call the black-box parser to get some nonterminal. |
| 679 | 659 | let mut mp = self.bb_mps.pop().unwrap(); |
| 680 | 660 | let loc = &matcher[mp.idx]; |
| 681 | if let &MatcherLoc::MetaVarDecl { | |
| 682 | span, kind, next_metavar, seq_depth, .. | |
| 683 | } = loc | |
| 684 | { | |
| 685 | // We use the span of the metavariable declaration to determine any | |
| 686 | // edition-specific matching behavior for non-terminals. | |
| 687 | let nt = match parser.to_mut().parse_nonterminal(kind) { | |
| 688 | Err(err) => { | |
| 689 | let guarantee = err.with_span_label( | |
| 690 | span, | |
| 691 | format!( | |
| 692 | "while parsing argument for this `{kind}` macro fragment" | |
| 693 | ), | |
| 694 | ) | |
| 695 | .emit(); | |
| 696 | return ErrorReported(guarantee); | |
| 697 | } | |
| 698 | Ok(nt) => nt, | |
| 699 | }; | |
| 700 | mp.push_match(next_metavar, seq_depth, MatchedSingle(nt)); | |
| 701 | mp.idx += 1; | |
| 702 | } else { | |
| 661 | let MatcherLoc::MetaVarDecl { kind, next_metavar, seq_depth, .. } = *loc else { | |
| 703 | 662 | unreachable!() |
| 704 | } | |
| 663 | }; | |
| 664 | ||
| 665 | // We use the span of the metavariable declaration to determine any | |
| 666 | // edition-specific matching behavior for non-terminals. | |
| 667 | let nt = match parser.to_mut().parse_nonterminal(kind) { | |
| 668 | Err(err) => return self.nt_parsing_error(loc, err), | |
| 669 | Ok(nt) => nt, | |
| 670 | }; | |
| 671 | mp.push_match(next_metavar, seq_depth, MatchedSingle(nt)); | |
| 672 | ||
| 673 | mp.idx += 1; | |
| 705 | 674 | self.cur_mps.push(mp); |
| 706 | 675 | } |
| 707 | 676 | |
| 708 | 677 | (_, _) => { |
| 709 | 678 | // Too many possibilities! |
| 710 | return self.ambiguity_error(matcher, parser.token.span); | |
| 679 | return self.ambiguity_error(parser, matcher, track); | |
| 711 | 680 | } |
| 712 | 681 | } |
| 713 | 682 | |
| ... | ... | @@ -715,54 +684,51 @@ impl TtParser { |
| 715 | 684 | } |
| 716 | 685 | } |
| 717 | 686 | |
| 718 | fn ambiguity_error<F>( | |
| 687 | fn nt_parsing_error<F>(&self, loc: &MatcherLoc, err: Diag<'_>) -> NamedParseResult<F> { | |
| 688 | let &MatcherLoc::MetaVarDecl { span, kind, .. } = loc else { unreachable!() }; | |
| 689 | let guarantee = err | |
| 690 | .with_span_label( | |
| 691 | span, | |
| 692 | format!("while parsing argument for this `{kind}` macro fragment"), | |
| 693 | ) | |
| 694 | .emit(); | |
| 695 | ErrorReported(guarantee) | |
| 696 | } | |
| 697 | ||
| 698 | fn ambiguity_error<'matcher, T: Tracker<'matcher>>( | |
| 719 | 699 | &self, |
| 720 | matcher: &[MatcherLoc], | |
| 721 | token_span: rustc_span::Span, | |
| 722 | ) -> NamedParseResult<F> { | |
| 723 | let nts = self | |
| 724 | .bb_mps | |
| 725 | .iter() | |
| 726 | .map(|mp| match &matcher[mp.idx] { | |
| 727 | MatcherLoc::MetaVarDecl { bind, kind, .. } => { | |
| 728 | format!("{kind} ('{bind}')") | |
| 729 | } | |
| 730 | _ => unreachable!(), | |
| 731 | }) | |
| 732 | .collect::<Vec<String>>() | |
| 733 | .join(" or "); | |
| 734 | ||
| 735 | Error( | |
| 736 | token_span, | |
| 737 | format!( | |
| 738 | "local ambiguity when calling macro `{}`: multiple parsing options: {}", | |
| 739 | self.macro_name, | |
| 740 | match self.next_mps.len() { | |
| 741 | 0 => format!("built-in NTs {nts}."), | |
| 742 | n => format!("built-in NTs {nts} or {n} other option{s}.", s = pluralize!(n)), | |
| 743 | } | |
| 744 | ), | |
| 745 | ) | |
| 700 | parser: &Parser<'_>, | |
| 701 | matcher: &'matcher [MatcherLoc], | |
| 702 | track: &mut T, | |
| 703 | ) -> NamedParseResult<T::Failure> { | |
| 704 | let bb_locs = self.bb_mps.iter().map(|mp| &matcher[mp.idx]); | |
| 705 | let next_locs = self.next_mps.iter().map(|mp| &matcher[mp.idx]); | |
| 706 | track.ambiguity(parser, bb_locs, next_locs); | |
| 707 | Ambiguity | |
| 746 | 708 | } |
| 747 | 709 | |
| 748 | fn nameize<I: Iterator<Item = NamedMatch>, F>( | |
| 710 | fn nameize<I: Iterator<Item = NamedMatch>>( | |
| 749 | 711 | &self, |
| 750 | 712 | matcher: &[MatcherLoc], |
| 751 | 713 | mut res: I, |
| 752 | ) -> NamedParseResult<F> { | |
| 714 | ) -> NamedMatches { | |
| 753 | 715 | // Make that each metavar has _exactly one_ binding. If so, insert the binding into the |
| 754 | 716 | // `NamedParseResult`. Otherwise, it's an error. |
| 755 | 717 | let mut ret_val = FxHashMap::default(); |
| 756 | 718 | for loc in matcher { |
| 757 | if let &MatcherLoc::MetaVarDecl { span, bind, .. } = loc { | |
| 758 | match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) { | |
| 759 | Vacant(spot) => spot.insert(res.next().unwrap()), | |
| 760 | Occupied(..) => { | |
| 761 | return Error(span, format!("duplicated bind name: {bind}")); | |
| 762 | } | |
| 763 | }; | |
| 719 | if let &MatcherLoc::MetaVarDecl { span, bind, .. } = loc | |
| 720 | && ret_val | |
| 721 | .insert(MacroRulesNormalizedIdent::new(bind), res.next().unwrap()) | |
| 722 | .is_some() | |
| 723 | { | |
| 724 | // Duplicate binds are checked for when the macro definition is processed, | |
| 725 | // and should have prevented the definition from ever being used. | |
| 726 | span_bug!( | |
| 727 | span, | |
| 728 | "duplicate meta-variable binding went undetected at macro definition" | |
| 729 | ) | |
| 764 | 730 | } |
| 765 | 731 | } |
| 766 | Success(ret_val) | |
| 732 | ret_val | |
| 767 | 733 | } |
| 768 | 734 | } |
compiler/rustc_expand/src/mbe/macro_rules.rs+69-17| ... | ... | @@ -40,7 +40,7 @@ use crate::base::{ |
| 40 | 40 | use crate::diagnostics; |
| 41 | 41 | use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_ast_fragment}; |
| 42 | 42 | use crate::mbe::macro_check::check_meta_variables; |
| 43 | use crate::mbe::macro_parser::{Error, ErrorReported, Failure, MatcherLoc, Success, TtParser}; | |
| 43 | use crate::mbe::macro_parser::{Ambiguity, ErrorReported, Failure, MatcherLoc, Success, TtParser}; | |
| 44 | 44 | use crate::mbe::quoted::{RulePart, parse_one_tt}; |
| 45 | 45 | use crate::mbe::transcribe::transcribe; |
| 46 | 46 | use crate::mbe::{self, KleeneOp}; |
| ... | ... | @@ -162,6 +162,32 @@ pub(crate) enum MacroRule { |
| 162 | 162 | Derive { body: Vec<MatcherLoc>, body_span: Span, rhs: mbe::TokenTree }, |
| 163 | 163 | } |
| 164 | 164 | |
| 165 | /// A selection of a matcher in a [`MacroRule`]. | |
| 166 | /// | |
| 167 | /// [`MacroRule::Attr`] has two different matchers (args and body). This enum allows distinguishing | |
| 168 | /// between them, even when used for other kinds of rules. | |
| 169 | /// | |
| 170 | /// This type implements [`Ord`]. The arms within a rule come in a fixed order and this type is | |
| 171 | /// consistent with that ordering. | |
| 172 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | |
| 173 | pub(crate) enum WhichMatcher { | |
| 174 | /// The arguments of an attr macro ([`MacroRule::Attr::args`]). | |
| 175 | Args, | |
| 176 | ||
| 177 | /// The body of an attr macro ([`MacroRule::Attr::body`]), **or** the only arm of the rule. | |
| 178 | /// | |
| 179 | /// This is also used to express the only arm in a [`MacroRule::Func`] or [`MacroRule::Derive`]. | |
| 180 | Body, | |
| 181 | } | |
| 182 | ||
| 183 | impl WhichMatcher { | |
| 184 | /// The [`WhichMatcher`] for [`MacroRule::Func`]. | |
| 185 | pub(crate) const FOR_FUNC: Self = Self::Body; | |
| 186 | ||
| 187 | /// The [`WhichMatcher`] for [`MacroRule::Derive`]. | |
| 188 | pub(crate) const FOR_DERIVE: Self = Self::Body; | |
| 189 | } | |
| 190 | ||
| 165 | 191 | pub struct MacroRulesMacroExpander { |
| 166 | 192 | node_id: NodeId, |
| 167 | 193 | name: Ident, |
| ... | ... | @@ -342,18 +368,23 @@ pub(super) trait Tracker<'matcher> { |
| 342 | 368 | fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure; |
| 343 | 369 | |
| 344 | 370 | /// This is called before trying to match next MatcherLoc on the current token. |
| 345 | fn before_match_loc(&mut self, _parser: &TtParser, _matcher: &'matcher MatcherLoc) {} | |
| 371 | fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc); | |
| 346 | 372 | |
| 347 | 373 | /// This is called after an arm has been parsed, either successfully or unsuccessfully. When |
| 348 | 374 | /// this is called, `before_match_loc` was called at least once (with a `MatcherLoc::Eof`). |
| 349 | fn after_arm(&mut self, _in_body: bool, _result: &NamedParseResult<Self::Failure>) {} | |
| 375 | fn after_arm(&mut self, which_matcher: WhichMatcher, result: &NamedParseResult<Self::Failure>); | |
| 376 | ||
| 377 | fn ambiguity( | |
| 378 | &mut self, | |
| 379 | parser: &Parser<'_>, | |
| 380 | bb_locs: impl IntoIterator<Item = &'matcher MatcherLoc>, | |
| 381 | next_locs: impl IntoIterator<Item = &'matcher MatcherLoc>, | |
| 382 | ); | |
| 350 | 383 | |
| 351 | 384 | /// For tracing. |
| 352 | 385 | fn description() -> &'static str; |
| 353 | 386 | |
| 354 | fn recovery() -> Recovery { | |
| 355 | Recovery::Forbidden | |
| 356 | } | |
| 387 | fn recovery() -> Recovery; | |
| 357 | 388 | } |
| 358 | 389 | |
| 359 | 390 | /// A noop tracker that is used in the hot path of the expansion, has zero overhead thanks to |
| ... | ... | @@ -365,9 +396,30 @@ impl<'matcher> Tracker<'matcher> for NoopTracker { |
| 365 | 396 | |
| 366 | 397 | fn build_failure(_tok: Token, _position: u32, _msg: &'static str) -> Self::Failure {} |
| 367 | 398 | |
| 399 | fn before_match_loc(&mut self, _parser: &TtParser, _matcher: &'matcher MatcherLoc) {} | |
| 400 | ||
| 401 | fn ambiguity( | |
| 402 | &mut self, | |
| 403 | _parser: &Parser<'_>, | |
| 404 | _bb_locs: impl IntoIterator<Item = &'matcher MatcherLoc>, | |
| 405 | _next_locs: impl IntoIterator<Item = &'matcher MatcherLoc>, | |
| 406 | ) { | |
| 407 | } | |
| 408 | ||
| 409 | fn after_arm( | |
| 410 | &mut self, | |
| 411 | _which_matcher: WhichMatcher, | |
| 412 | _result: &NamedParseResult<Self::Failure>, | |
| 413 | ) { | |
| 414 | } | |
| 415 | ||
| 368 | 416 | fn description() -> &'static str { |
| 369 | 417 | "none" |
| 370 | 418 | } |
| 419 | ||
| 420 | fn recovery() -> Recovery { | |
| 421 | Recovery::Forbidden | |
| 422 | } | |
| 371 | 423 | } |
| 372 | 424 | |
| 373 | 425 | /// Expands the rules based macro defined by `rules` for a given input `arg`. |
| ... | ... | @@ -572,7 +624,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>( |
| 572 | 624 | // this situation.) |
| 573 | 625 | let parser = parser_from_cx(psess, arg.clone(), T::recovery()); |
| 574 | 626 | // Try each arm's matchers. |
| 575 | let mut tt_parser = TtParser::new(name); | |
| 627 | let mut tt_parser = TtParser::new(); | |
| 576 | 628 | for (i, rule) in rules.iter().enumerate() { |
| 577 | 629 | let MacroRule::Func { lhs, .. } = rule else { continue }; |
| 578 | 630 | let _tracing_span = trace_span!("Matching arm", %i); |
| ... | ... | @@ -585,7 +637,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>( |
| 585 | 637 | |
| 586 | 638 | let result = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, track); |
| 587 | 639 | |
| 588 | track.after_arm(true, &result); | |
| 640 | track.after_arm(WhichMatcher::FOR_FUNC, &result); | |
| 589 | 641 | |
| 590 | 642 | match result { |
| 591 | 643 | Success(named_matches) => { |
| ... | ... | @@ -600,7 +652,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>( |
| 600 | 652 | trace!("Failed to match arm, trying the next one"); |
| 601 | 653 | // Try the next arm. |
| 602 | 654 | } |
| 603 | Error(_, _) => { | |
| 655 | Ambiguity => { | |
| 604 | 656 | debug!("Fatal error occurred during matching"); |
| 605 | 657 | // We haven't emitted an error yet, so we can retry. |
| 606 | 658 | return Err(CanRetry::Yes); |
| ... | ... | @@ -635,14 +687,14 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>( |
| 635 | 687 | // This uses the same strategy as `try_match_macro` |
| 636 | 688 | let args_parser = parser_from_cx(psess, attr_args.clone(), T::recovery()); |
| 637 | 689 | let body_parser = parser_from_cx(psess, attr_body.clone(), T::recovery()); |
| 638 | let mut tt_parser = TtParser::new(name); | |
| 690 | let mut tt_parser = TtParser::new(); | |
| 639 | 691 | for (i, rule) in rules.iter().enumerate() { |
| 640 | 692 | let MacroRule::Attr { args, body, .. } = rule else { continue }; |
| 641 | 693 | |
| 642 | 694 | let mut gated_spans_snapshot = mem::take(&mut *psess.gated_spans.spans.borrow_mut()); |
| 643 | 695 | |
| 644 | 696 | let result = tt_parser.parse_tt(&mut Cow::Borrowed(&args_parser), args, track); |
| 645 | track.after_arm(false, &result); | |
| 697 | track.after_arm(WhichMatcher::Args, &result); | |
| 646 | 698 | |
| 647 | 699 | let mut named_matches = match result { |
| 648 | 700 | Success(named_matches) => named_matches, |
| ... | ... | @@ -650,12 +702,12 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>( |
| 650 | 702 | mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut()); |
| 651 | 703 | continue; |
| 652 | 704 | } |
| 653 | Error(_, _) => return Err(CanRetry::Yes), | |
| 705 | Ambiguity => return Err(CanRetry::Yes), | |
| 654 | 706 | ErrorReported(guar) => return Err(CanRetry::No(guar)), |
| 655 | 707 | }; |
| 656 | 708 | |
| 657 | 709 | let result = tt_parser.parse_tt(&mut Cow::Borrowed(&body_parser), body, track); |
| 658 | track.after_arm(true, &result); | |
| 710 | track.after_arm(WhichMatcher::Body, &result); | |
| 659 | 711 | |
| 660 | 712 | match result { |
| 661 | 713 | Success(body_named_matches) => { |
| ... | ... | @@ -667,7 +719,7 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>( |
| 667 | 719 | Failure(_) => { |
| 668 | 720 | mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut()) |
| 669 | 721 | } |
| 670 | Error(_, _) => return Err(CanRetry::Yes), | |
| 722 | Ambiguity => return Err(CanRetry::Yes), | |
| 671 | 723 | ErrorReported(guar) => return Err(CanRetry::No(guar)), |
| 672 | 724 | } |
| 673 | 725 | } |
| ... | ... | @@ -688,14 +740,14 @@ pub(super) fn try_match_macro_derive<'matcher, T: Tracker<'matcher>>( |
| 688 | 740 | ) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> { |
| 689 | 741 | // This uses the same strategy as `try_match_macro` |
| 690 | 742 | let body_parser = parser_from_cx(psess, body.clone(), T::recovery()); |
| 691 | let mut tt_parser = TtParser::new(name); | |
| 743 | let mut tt_parser = TtParser::new(); | |
| 692 | 744 | for (i, rule) in rules.iter().enumerate() { |
| 693 | 745 | let MacroRule::Derive { body, .. } = rule else { continue }; |
| 694 | 746 | |
| 695 | 747 | let mut gated_spans_snapshot = mem::take(&mut *psess.gated_spans.spans.borrow_mut()); |
| 696 | 748 | |
| 697 | 749 | let result = tt_parser.parse_tt(&mut Cow::Borrowed(&body_parser), body, track); |
| 698 | track.after_arm(true, &result); | |
| 750 | track.after_arm(WhichMatcher::FOR_DERIVE, &result); | |
| 699 | 751 | |
| 700 | 752 | match result { |
| 701 | 753 | Success(named_matches) => { |
| ... | ... | @@ -705,7 +757,7 @@ pub(super) fn try_match_macro_derive<'matcher, T: Tracker<'matcher>>( |
| 705 | 757 | Failure(_) => { |
| 706 | 758 | mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut()) |
| 707 | 759 | } |
| 708 | Error(_, _) => return Err(CanRetry::Yes), | |
| 760 | Ambiguity => return Err(CanRetry::Yes), | |
| 709 | 761 | ErrorReported(guar) => return Err(CanRetry::No(guar)), |
| 710 | 762 | } |
| 711 | 763 | } |