diff options
Diffstat (limited to 'compiler/rustc_expand/src')
| -rw-r--r-- | compiler/rustc_expand/src/errors.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/mbe/quoted.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/mbe/transcribe.rs | 55 |
4 files changed, 65 insertions, 19 deletions
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 2584ff62e98..929f3479466 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -408,3 +408,13 @@ pub struct DuplicateMatcherBinding { #[label(expand_label2)] pub prev: Span, } + +#[derive(Diagnostic)] +#[diag(expand_invalid_fragment_specifier)] +#[help] +pub struct InvalidFragmentSpecifier { + #[primary_span] + pub span: Span, + pub fragment: Ident, + pub help: String, +} diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 4da86d77dc8..0e73abc9ed8 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -6,6 +6,7 @@ #![feature(if_let_guard)] #![feature(let_chains)] #![feature(macro_metavar_expr)] +#![feature(map_try_insert)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] #![feature(proc_macro_span)] diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 4824b67d277..0fdfa563138 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -1,3 +1,4 @@ +use crate::errors; use crate::mbe::macro_parser::count_metavar_decls; use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree}; @@ -60,11 +61,11 @@ pub(super) fn parse( Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => { match trees.next() { Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { - Some((frag, _)) => { + Some((fragment, _)) => { let span = token.span.with_lo(start_sp.lo()); let kind = - token::NonterminalKind::from_symbol(frag.name, || { + token::NonterminalKind::from_symbol(fragment.name, || { // FIXME(#85708) - once we properly decode a foreign // crate's `SyntaxContext::root`, then we can replace // this with just `span.edition()`. A @@ -81,14 +82,13 @@ pub(super) fn parse( }) .unwrap_or_else( || { - let msg = format!( - "invalid fragment specifier `{}`", - frag.name + sess.dcx().emit_err( + errors::InvalidFragmentSpecifier { + span, + fragment, + help: VALID_FRAGMENT_NAMES_MSG.into(), + }, ); - sess.dcx() - .struct_span_err(span, msg) - .with_help(VALID_FRAGMENT_NAMES_MSG) - .emit(); token::NonterminalKind::Ident }, ); diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 434891ebc76..519e4a634d8 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -13,7 +13,7 @@ use rustc_errors::DiagnosticBuilder; use rustc_errors::{pluralize, PResult}; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; -use rustc_span::{Span, SyntaxContext}; +use rustc_span::{with_metavar_spans, Span, SyntaxContext}; use smallvec::{smallvec, SmallVec}; use std::mem; @@ -254,7 +254,8 @@ pub(super) fn transcribe<'a>( MatchedTokenTree(tt) => { // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. - result.push(maybe_use_metavar_location(cx, &stack, sp, tt)); + let tt = maybe_use_metavar_location(cx, &stack, sp, tt, &mut marker); + result.push(tt); } MatchedNonterminal(nt) => { // Other variables are emitted into the output stream as groups with @@ -319,6 +320,17 @@ pub(super) fn transcribe<'a>( } } +/// Store the metavariable span for this original span into a side table. +/// FIXME: Try to put the metavariable span into `SpanData` instead of a side table (#118517). +/// An optimal encoding for inlined spans will need to be selected to minimize regressions. +/// The side table approach is relatively good, but not perfect due to collisions. +/// In particular, collisions happen when token is passed as an argument through several macro +/// calls, like in recursive macros. +/// The old heuristic below is used to improve spans in case of collisions, but diagnostics are +/// still degraded sometimes in those cases. +/// +/// The old heuristic: +/// /// Usually metavariables `$var` produce interpolated tokens, which have an additional place for /// keeping both the original span and the metavariable span. For `tt` metavariables that's not the /// case however, and there's no place for keeping a second span. So we try to give the single @@ -338,15 +350,12 @@ pub(super) fn transcribe<'a>( /// These are typically used for passing larger amounts of code, and tokens in that code usually /// combine with each other and not with tokens outside of the sequence. /// - The metavariable span comes from a different crate, then we prefer the more local span. -/// -/// FIXME: Find a way to keep both original and metavariable spans for all tokens without -/// regressing compilation time too much. Several experiments for adding such spans were made in -/// the past (PR #95580, #118517, #118671) and all showed some regressions. fn maybe_use_metavar_location( cx: &ExtCtxt<'_>, stack: &[Frame<'_>], - metavar_span: Span, + mut metavar_span: Span, orig_tt: &TokenTree, + marker: &mut Marker, ) -> TokenTree { let undelimited_seq = matches!( stack.last(), @@ -357,18 +366,44 @@ fn maybe_use_metavar_location( .. }) ); - if undelimited_seq || cx.source_map().is_imported(metavar_span) { + if undelimited_seq { + // Do not record metavar spans for tokens from undelimited sequences, for perf reasons. + return orig_tt.clone(); + } + + let insert = |mspans: &mut FxHashMap<_, _>, s, ms| match mspans.try_insert(s, ms) { + Ok(_) => true, + Err(err) => *err.entry.get() == ms, // Tried to insert the same span, still success + }; + marker.visit_span(&mut metavar_span); + let no_collision = match orig_tt { + TokenTree::Token(token, ..) => { + with_metavar_spans(|mspans| insert(mspans, token.span, metavar_span)) + } + TokenTree::Delimited(dspan, ..) => with_metavar_spans(|mspans| { + insert(mspans, dspan.open, metavar_span) + && insert(mspans, dspan.close, metavar_span) + && insert(mspans, dspan.entire(), metavar_span) + }), + }; + if no_collision || cx.source_map().is_imported(metavar_span) { return orig_tt.clone(); } + // Setting metavar spans for the heuristic spans gives better opportunities for combining them + // with neighboring spans even despite their different syntactic contexts. match orig_tt { TokenTree::Token(Token { kind, span }, spacing) => { let span = metavar_span.with_ctxt(span.ctxt()); + with_metavar_spans(|mspans| insert(mspans, span, metavar_span)); TokenTree::Token(Token { kind: kind.clone(), span }, *spacing) } TokenTree::Delimited(dspan, dspacing, delimiter, tts) => { - let open = metavar_span.shrink_to_lo().with_ctxt(dspan.open.ctxt()); - let close = metavar_span.shrink_to_hi().with_ctxt(dspan.close.ctxt()); + let open = metavar_span.with_ctxt(dspan.open.ctxt()); + let close = metavar_span.with_ctxt(dspan.close.ctxt()); + with_metavar_spans(|mspans| { + insert(mspans, open, metavar_span) && insert(mspans, close, metavar_span) + }); let dspan = DelimSpan::from_pair(open, close); TokenTree::Delimited(dspan, *dspacing, *delimiter, tts.clone()) } |
