Skip to content

Commit 39584b1

Browse files
committed
Factor out matching into try_match_macro
This moves out the matching part of expansion into a new function. This function will try to match the macro and return an error if it failed to match. A tracker can be used to get more information about the matching.
1 parent 2f8a068 commit 39584b1

File tree

2 files changed

+129
-124
lines changed

2 files changed

+129
-124
lines changed

compiler/rustc_expand/src/mbe/macro_parser.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,11 @@ pub(crate) enum ParseResult<T> {
277277
/// A `ParseResult` where the `Success` variant contains a mapping of
278278
/// `MacroRulesNormalizedIdent`s to `NamedMatch`es. This represents the mapping
279279
/// of metavars to the token trees they bind to.
280-
pub(crate) type NamedParseResult = ParseResult<FxHashMap<MacroRulesNormalizedIdent, NamedMatch>>;
280+
pub(crate) type NamedParseResult = ParseResult<NamedMatches>;
281+
282+
/// Contains a mapping of `MacroRulesNormalizedIdent`s to `NamedMatch`es.
283+
/// This represents the mapping of metavars to the token trees they bind to.
284+
pub(crate) type NamedMatches = FxHashMap<MacroRulesNormalizedIdent, NamedMatch>;
281285

282286
/// Count how many metavars declarations are in `matcher`.
283287
pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize {

compiler/rustc_expand/src/mbe/macro_rules.rs

+124-123
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use rustc_ast::{NodeId, DUMMY_NODE_ID};
1414
use rustc_ast_pretty::pprust;
1515
use rustc_attr::{self as attr, TransparencyError};
1616
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
17-
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage};
17+
use rustc_errors::{
18+
Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
19+
};
1820
use rustc_feature::Features;
1921
use rustc_lint_defs::builtin::{
2022
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
@@ -33,7 +35,7 @@ use std::borrow::Cow;
3335
use std::collections::hash_map::Entry;
3436
use std::{mem, slice};
3537

36-
use super::macro_parser::NamedParseResult;
38+
use super::macro_parser::{NamedMatches, NamedParseResult};
3739

3840
pub(crate) struct ParserAnyMacro<'a> {
3941
parser: Parser<'a>,
@@ -253,9 +255,87 @@ fn expand_macro<'cx>(
253255
trace_macros_note(&mut cx.expansions, sp, msg);
254256
}
255257

256-
// Which arm's failure should we report? (the one furthest along)
257-
let mut best_failure: Option<(Token, &str)> = None;
258+
// Track nothing for the best performance
259+
let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut NoopTracker);
260+
261+
match try_success_result {
262+
Ok((i, named_matches)) => {
263+
let (rhs, rhs_span): (&mbe::Delimited, DelimSpan) = match &rhses[i] {
264+
mbe::TokenTree::Delimited(span, delimited) => (&delimited, *span),
265+
_ => cx.span_bug(sp, "malformed macro rhs"),
266+
};
267+
let arm_span = rhses[i].span();
268+
269+
let rhs_spans = rhs.tts.iter().map(|t| t.span()).collect::<Vec<_>>();
270+
// rhs has holes ( `$id` and `$(...)` that need filled)
271+
let mut tts = match transcribe(cx, &named_matches, &rhs, rhs_span, transparency) {
272+
Ok(tts) => tts,
273+
Err(mut err) => {
274+
err.emit();
275+
return DummyResult::any(arm_span);
276+
}
277+
};
278+
279+
// Replace all the tokens for the corresponding positions in the macro, to maintain
280+
// proper positions in error reporting, while maintaining the macro_backtrace.
281+
if rhs_spans.len() == tts.len() {
282+
tts = tts.map_enumerated(|i, tt| {
283+
let mut tt = tt.clone();
284+
let mut sp = rhs_spans[i];
285+
sp = sp.with_ctxt(tt.span().ctxt());
286+
tt.set_span(sp);
287+
tt
288+
});
289+
}
290+
291+
if cx.trace_macros() {
292+
let msg = format!("to `{}`", pprust::tts_to_string(&tts));
293+
trace_macros_note(&mut cx.expansions, sp, msg);
294+
}
295+
296+
let mut p = Parser::new(sess, tts, false, None);
297+
p.last_type_ascription = cx.current_expansion.prior_type_ascription;
298+
299+
if is_local {
300+
cx.resolver.record_macro_rule_usage(node_id, i);
301+
}
302+
303+
// Let the context choose how to interpret the result.
304+
// Weird, but useful for X-macros.
305+
return Box::new(ParserAnyMacro {
306+
parser: p,
307+
308+
// Pass along the original expansion site and the name of the macro
309+
// so we can print a useful error message if the parse of the expanded
310+
// macro leaves unparsed tokens.
311+
site_span: sp,
312+
macro_ident: name,
313+
lint_node_id: cx.current_expansion.lint_node_id,
314+
is_trailing_mac: cx.current_expansion.is_trailing_mac,
315+
arm_span,
316+
is_local,
317+
});
318+
}
319+
Err(()) => {
320+
todo!("Retry macro invocation while tracking diagnostics info and emit error");
321+
322+
return DummyResult::any(sp);
323+
}
324+
}
325+
326+
DummyResult::any(sp)
327+
}
258328

329+
/// Try expanding the macro. Returns the index of the sucessful arm and its named_matches if it was successful,
330+
/// and nothing if it failed. On failure, it's the callers job to use `track` accordingly to record all errors
331+
/// correctly.
332+
fn try_match_macro<'matcher, T: Tracker<'matcher>>(
333+
sess: &ParseSess,
334+
name: Ident,
335+
arg: &TokenStream,
336+
lhses: &'matcher [Vec<MatcherLoc>],
337+
track: &mut T,
338+
) -> Result<(usize, NamedMatches), ()> {
259339
// We create a base parser that can be used for the "black box" parts.
260340
// Every iteration needs a fresh copy of that parser. However, the parser
261341
// is not mutated on many of the iterations, particularly when dealing with
@@ -277,7 +357,6 @@ fn expand_macro<'cx>(
277357
// this situation.)
278358
// FIXME(Nilstrieb): Stop recovery from happening on this parser and retry later with recovery if the macro failed to match.
279359
let parser = parser_from_cx(sess, arg.clone());
280-
281360
// Try each arm's matchers.
282361
let mut tt_parser = TtParser::new(name);
283362
for (i, lhs) in lhses.iter().enumerate() {
@@ -287,115 +366,36 @@ fn expand_macro<'cx>(
287366
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
288367
let mut gated_spans_snapshot = mem::take(&mut *sess.gated_spans.spans.borrow_mut());
289368

290-
match tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker) {
369+
let result = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, track);
370+
371+
track.after_arm(&result);
372+
373+
match result {
291374
Success(named_matches) => {
292375
// The matcher was `Success(..)`ful.
293376
// Merge the gated spans from parsing the matcher with the pre-existing ones.
294377
sess.gated_spans.merge(gated_spans_snapshot);
295378

296-
let (rhs, rhs_span): (&mbe::Delimited, DelimSpan) = match &rhses[i] {
297-
mbe::TokenTree::Delimited(span, delimited) => (&delimited, *span),
298-
_ => cx.span_bug(sp, "malformed macro rhs"),
299-
};
300-
let arm_span = rhses[i].span();
301-
302-
let rhs_spans = rhs.tts.iter().map(|t| t.span()).collect::<Vec<_>>();
303-
// rhs has holes ( `$id` and `$(...)` that need filled)
304-
let mut tts = match transcribe(cx, &named_matches, &rhs, rhs_span, transparency) {
305-
Ok(tts) => tts,
306-
Err(mut err) => {
307-
err.emit();
308-
return DummyResult::any(arm_span);
309-
}
310-
};
311-
312-
// Replace all the tokens for the corresponding positions in the macro, to maintain
313-
// proper positions in error reporting, while maintaining the macro_backtrace.
314-
if rhs_spans.len() == tts.len() {
315-
tts = tts.map_enumerated(|i, tt| {
316-
let mut tt = tt.clone();
317-
let mut sp = rhs_spans[i];
318-
sp = sp.with_ctxt(tt.span().ctxt());
319-
tt.set_span(sp);
320-
tt
321-
});
322-
}
323-
324-
if cx.trace_macros() {
325-
let msg = format!("to `{}`", pprust::tts_to_string(&tts));
326-
trace_macros_note(&mut cx.expansions, sp, msg);
327-
}
328-
329-
let mut p = Parser::new(sess, tts, false, None);
330-
p.last_type_ascription = cx.current_expansion.prior_type_ascription;
331-
332-
if is_local {
333-
cx.resolver.record_macro_rule_usage(node_id, i);
334-
}
335-
336-
// Let the context choose how to interpret the result.
337-
// Weird, but useful for X-macros.
338-
return Box::new(ParserAnyMacro {
339-
parser: p,
340-
341-
// Pass along the original expansion site and the name of the macro
342-
// so we can print a useful error message if the parse of the expanded
343-
// macro leaves unparsed tokens.
344-
site_span: sp,
345-
macro_ident: name,
346-
lint_node_id: cx.current_expansion.lint_node_id,
347-
is_trailing_mac: cx.current_expansion.is_trailing_mac,
348-
arm_span,
349-
is_local,
350-
});
379+
return Ok((i, named_matches));
351380
}
352-
Failure(token, msg) => match best_failure {
353-
Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {}
354-
_ => best_failure = Some((token, msg)),
355-
},
356-
Error(err_sp, ref msg) => {
357-
let span = err_sp.substitute_dummy(sp);
358-
cx.struct_span_err(span, &msg).emit();
359-
return DummyResult::any(span);
381+
Failure(_, _) => {
382+
// Try the next arm
383+
}
384+
Error(_, _) => {
385+
// We haven't emitted an error yet
386+
return Err(());
387+
}
388+
ErrorReported(_) => {
389+
return Err(());
360390
}
361-
ErrorReported(_) => return DummyResult::any(sp),
362391
}
363392

364393
// The matcher was not `Success(..)`ful.
365394
// Restore to the state before snapshotting and maybe try again.
366395
mem::swap(&mut gated_spans_snapshot, &mut sess.gated_spans.spans.borrow_mut());
367396
}
368-
drop(parser);
369-
370-
let (token, label) = best_failure.expect("ran no matchers");
371-
let span = token.span.substitute_dummy(sp);
372-
let mut err = cx.struct_span_err(span, &parse_failure_msg(&token));
373-
err.span_label(span, label);
374-
if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
375-
err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
376-
}
377-
annotate_doc_comment(&mut err, sess.source_map(), span);
378-
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
379-
if let Some((arg, comma_span)) = arg.add_comma() {
380-
for lhs in lhses {
381-
let parser = parser_from_cx(sess, arg.clone());
382-
if let Success(_) = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker) {
383-
if comma_span.is_dummy() {
384-
err.note("you might be missing a comma");
385-
} else {
386-
err.span_suggestion_short(
387-
comma_span,
388-
"missing comma here",
389-
", ",
390-
Applicability::MachineApplicable,
391-
);
392-
}
393-
}
394-
}
395-
}
396-
err.emit();
397-
cx.trace_macros_diag();
398-
DummyResult::any(sp)
397+
398+
Err(())
399399
}
400400

401401
// Note that macro-by-example's input is also matched against a token tree:
@@ -477,28 +477,29 @@ pub fn compile_declarative_macro(
477477
let parser = Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS);
478478
let mut tt_parser =
479479
TtParser::new(Ident::with_dummy_span(if macro_rules { kw::MacroRules } else { kw::Macro }));
480-
let argument_map = match tt_parser.parse_tt(&mut Cow::Borrowed(&parser), &argument_gram, &mut NoopTracker) {
481-
Success(m) => m,
482-
Failure(token, msg) => {
483-
let s = parse_failure_msg(&token);
484-
let sp = token.span.substitute_dummy(def.span);
485-
let mut err = sess.parse_sess.span_diagnostic.struct_span_err(sp, &s);
486-
err.span_label(sp, msg);
487-
annotate_doc_comment(&mut err, sess.source_map(), sp);
488-
err.emit();
489-
return dummy_syn_ext();
490-
}
491-
Error(sp, msg) => {
492-
sess.parse_sess
493-
.span_diagnostic
494-
.struct_span_err(sp.substitute_dummy(def.span), &msg)
495-
.emit();
496-
return dummy_syn_ext();
497-
}
498-
ErrorReported(_) => {
499-
return dummy_syn_ext();
500-
}
501-
};
480+
let argument_map =
481+
match tt_parser.parse_tt(&mut Cow::Borrowed(&parser), &argument_gram, &mut NoopTracker) {
482+
Success(m) => m,
483+
Failure(token, msg) => {
484+
let s = parse_failure_msg(&token);
485+
let sp = token.span.substitute_dummy(def.span);
486+
let mut err = sess.parse_sess.span_diagnostic.struct_span_err(sp, &s);
487+
err.span_label(sp, msg);
488+
annotate_doc_comment(&mut err, sess.source_map(), sp);
489+
err.emit();
490+
return dummy_syn_ext();
491+
}
492+
Error(sp, msg) => {
493+
sess.parse_sess
494+
.span_diagnostic
495+
.struct_span_err(sp.substitute_dummy(def.span), &msg)
496+
.emit();
497+
return dummy_syn_ext();
498+
}
499+
ErrorReported(_) => {
500+
return dummy_syn_ext();
501+
}
502+
};
502503

503504
let mut valid = true;
504505

0 commit comments

Comments
 (0)