@@ -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 } ;
@@ -13,7 +13,7 @@ use rustc_errors::DiagnosticBuilder;
13
13
use rustc_errors:: { pluralize, PResult } ;
14
14
use rustc_span:: hygiene:: { LocalExpnId , Transparency } ;
15
15
use rustc_span:: symbol:: { sym, Ident , MacroRulesNormalizedIdent } ;
16
- use rustc_span:: Span ;
16
+ use rustc_span:: { try_insert_metavar_span , Span } ;
17
17
18
18
use smallvec:: { smallvec, SmallVec } ;
19
19
use std:: mem;
@@ -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
@@ -207,7 +208,7 @@ pub(super) fn transcribe<'a>(
207
208
208
209
// Is the repetition empty?
209
210
if len == 0 {
210
- if seq. kleene . op == mbe :: KleeneOp :: OneOrMore {
211
+ if seq. kleene . op == KleeneOp :: OneOrMore {
211
212
// FIXME: this really ought to be caught at macro definition
212
213
// time... It happens when the Kleene operator in the matcher and
213
214
// the body for the same meta-variable do not match.
@@ -227,6 +228,7 @@ pub(super) fn transcribe<'a>(
227
228
idx : 0 ,
228
229
sep : seq. separator . clone ( ) ,
229
230
tts : & delimited. tts ,
231
+ kleene_op : seq. kleene . op ,
230
232
} ) ;
231
233
}
232
234
}
@@ -243,7 +245,8 @@ pub(super) fn transcribe<'a>(
243
245
MatchedTokenTree ( tt) => {
244
246
// `tt`s are emitted into the output stream directly as "raw tokens",
245
247
// without wrapping them into groups.
246
- result. push ( tt. clone ( ) ) ;
248
+ marker. visit_span ( & mut sp) ;
249
+ result. push ( maybe_use_metavar_location ( cx, & stack, sp, tt) ) ;
247
250
}
248
251
MatchedNonterminal ( nt) => {
249
252
// Other variables are emitted into the output stream as groups with
@@ -308,6 +311,82 @@ pub(super) fn transcribe<'a>(
308
311
}
309
312
}
310
313
314
+ /// Store the metavariable span for this original span into a side table.
315
+ /// FIXME: Try to put the metavariable span into `SpanData` instead of a side table (#118517).
316
+ /// An optimal encoding for inlined spans will need to be selected to minimize regressions.
317
+ /// The side table approach is relatively good, but not perfect due to collisions.
318
+ /// The old heuristic below is used to improve spans in case of collisions, but diagnostics are
319
+ /// still degraded sometimes in those cases.
320
+ ///
321
+ /// The old heuristic:
322
+ ///
323
+ /// Usually metavariables `$var` produce interpolated tokens, which have an additional place for
324
+ /// keeping both the original span and the metavariable span. For `tt` metavariables that's not the
325
+ /// case however, and there's no place for keeping a second span. So we try to give the single
326
+ /// produced span a location that would be most useful in practice (the hygiene part of the span
327
+ /// must not be changed).
328
+ ///
329
+ /// Different locations are useful for different purposes:
330
+ /// - The original location is useful when we need to report a diagnostic for the original token in
331
+ /// isolation, without combining it with any surrounding tokens. This case occurs, but it is not
332
+ /// very common in practice.
333
+ /// - The metavariable location is useful when we need to somehow combine the token span with spans
334
+ /// of its surrounding tokens. This is the most common way to use token spans.
335
+ ///
336
+ /// So this function replaces the original location with the metavariable location in all cases
337
+ /// except these two:
338
+ /// - The metavariable is an element of undelimited sequence `$($tt)*`.
339
+ /// These are typically used for passing larger amounts of code, and tokens in that code usually
340
+ /// combine with each other and not with tokens outside of the sequence.
341
+ /// - The metavariable span comes from a different crate, then we prefer the more local span.
342
+ fn maybe_use_metavar_location (
343
+ cx : & ExtCtxt < ' _ > ,
344
+ stack : & [ Frame < ' _ > ] ,
345
+ metavar_span : Span ,
346
+ orig_tt : & TokenTree ,
347
+ ) -> TokenTree {
348
+ let no_collision = match orig_tt {
349
+ TokenTree :: Token ( token, ..) => try_insert_metavar_span ( token. span , metavar_span) ,
350
+ TokenTree :: Delimited ( dspan, ..) => {
351
+ try_insert_metavar_span ( dspan. open , metavar_span)
352
+ && try_insert_metavar_span ( dspan. close , metavar_span)
353
+ && try_insert_metavar_span ( dspan. entire ( ) , metavar_span)
354
+ }
355
+ } ;
356
+ let undelimited_seq = || {
357
+ matches ! (
358
+ stack. last( ) ,
359
+ Some ( Frame :: Sequence {
360
+ tts: [ _] ,
361
+ sep: None ,
362
+ kleene_op: KleeneOp :: ZeroOrMore | KleeneOp :: OneOrMore ,
363
+ ..
364
+ } )
365
+ )
366
+ } ;
367
+ if no_collision || undelimited_seq ( ) || cx. source_map ( ) . is_imported ( metavar_span) {
368
+ return orig_tt. clone ( ) ;
369
+ }
370
+
371
+ // Setting metavar spans for the heuristic spans gives better opportunities for combining them
372
+ // with neighboring spans even despite their different syntactic contexts.
373
+ match orig_tt {
374
+ TokenTree :: Token ( Token { kind, span } , spacing) => {
375
+ let span = metavar_span. with_ctxt ( span. ctxt ( ) ) ;
376
+ let _ = try_insert_metavar_span ( span, metavar_span) ;
377
+ TokenTree :: Token ( Token { kind : kind. clone ( ) , span } , * spacing)
378
+ }
379
+ TokenTree :: Delimited ( dspan, dspacing, delimiter, tts) => {
380
+ let open = metavar_span. with_ctxt ( dspan. open . ctxt ( ) ) ;
381
+ let close = metavar_span. with_ctxt ( dspan. close . ctxt ( ) ) ;
382
+ let _ = try_insert_metavar_span ( open, metavar_span) ;
383
+ let _ = try_insert_metavar_span ( close, metavar_span) ;
384
+ let dspan = DelimSpan :: from_pair ( open, close) ;
385
+ TokenTree :: Delimited ( dspan, * dspacing, * delimiter, tts. clone ( ) )
386
+ }
387
+ }
388
+ }
389
+
311
390
/// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
312
391
/// the set of matches `interpolations`.
313
392
///
0 commit comments