@@ -72,6 +72,7 @@ pub mod fatal_error;
7272
7373pub mod profiling;
7474
75+ use rustc_data_structures:: fx:: FxHashMap ;
7576use rustc_data_structures:: stable_hasher:: { Hash128 , Hash64 , HashStable , StableHasher } ;
7677use rustc_data_structures:: sync:: { FreezeLock , FreezeWriteGuard , Lock , Lrc } ;
7778
@@ -98,6 +99,9 @@ mod tests;
9899pub struct SessionGlobals {
99100 symbol_interner : symbol:: Interner ,
100101 span_interner : Lock < span_encoding:: SpanInterner > ,
102+ /// Maps a macro argument token into use of the corresponding metavariable in the macro body.
103+ /// Collisions are possible and processed in `maybe_use_metavar_location` on best effort basis.
104+ metavar_spans : Lock < FxHashMap < Span , Span > > ,
101105 hygiene_data : Lock < hygiene:: HygieneData > ,
102106
103107 /// A reference to the source map in the `Session`. It's an `Option`
@@ -115,6 +119,7 @@ impl SessionGlobals {
115119 SessionGlobals {
116120 symbol_interner : symbol:: Interner :: fresh ( ) ,
117121 span_interner : Lock :: new ( span_encoding:: SpanInterner :: default ( ) ) ,
122+ metavar_spans : Default :: default ( ) ,
118123 hygiene_data : Lock :: new ( hygiene:: HygieneData :: new ( edition) ) ,
119124 source_map : Lock :: new ( None ) ,
120125 }
@@ -168,6 +173,11 @@ pub fn create_default_session_globals_then<R>(f: impl FnOnce() -> R) -> R {
168173// deserialization.
169174scoped_tls:: scoped_thread_local!( static SESSION_GLOBALS : SessionGlobals ) ;
170175
176+ #[ inline]
177+ pub fn with_metavar_spans < R > ( f : impl FnOnce ( & mut FxHashMap < Span , Span > ) -> R ) -> R {
178+ with_session_globals ( |session_globals| f ( & mut session_globals. metavar_spans . lock ( ) ) )
179+ }
180+
171181// FIXME: We should use this enum or something like it to get rid of the
172182// use of magic `/rust/1.x/...` paths across the board.
173183#[ derive( Debug , Eq , PartialEq , Clone , Ord , PartialOrd , Decodable ) ]
@@ -824,29 +834,64 @@ impl Span {
824834 )
825835 }
826836
837+ /// Check if you can select metavar spans for the given spans to get matching contexts.
838+ fn try_metavars ( a : SpanData , b : SpanData , a_orig : Span , b_orig : Span ) -> ( SpanData , SpanData ) {
839+ let get = |mspans : & FxHashMap < _ , _ > , s| mspans. get ( & s) . copied ( ) ;
840+ match with_metavar_spans ( |mspans| ( get ( mspans, a_orig) , get ( mspans, b_orig) ) ) {
841+ ( None , None ) => { }
842+ ( Some ( meta_a) , None ) => {
843+ let meta_a = meta_a. data ( ) ;
844+ if meta_a. ctxt == b. ctxt {
845+ return ( meta_a, b) ;
846+ }
847+ }
848+ ( None , Some ( meta_b) ) => {
849+ let meta_b = meta_b. data ( ) ;
850+ if a. ctxt == meta_b. ctxt {
851+ return ( a, meta_b) ;
852+ }
853+ }
854+ ( Some ( meta_a) , Some ( meta_b) ) => {
855+ let meta_b = meta_b. data ( ) ;
856+ if a. ctxt == meta_b. ctxt {
857+ return ( a, meta_b) ;
858+ }
859+ let meta_a = meta_a. data ( ) ;
860+ if meta_a. ctxt == b. ctxt {
861+ return ( meta_a, b) ;
862+ } else if meta_a. ctxt == meta_b. ctxt {
863+ return ( meta_a, meta_b) ;
864+ }
865+ }
866+ }
867+
868+ ( a, b)
869+ }
870+
827871 /// Prepare two spans to a combine operation like `to` or `between`.
828- /// FIXME: consider using declarative macro metavariable spans for the given spans if they are
829- /// better suitable for combining (#119412).
830872 fn prepare_to_combine (
831873 a_orig : Span ,
832874 b_orig : Span ,
833875 ) -> Result < ( SpanData , SpanData , Option < LocalDefId > ) , Span > {
834876 let ( a, b) = ( a_orig. data ( ) , b_orig. data ( ) ) ;
877+ if a. ctxt == b. ctxt {
878+ return Ok ( ( a, b, if a. parent == b. parent { a. parent } else { None } ) ) ;
879+ }
835880
836- if a. ctxt != b. ctxt {
837- // Context mismatches usually happen when procedural macros combine spans copied from
838- // the macro input with spans produced by the macro (`Span::*_site`).
839- // In that case we consider the combined span to be produced by the macro and return
840- // the original macro-produced span as the result.
841- // Otherwise we just fall back to returning the first span.
842- // Combining locations typically doesn't make sense in case of context mismatches.
843- // `is_root` here is a fast path optimization.
844- let a_is_callsite = a. ctxt . is_root ( ) || a. ctxt == b. span ( ) . source_callsite ( ) . ctxt ( ) ;
845- return Err ( if a_is_callsite { b_orig } else { a_orig } ) ;
881+ let ( a, b) = Span :: try_metavars ( a, b, a_orig, b_orig) ;
882+ if a. ctxt == b. ctxt {
883+ return Ok ( ( a, b, if a. parent == b. parent { a. parent } else { None } ) ) ;
846884 }
847885
848- let parent = if a. parent == b. parent { a. parent } else { None } ;
849- Ok ( ( a, b, parent) )
886+ // Context mismatches usually happen when procedural macros combine spans copied from
887+ // the macro input with spans produced by the macro (`Span::*_site`).
888+ // In that case we consider the combined span to be produced by the macro and return
889+ // the original macro-produced span as the result.
890+ // Otherwise we just fall back to returning the first span.
891+ // Combining locations typically doesn't make sense in case of context mismatches.
892+ // `is_root` here is a fast path optimization.
893+ let a_is_callsite = a. ctxt . is_root ( ) || a. ctxt == b. span ( ) . source_callsite ( ) . ctxt ( ) ;
894+ Err ( if a_is_callsite { b_orig } else { a_orig } )
850895 }
851896
852897 /// This span, but in a larger context, may switch to the metavariable span if suitable.
0 commit comments