@@ -72,6 +72,7 @@ pub mod fatal_error;
72
72
73
73
pub mod profiling;
74
74
75
+ use rustc_data_structures:: fx:: FxHashMap ;
75
76
use rustc_data_structures:: stable_hasher:: { Hash128 , Hash64 , HashStable , StableHasher } ;
76
77
use rustc_data_structures:: sync:: { FreezeLock , FreezeWriteGuard , Lock , Lrc } ;
77
78
@@ -98,6 +99,9 @@ mod tests;
98
99
pub struct SessionGlobals {
99
100
symbol_interner : symbol:: Interner ,
100
101
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 > > ,
101
105
hygiene_data : Lock < hygiene:: HygieneData > ,
102
106
103
107
/// A reference to the source map in the `Session`. It's an `Option`
@@ -115,6 +119,7 @@ impl SessionGlobals {
115
119
SessionGlobals {
116
120
symbol_interner : symbol:: Interner :: fresh ( ) ,
117
121
span_interner : Lock :: new ( span_encoding:: SpanInterner :: default ( ) ) ,
122
+ metavar_spans : Default :: default ( ) ,
118
123
hygiene_data : Lock :: new ( hygiene:: HygieneData :: new ( edition) ) ,
119
124
source_map : Lock :: new ( None ) ,
120
125
}
@@ -168,6 +173,11 @@ pub fn create_default_session_globals_then<R>(f: impl FnOnce() -> R) -> R {
168
173
// deserialization.
169
174
scoped_tls:: scoped_thread_local!( static SESSION_GLOBALS : SessionGlobals ) ;
170
175
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
+
171
181
// FIXME: We should use this enum or something like it to get rid of the
172
182
// use of magic `/rust/1.x/...` paths across the board.
173
183
#[ derive( Debug , Eq , PartialEq , Clone , Ord , PartialOrd , Decodable ) ]
@@ -824,29 +834,64 @@ impl Span {
824
834
)
825
835
}
826
836
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
+
827
871
/// 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).
830
872
fn prepare_to_combine (
831
873
a_orig : Span ,
832
874
b_orig : Span ,
833
875
) -> Result < ( SpanData , SpanData , Option < LocalDefId > ) , Span > {
834
876
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
+ }
835
880
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 } ) ) ;
846
884
}
847
885
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 } )
850
895
}
851
896
852
897
/// This span, but in a larger context, may switch to the metavariable span if suitable.
0 commit comments