@@ -825,6 +825,39 @@ impl Span {
825825 )
826826 }
827827
828+ /// 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).
831+ fn prepare_to_combine (
832+ a_orig : Span ,
833+ b_orig : Span ,
834+ ) -> Result < ( SpanData , SpanData , Option < LocalDefId > ) , Span > {
835+ let ( a, b) = ( a_orig. data ( ) , b_orig. data ( ) ) ;
836+
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 } ) ;
847+ }
848+
849+ let parent = if a. parent == b. parent { a. parent } else { None } ;
850+ Ok ( ( a, b, parent) )
851+ }
852+
853+ /// This span, but in a larger context, may switch to the metavariable span if suitable.
854+ pub fn with_neighbor ( self , neighbor : Span ) -> Span {
855+ match Span :: prepare_to_combine ( self , neighbor) {
856+ Ok ( ( this, ..) ) => Span :: new ( this. lo , this. hi , this. ctxt , this. parent ) ,
857+ Err ( _) => self ,
858+ }
859+ }
860+
828861 /// Returns a `Span` that would enclose both `self` and `end`.
829862 ///
830863 /// Note that this can also be used to extend the span "backwards":
@@ -836,26 +869,12 @@ impl Span {
836869 /// ^^^^^^^^^^^^^^^^^^^^
837870 /// ```
838871 pub fn to ( self , end : Span ) -> Span {
839- let span_data = self . data ( ) ;
840- let end_data = end. data ( ) ;
841- // FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480).
842- // Return the macro span on its own to avoid weird diagnostic output. It is preferable to
843- // have an incomplete span than a completely nonsensical one.
844- if span_data. ctxt != end_data. ctxt {
845- if span_data. ctxt . is_root ( ) {
846- return end;
847- } else if end_data. ctxt . is_root ( ) {
848- return self ;
872+ match Span :: prepare_to_combine ( self , end) {
873+ Ok ( ( from, to, parent) ) => {
874+ Span :: new ( cmp:: min ( from. lo , to. lo ) , cmp:: max ( from. hi , to. hi ) , from. ctxt , parent)
849875 }
850- // Both spans fall within a macro.
851- // FIXME(estebank): check if it is the *same* macro.
876+ Err ( fallback) => fallback,
852877 }
853- Span :: new (
854- cmp:: min ( span_data. lo , end_data. lo ) ,
855- cmp:: max ( span_data. hi , end_data. hi ) ,
856- if span_data. ctxt . is_root ( ) { end_data. ctxt } else { span_data. ctxt } ,
857- if span_data. parent == end_data. parent { span_data. parent } else { None } ,
858- )
859878 }
860879
861880 /// Returns a `Span` between the end of `self` to the beginning of `end`.
@@ -866,14 +885,12 @@ impl Span {
866885 /// ^^^^^^^^^^^^^
867886 /// ```
868887 pub fn between ( self , end : Span ) -> Span {
869- let span = self . data ( ) ;
870- let end = end. data ( ) ;
871- Span :: new (
872- span. hi ,
873- end. lo ,
874- if end. ctxt . is_root ( ) { end. ctxt } else { span. ctxt } ,
875- if span. parent == end. parent { span. parent } else { None } ,
876- )
888+ match Span :: prepare_to_combine ( self , end) {
889+ Ok ( ( from, to, parent) ) => {
890+ Span :: new ( cmp:: min ( from. hi , to. hi ) , cmp:: max ( from. lo , to. lo ) , from. ctxt , parent)
891+ }
892+ Err ( fallback) => fallback,
893+ }
877894 }
878895
879896 /// Returns a `Span` from the beginning of `self` until the beginning of `end`.
@@ -884,31 +901,12 @@ impl Span {
884901 /// ^^^^^^^^^^^^^^^^^
885902 /// ```
886903 pub fn until ( self , end : Span ) -> Span {
887- // Most of this function's body is copied from `to`.
888- // We can't just do `self.to(end.shrink_to_lo())`,
889- // because to also does some magic where it uses min/max so
890- // it can handle overlapping spans. Some advanced mis-use of
891- // `until` with different ctxts makes this visible.
892- let span_data = self . data ( ) ;
893- let end_data = end. data ( ) ;
894- // FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480).
895- // Return the macro span on its own to avoid weird diagnostic output. It is preferable to
896- // have an incomplete span than a completely nonsensical one.
897- if span_data. ctxt != end_data. ctxt {
898- if span_data. ctxt . is_root ( ) {
899- return end;
900- } else if end_data. ctxt . is_root ( ) {
901- return self ;
904+ match Span :: prepare_to_combine ( self , end) {
905+ Ok ( ( from, to, parent) ) => {
906+ Span :: new ( cmp:: min ( from. lo , to. lo ) , cmp:: max ( from. lo , to. lo ) , from. ctxt , parent)
902907 }
903- // Both spans fall within a macro.
904- // FIXME(estebank): check if it is the *same* macro.
908+ Err ( fallback) => fallback,
905909 }
906- Span :: new (
907- span_data. lo ,
908- end_data. lo ,
909- if end_data. ctxt . is_root ( ) { end_data. ctxt } else { span_data. ctxt } ,
910- if span_data. parent == end_data. parent { span_data. parent } else { None } ,
911- )
912910 }
913911
914912 pub fn from_inner ( self , inner : InnerSpan ) -> Span {
0 commit comments