diff options
Diffstat (limited to 'compiler/rustc_expand/src')
| -rw-r--r-- | compiler/rustc_expand/src/mbe/macro_rules.rs | 35 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/mbe/transcribe.rs | 64 |
2 files changed, 63 insertions, 36 deletions
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index e9736d6f2c8..e9797abcbdf 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -10,7 +10,7 @@ use crate::mbe::transcribe::transcribe; use rustc_ast as ast; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*}; -use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; @@ -213,7 +213,7 @@ fn expand_macro<'cx>( let arm_span = rhses[i].span(); // rhs has holes ( `$id` and `$(...)` that need filled) - let mut tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) { + let tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) { Ok(tts) => tts, Err(mut err) => { err.emit(); @@ -221,37 +221,6 @@ fn expand_macro<'cx>( } }; - // Replace all the tokens for the corresponding positions in the macro, to maintain - // proper positions in error reporting, while maintaining the macro_backtrace. - if tts.len() == rhs.tts.len() { - tts = tts.map_enumerated_owned(|i, mut tt| { - let rhs_tt = &rhs.tts[i]; - let ctxt = tt.span().ctxt(); - match (&mut tt, rhs_tt) { - // preserve the delim spans if able - ( - TokenTree::Delimited(target_sp, ..), - mbe::TokenTree::Delimited(source_sp, ..), - ) => { - target_sp.open = source_sp.open.with_ctxt(ctxt); - target_sp.close = source_sp.close.with_ctxt(ctxt); - } - ( - TokenTree::Delimited(target_sp, ..), - mbe::TokenTree::MetaVar(source_sp, ..), - ) => { - target_sp.open = source_sp.with_ctxt(ctxt); - target_sp.close = source_sp.with_ctxt(ctxt).shrink_to_hi(); - } - _ => { - let sp = rhs_tt.span().with_ctxt(ctxt); - tt.set_span(sp); - } - } - tt - }); - } - if cx.trace_macros() { let msg = format!("to `{}`", pprust::tts_to_string(&tts)); trace_macros_note(&mut cx.expansions, sp, msg); diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index f2a9875ffd2..c969ca7ef89 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -4,7 +4,7 @@ use crate::errors::{ NoSyntaxVarsExprRepeat, VarStillRepeating, }; use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch}; -use crate::mbe::{self, MetaVarExpr}; +use crate::mbe::{self, KleeneOp, MetaVarExpr}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; @@ -42,6 +42,7 @@ enum Frame<'a> { tts: &'a [mbe::TokenTree], idx: usize, sep: Option<Token>, + kleene_op: KleeneOp, }, } @@ -207,7 +208,7 @@ pub(super) fn transcribe<'a>( // Is the repetition empty? if len == 0 { - if seq.kleene.op == mbe::KleeneOp::OneOrMore { + if seq.kleene.op == KleeneOp::OneOrMore { // FIXME: this really ought to be caught at macro definition // time... It happens when the Kleene operator in the matcher and // the body for the same meta-variable do not match. @@ -227,6 +228,7 @@ pub(super) fn transcribe<'a>( idx: 0, sep: seq.separator.clone(), tts: &delimited.tts, + kleene_op: seq.kleene.op, }); } } @@ -243,7 +245,7 @@ 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(tt.clone()); + result.push(maybe_use_metavar_location(cx, &stack, sp, tt)); } MatchedNonterminal(nt) => { // Other variables are emitted into the output stream as groups with @@ -308,6 +310,62 @@ pub(super) fn transcribe<'a>( } } +/// 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 +/// produced span a location that would be most useful in practice (the hygiene part of the span +/// must not be changed). +/// +/// Different locations are useful for different purposes: +/// - The original location is useful when we need to report a diagnostic for the original token in +/// isolation, without combining it with any surrounding tokens. This case occurs, but it is not +/// very common in practice. +/// - The metavariable location is useful when we need to somehow combine the token span with spans +/// of its surrounding tokens. This is the most common way to use token spans. +/// +/// So this function replaces the original location with the metavariable location in all cases +/// except these two: +/// - The metavariable is an element of undelimited sequence `$($tt)*`. +/// 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, + orig_tt: &TokenTree, +) -> TokenTree { + let undelimited_seq = matches!( + stack.last(), + Some(Frame::Sequence { + tts: [_], + sep: None, + kleene_op: KleeneOp::ZeroOrMore | KleeneOp::OneOrMore, + .. + }) + ); + if undelimited_seq || cx.source_map().is_imported(metavar_span) { + return orig_tt.clone(); + } + + match orig_tt { + TokenTree::Token(Token { kind, span }, spacing) => { + let span = metavar_span.with_ctxt(span.ctxt()); + 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 dspan = DelimSpan::from_pair(open, close); + TokenTree::Delimited(dspan, *dspacing, *delimiter, tts.clone()) + } + } +} + /// Lookup the meta-var named `ident` and return the matched token tree from the invocation using /// the set of matches `interpolations`. /// |
