@@ -4,7 +4,7 @@ use crate::errors::{
44 NoSyntaxVarsExprRepeat , VarStillRepeating ,
55} ;
66use crate :: mbe:: macro_parser:: { MatchedNonterminal , MatchedSeq , MatchedTokenTree , NamedMatch } ;
7- use crate :: mbe:: { self , MetaVarExpr } ;
7+ use crate :: mbe:: { self , KleeneOp , MetaVarExpr } ;
88use rustc_ast:: mut_visit:: { self , MutVisitor } ;
99use rustc_ast:: token:: { self , Delimiter , Token , TokenKind } ;
1010use rustc_ast:: tokenstream:: { DelimSpacing , DelimSpan , Spacing , TokenStream , TokenTree } ;
@@ -42,6 +42,7 @@ enum Frame<'a> {
4242 tts : & ' a [ mbe:: TokenTree ] ,
4343 idx : usize ,
4444 sep : Option < Token > ,
45+ kleene_op : KleeneOp ,
4546 } ,
4647}
4748
@@ -203,7 +204,7 @@ pub(super) fn transcribe<'a>(
203204
204205 // Is the repetition empty?
205206 if len == 0 {
206- if seq. kleene . op == mbe :: KleeneOp :: OneOrMore {
207+ if seq. kleene . op == KleeneOp :: OneOrMore {
207208 // FIXME: this really ought to be caught at macro definition
208209 // time... It happens when the Kleene operator in the matcher and
209210 // the body for the same meta-variable do not match.
@@ -221,6 +222,7 @@ pub(super) fn transcribe<'a>(
221222 idx : 0 ,
222223 sep : seq. separator . clone ( ) ,
223224 tts : & delimited. tts ,
225+ kleene_op : seq. kleene . op ,
224226 } ) ;
225227 }
226228 }
@@ -237,7 +239,7 @@ pub(super) fn transcribe<'a>(
237239 MatchedTokenTree ( tt) => {
238240 // `tt`s are emitted into the output stream directly as "raw tokens",
239241 // without wrapping them into groups.
240- result. push ( tt . clone ( ) ) ;
242+ result. push ( maybe_use_metavar_location ( cx , & stack , sp , tt ) ) ;
241243 }
242244 MatchedNonterminal ( nt) => {
243245 // Other variables are emitted into the output stream as groups with
@@ -302,6 +304,63 @@ pub(super) fn transcribe<'a>(
302304 }
303305}
304306
307+ /// Usually metavariables `$var` produce interpolated tokens, which have an additional place for
308+ /// keeping both the original span and the metavariable span. For `tt` metavariables that's not the
309+ /// case however, and there's no place for keeping a second span. So we try to give the single
310+ /// produced span a location that would be most useful in practice (the hygiene part of the span
311+ /// must not be changed).
312+ ///
313+ /// Different locations are useful for different purposes:
314+ /// - The original location is useful when we need to report a diagnostic for the original token in
315+ /// isolation, without combining it with any surrounding tokens. This case occurs, but it is not
316+ /// very common in practice.
317+ /// - The metavariable location is useful when we need to somehow combine the token span with spans
318+ /// of its surrounding tokens. This is the most common way to use token spans.
319+ ///
320+ /// So this function replaces the original location with the metavariable location in all cases
321+ /// except these two:
322+ /// - The metavariable is an element of undelimited sequence `$($tt)*`.
323+ /// These are typically used for passing larger amounts of code, and tokens in that code usually
324+ /// combine with each other and not with tokens outside of the sequence.
325+ /// - The metavariable span comes from a different crate, then we prefer the more local span.
326+ ///
327+ /// FIXME: Find a way to keep both original and metavariable spans for all tokens without
328+ /// regressing compilation time too much. Several experiments for adding such spans were made in
329+ /// the past (PR #95580, #118517, #118671) and all showed some regressions.
330+ fn maybe_use_metavar_location (
331+ cx : & ExtCtxt < ' _ > ,
332+ stack : & [ Frame < ' _ > ] ,
333+ metavar_span : Span ,
334+ orig_tt : & TokenTree ,
335+ ) -> TokenTree {
336+ let undelimited_seq = matches ! (
337+ stack. last( ) ,
338+ Some ( Frame :: Sequence {
339+ tts: [ _] ,
340+ sep: None ,
341+ kleene_op: KleeneOp :: ZeroOrMore | KleeneOp :: OneOrMore ,
342+ ..
343+ } )
344+ ) ;
345+ if undelimited_seq || cx. source_map ( ) . is_imported ( metavar_span) {
346+ return orig_tt. clone ( ) ;
347+ }
348+
349+ match orig_tt {
350+ TokenTree :: Token ( Token { kind, span } , spacing) => {
351+ let span = metavar_span. with_ctxt ( span. ctxt ( ) ) ;
352+ TokenTree :: Token ( Token { kind : kind. clone ( ) , span } , * spacing)
353+ }
354+ TokenTree :: Delimited ( dspan, dspacing, delimiter, tts) => {
355+ let dspan = DelimSpan :: from_pair (
356+ metavar_span. shrink_to_lo ( ) . with_ctxt ( dspan. open . ctxt ( ) ) ,
357+ metavar_span. shrink_to_hi ( ) . with_ctxt ( dspan. close . ctxt ( ) ) ,
358+ ) ;
359+ TokenTree :: Delimited ( dspan, * dspacing, * delimiter, tts. clone ( ) )
360+ }
361+ }
362+ }
363+
305364/// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
306365/// the set of matches `interpolations`.
307366///
0 commit comments