@@ -228,7 +228,7 @@ pub(super) fn transcribe<'a>(
228
228
}
229
229
230
230
// Replace the meta-var with the matched token tree from the invocation.
231
- mbe:: TokenTree :: MetaVar ( mut sp, mut original_ident) => {
231
+ mbe:: TokenTree :: MetaVar ( mut sp, mut original_ident, undelimited_seq ) => {
232
232
// Find the matched nonterminal from the macro invocation, and use it to replace
233
233
// the meta-var.
234
234
let ident = MacroRulesNormalizedIdent :: new ( original_ident) ;
@@ -237,7 +237,7 @@ pub(super) fn transcribe<'a>(
237
237
MatchedTokenTree ( tt) => {
238
238
// `tt`s are emitted into the output stream directly as "raw tokens",
239
239
// without wrapping them into groups.
240
- result. push ( tt . clone ( ) ) ;
240
+ result. push ( maybe_use_metavar_location ( cx , tt , sp , * undelimited_seq ) ) ;
241
241
}
242
242
MatchedNonterminal ( nt) => {
243
243
// Other variables are emitted into the output stream as groups with
@@ -302,6 +302,54 @@ pub(super) fn transcribe<'a>(
302
302
}
303
303
}
304
304
305
+ /// Usually metavariables `$var` produce interpolated tokens, which have an additional place for
306
+ /// keeping both the original span and the metavariable span. For `tt` metavariables that's not the
307
+ /// case however, and there's no place for keeping a second span. So we try to give the single
308
+ /// produced span a location that would be most useful in practice (the hygiene part of the span
309
+ /// must not be changed).
310
+ ///
311
+ /// Different locations are useful for different purposes:
312
+ /// - The original location is useful when we need to report a diagnostic for the original token in
313
+ /// isolation, without combining it with any surrounding tokens. This case occurs, but it is not
314
+ /// very common in practice.
315
+ /// - The metavariable location is useful when we need to somehow combine the token span with spans
316
+ /// of its surrounding tokens. This is the most common way to use token spans.
317
+ ///
318
+ /// So this function replaces the original location with the metavariable location in all cases
319
+ /// except these two:
320
+ /// - The metavariable is an element of undelimited sequence `$($tt)*`.
321
+ /// These are typically used for passing larger amounts of code, and tokens in that code usually
322
+ /// combine with each other and not with tokens outside of the sequence.
323
+ /// - The metavariable span comes from a different crate, then we prefer the more local span.
324
+ ///
325
+ /// FIXME: Find a way to keep both original and metavariable spans for all tokens without
326
+ /// regressing compilation time too much. Several experiments for adding such spans were made in
327
+ /// the past (PR #95580, #118517, #118671) and all showed some regressions.
328
+ fn maybe_use_metavar_location (
329
+ cx : & ExtCtxt < ' _ > ,
330
+ orig_tt : & TokenTree ,
331
+ metavar_span : Span ,
332
+ undelimited_seq : bool ,
333
+ ) -> TokenTree {
334
+ if undelimited_seq || cx. source_map ( ) . is_imported ( metavar_span) {
335
+ return orig_tt. clone ( ) ;
336
+ }
337
+
338
+ match orig_tt {
339
+ TokenTree :: Token ( Token { kind, span } , spacing) => {
340
+ let span = metavar_span. with_ctxt ( span. ctxt ( ) ) ;
341
+ TokenTree :: Token ( Token { kind : kind. clone ( ) , span } , * spacing)
342
+ }
343
+ TokenTree :: Delimited ( dspan, dspacing, delimiter, tts) => {
344
+ let dspan = DelimSpan :: from_pair (
345
+ metavar_span. shrink_to_lo ( ) . with_ctxt ( dspan. open . ctxt ( ) ) ,
346
+ metavar_span. shrink_to_hi ( ) . with_ctxt ( dspan. close . ctxt ( ) ) ,
347
+ ) ;
348
+ TokenTree :: Delimited ( dspan, * dspacing, * delimiter, tts. clone ( ) )
349
+ }
350
+ }
351
+ }
352
+
305
353
/// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
306
354
/// the set of matches `interpolations`.
307
355
///
@@ -402,7 +450,7 @@ fn lockstep_iter_size(
402
450
size. with ( lockstep_iter_size ( tt, interpolations, repeats) )
403
451
} )
404
452
}
405
- TokenTree :: MetaVar ( _, name) | TokenTree :: MetaVarDecl ( _, name, _) => {
453
+ TokenTree :: MetaVar ( _, name, _ ) | TokenTree :: MetaVarDecl ( _, name, _) => {
406
454
let name = MacroRulesNormalizedIdent :: new ( * name) ;
407
455
match lookup_cur_matched ( name, interpolations, repeats) {
408
456
Some ( matched) => match matched {
0 commit comments