@@ -364,6 +364,46 @@ impl CodeMap {
364364 }
365365 }
366366
367+ /// Returns `Some(span)`, a union of the lhs and rhs span. The lhs must precede the rhs. If
368+ /// there are gaps between lhs and rhs, the resulting union will cross these gaps.
369+ /// For this to work, the spans have to be:
370+ /// * the expn_id of both spans much match
371+ /// * the lhs span needs to end on the same line the rhs span begins
372+ /// * the lhs span must start at or before the rhs span
373+ pub fn merge_spans ( & self , sp_lhs : Span , sp_rhs : Span ) -> Option < Span > {
374+ use std:: cmp;
375+
376+ // make sure we're at the same expansion id
377+ if sp_lhs. expn_id != sp_rhs. expn_id {
378+ return None ;
379+ }
380+
381+ let lhs_end = match self . lookup_line ( sp_lhs. hi ) {
382+ Ok ( x) => x,
383+ Err ( _) => return None
384+ } ;
385+ let rhs_begin = match self . lookup_line ( sp_rhs. lo ) {
386+ Ok ( x) => x,
387+ Err ( _) => return None
388+ } ;
389+
390+ // if we must cross lines to merge, don't merge
391+ if lhs_end. line != rhs_begin. line {
392+ return None ;
393+ }
394+
395+ // ensure these follow the expected order
396+ if sp_lhs. lo <= sp_rhs. lo {
397+ Some ( Span {
398+ lo : cmp:: min ( sp_lhs. lo , sp_rhs. lo ) ,
399+ hi : cmp:: max ( sp_lhs. hi , sp_rhs. hi ) ,
400+ expn_id : sp_lhs. expn_id ,
401+ } )
402+ } else {
403+ None
404+ }
405+ }
406+
367407 pub fn span_to_string ( & self , sp : Span ) -> String {
368408 if sp == COMMAND_LINE_SP {
369409 return "<command line option>" . to_string ( ) ;
@@ -819,6 +859,9 @@ impl CodeMapper for CodeMap {
819859 fn macro_backtrace ( & self , span : Span ) -> Vec < MacroBacktrace > {
820860 self . macro_backtrace ( span)
821861 }
862+ fn merge_spans ( & self , sp_lhs : Span , sp_rhs : Span ) -> Option < Span > {
863+ self . merge_spans ( sp_lhs, sp_rhs)
864+ }
822865}
823866
824867// _____________________________________________________________________________
@@ -1072,6 +1115,45 @@ mod tests {
10721115 blork.rs:1:1: 1:12\n `first line.`\n ") ;
10731116 }
10741117
1118+ /// Test merging two spans on the same line
1119+ #[ test]
1120+ fn span_merging ( ) {
1121+ let cm = CodeMap :: new ( ) ;
1122+ let inputtext = "bbbb BB bb CCC\n " ;
1123+ let selection1 = " ~~ \n " ;
1124+ let selection2 = " ~~~\n " ;
1125+ cm. new_filemap_and_lines ( "blork.rs" , None , inputtext) ;
1126+ let span1 = span_from_selection ( inputtext, selection1) ;
1127+ let span2 = span_from_selection ( inputtext, selection2) ;
1128+
1129+ if let Some ( sp) = cm. merge_spans ( span1, span2) {
1130+ let sstr = cm. span_to_expanded_string ( sp) ;
1131+ assert_eq ! ( sstr, "blork.rs:1:6: 1:15\n `BB bb CCC`\n " ) ;
1132+ }
1133+ else {
1134+ assert ! ( false ) ;
1135+ }
1136+ }
1137+
1138+ /// Test failing to merge two spans on different lines
1139+ #[ test]
1140+ fn span_merging_fail ( ) {
1141+ let cm = CodeMap :: new ( ) ;
1142+ let inputtext = "bbbb BB\n cc CCC\n " ;
1143+ let selection1 = " ~~\n \n " ;
1144+ let selection2 = " \n ~~~\n " ;
1145+ cm. new_filemap_and_lines ( "blork.rs" , None , inputtext) ;
1146+ let span1 = span_from_selection ( inputtext, selection1) ;
1147+ let span2 = span_from_selection ( inputtext, selection2) ;
1148+
1149+ if let Some ( _) = cm. merge_spans ( span1, span2) {
1150+ assert ! ( false ) ;
1151+ }
1152+ else {
1153+ assert ! ( true ) ;
1154+ }
1155+ }
1156+
10751157 /// Returns the span corresponding to the `n`th occurrence of
10761158 /// `substring` in `source_text`.
10771159 trait CodeMapExtension {
0 commit comments