@@ -4,7 +4,7 @@ use crate::errors::{
4
4
NoSyntaxVarsExprRepeat , VarStillRepeating ,
5
5
} ;
6
6
use crate :: mbe:: macro_parser:: { MatchedNonterminal , MatchedSeq , MatchedTokenTree , NamedMatch } ;
7
- use crate :: mbe:: { self , MetaVarExpr } ;
7
+ use crate :: mbe:: { self , KleeneOp , MetaVarExpr } ;
8
8
use rustc_ast:: mut_visit:: { self , MutVisitor } ;
9
9
use rustc_ast:: token:: { self , Delimiter , Token , TokenKind } ;
10
10
use rustc_ast:: tokenstream:: { DelimSpacing , DelimSpan , Spacing , TokenStream , TokenTree } ;
@@ -42,6 +42,7 @@ enum Frame<'a> {
42
42
tts : & ' a [ mbe:: TokenTree ] ,
43
43
idx : usize ,
44
44
sep : Option < Token > ,
45
+ kleene_op : KleeneOp ,
45
46
} ,
46
47
}
47
48
@@ -203,7 +204,7 @@ pub(super) fn transcribe<'a>(
203
204
204
205
// Is the repetition empty?
205
206
if len == 0 {
206
- if seq. kleene . op == mbe :: KleeneOp :: OneOrMore {
207
+ if seq. kleene . op == KleeneOp :: OneOrMore {
207
208
// FIXME: this really ought to be caught at macro definition
208
209
// time... It happens when the Kleene operator in the matcher and
209
210
// the body for the same meta-variable do not match.
@@ -221,6 +222,7 @@ pub(super) fn transcribe<'a>(
221
222
idx : 0 ,
222
223
sep : seq. separator . clone ( ) ,
223
224
tts : & delimited. tts ,
225
+ kleene_op : seq. kleene . op ,
224
226
} ) ;
225
227
}
226
228
}
@@ -237,7 +239,7 @@ pub(super) fn transcribe<'a>(
237
239
MatchedTokenTree ( tt) => {
238
240
// `tt`s are emitted into the output stream directly as "raw tokens",
239
241
// without wrapping them into groups.
240
- result. push ( tt . clone ( ) ) ;
242
+ result. push ( maybe_use_metavar_location ( cx , & stack , sp , tt ) ) ;
241
243
}
242
244
MatchedNonterminal ( nt) => {
243
245
// Other variables are emitted into the output stream as groups with
@@ -302,6 +304,63 @@ pub(super) fn transcribe<'a>(
302
304
}
303
305
}
304
306
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
+
305
364
/// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
306
365
/// the set of matches `interpolations`.
307
366
///
0 commit comments