@@ -364,6 +364,46 @@ impl CodeMap {
364
364
}
365
365
}
366
366
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
+
367
407
pub fn span_to_string ( & self , sp : Span ) -> String {
368
408
if sp == COMMAND_LINE_SP {
369
409
return "<command line option>" . to_string ( ) ;
@@ -819,6 +859,9 @@ impl CodeMapper for CodeMap {
819
859
fn macro_backtrace ( & self , span : Span ) -> Vec < MacroBacktrace > {
820
860
self . macro_backtrace ( span)
821
861
}
862
+ fn merge_spans ( & self , sp_lhs : Span , sp_rhs : Span ) -> Option < Span > {
863
+ self . merge_spans ( sp_lhs, sp_rhs)
864
+ }
822
865
}
823
866
824
867
// _____________________________________________________________________________
@@ -1072,6 +1115,45 @@ mod tests {
1072
1115
blork.rs:1:1: 1:12\n `first line.`\n ") ;
1073
1116
}
1074
1117
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
+
1075
1157
/// Returns the span corresponding to the `n`th occurrence of
1076
1158
/// `substring` in `source_text`.
1077
1159
trait CodeMapExtension {
0 commit comments