2626#![ feature( core_io_borrowed_buf) ]
2727#![ feature( if_let_guard) ]
2828#![ feature( let_chains) ]
29+ #![ feature( map_try_insert) ]
2930#![ feature( min_specialization) ]
3031#![ feature( negative_impls) ]
3132#![ feature( new_uninit) ]
@@ -74,6 +75,7 @@ pub mod fatal_error;
7475
7576pub mod profiling;
7677
78+ use rustc_data_structures:: fx:: FxHashMap ;
7779use rustc_data_structures:: stable_hasher:: { Hash128 , Hash64 , HashStable , StableHasher } ;
7880use rustc_data_structures:: sync:: { FreezeLock , FreezeWriteGuard , Lock , Lrc } ;
7981
@@ -100,6 +102,7 @@ mod tests;
100102pub struct SessionGlobals {
101103 symbol_interner : symbol:: Interner ,
102104 span_interner : Lock < span_encoding:: SpanInterner > ,
105+ metavar_spans : Lock < FxHashMap < Span , Span > > ,
103106 hygiene_data : Lock < hygiene:: HygieneData > ,
104107
105108 /// A reference to the source map in the `Session`. It's an `Option`
@@ -117,6 +120,7 @@ impl SessionGlobals {
117120 SessionGlobals {
118121 symbol_interner : symbol:: Interner :: fresh ( ) ,
119122 span_interner : Lock :: new ( span_encoding:: SpanInterner :: default ( ) ) ,
123+ metavar_spans : Default :: default ( ) ,
120124 hygiene_data : Lock :: new ( hygiene:: HygieneData :: new ( edition) ) ,
121125 source_map : Lock :: new ( None ) ,
122126 }
@@ -170,6 +174,11 @@ pub fn create_default_session_globals_then<R>(f: impl FnOnce() -> R) -> R {
170174// deserialization.
171175scoped_tls:: scoped_thread_local!( static SESSION_GLOBALS : SessionGlobals ) ;
172176
177+ #[ inline]
178+ pub fn with_metavar_spans < R > ( f : impl FnOnce ( & mut FxHashMap < Span , Span > ) -> R ) -> R {
179+ with_session_globals ( |session_globals| f ( & mut session_globals. metavar_spans . lock ( ) ) )
180+ }
181+
173182// FIXME: We should use this enum or something like it to get rid of the
174183// use of magic `/rust/1.x/...` paths across the board.
175184#[ derive( Debug , Eq , PartialEq , Clone , Ord , PartialOrd , Decodable ) ]
@@ -825,29 +834,64 @@ impl Span {
825834 )
826835 }
827836
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+
828871 /// Prepare two spans to a combine operation like `to` or `between`.
829- /// FIXME: consider using declarative macro metavariable spans for the given spans if they are
830- /// better suitable for combining (#119412).
831872 fn prepare_to_combine (
832873 a_orig : Span ,
833874 b_orig : Span ,
834875 ) -> Result < ( SpanData , SpanData , Option < LocalDefId > ) , Span > {
835876 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+ }
836880
837- if a. ctxt != b. ctxt {
838- // Context mismatches usually happen when procedural macros combine spans copied from
839- // the macro input with spans produced by the macro (`Span::*_site`).
840- // In that case we consider the combined span to be produced by the macro and return
841- // the original macro-produced span as the result.
842- // Otherwise we just fall back to returning the first span.
843- // Combining locations typically doesn't make sense in case of context mismatches.
844- // `is_root` here is a fast path optimization.
845- let a_is_callsite = a. ctxt . is_root ( ) || a. ctxt == b. span ( ) . source_callsite ( ) . ctxt ( ) ;
846- 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 } ) ) ;
847884 }
848885
849- let parent = if a. parent == b. parent { a. parent } else { None } ;
850- 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 } )
851895 }
852896
853897 /// This span, but in a larger context, may switch to the metavariable span if suitable.
0 commit comments