@@ -25,6 +25,7 @@ pub use self::ExpnFormat::*;
25
25
use rustc_data_structures:: fx:: FxHashMap ;
26
26
use rustc_data_structures:: stable_hasher:: StableHasher ;
27
27
use std:: cell:: { RefCell , Ref } ;
28
+ use std:: cmp;
28
29
use std:: hash:: Hash ;
29
30
use std:: path:: { Path , PathBuf } ;
30
31
use std:: rc:: Rc ;
@@ -607,6 +608,101 @@ impl CodeMap {
607
608
self . span_until_char ( sp, '{' )
608
609
}
609
610
611
+ /// Returns a new span representing just the end-point of this span
612
+ pub fn end_point ( & self , sp : Span ) -> Span {
613
+ let pos = sp. hi ( ) . 0 ;
614
+
615
+ let width = self . find_width_of_character_at_span ( sp, false ) ;
616
+ let corrected_end_position = pos. checked_sub ( width) . unwrap_or ( pos) ;
617
+
618
+ let end_point = BytePos ( cmp:: max ( corrected_end_position, sp. lo ( ) . 0 ) ) ;
619
+ sp. with_lo ( end_point)
620
+ }
621
+
622
+ /// Returns a new span representing the next character after the end-point of this span
623
+ pub fn next_point ( & self , sp : Span ) -> Span {
624
+ let start_of_next_point = sp. hi ( ) . 0 ;
625
+
626
+ let width = self . find_width_of_character_at_span ( sp, true ) ;
627
+ // If the width is 1, then the next span should point to the same `lo` and `hi`. However,
628
+ // in the case of a multibyte character, where the width != 1, the next span should
629
+ // span multiple bytes to include the whole character.
630
+ let end_of_next_point = start_of_next_point. checked_add (
631
+ width - 1 ) . unwrap_or ( start_of_next_point) ;
632
+
633
+ let end_of_next_point = BytePos ( cmp:: max ( sp. lo ( ) . 0 + 1 , end_of_next_point) ) ;
634
+ Span :: new ( BytePos ( start_of_next_point) , end_of_next_point, sp. ctxt ( ) )
635
+ }
636
+
637
+ /// Finds the width of a character, either before or after the provided span.
638
+ fn find_width_of_character_at_span ( & self , sp : Span , forwards : bool ) -> u32 {
639
+ // Disregard malformed spans and assume a one-byte wide character.
640
+ if sp. lo ( ) >= sp. hi ( ) {
641
+ debug ! ( "find_width_of_character_at_span: early return malformed span" ) ;
642
+ return 1 ;
643
+ }
644
+
645
+ let local_begin = self . lookup_byte_offset ( sp. lo ( ) ) ;
646
+ let local_end = self . lookup_byte_offset ( sp. hi ( ) ) ;
647
+ debug ! ( "find_width_of_character_at_span: local_begin=`{:?}`, local_end=`{:?}`" ,
648
+ local_begin, local_end) ;
649
+
650
+ let start_index = local_begin. pos . to_usize ( ) ;
651
+ let end_index = local_end. pos . to_usize ( ) ;
652
+ debug ! ( "find_width_of_character_at_span: start_index=`{:?}`, end_index=`{:?}`" ,
653
+ start_index, end_index) ;
654
+
655
+ // Disregard indexes that are at the start or end of their spans, they can't fit bigger
656
+ // characters.
657
+ if ( !forwards && end_index == usize:: min_value ( ) ) ||
658
+ ( forwards && start_index == usize:: max_value ( ) ) {
659
+ debug ! ( "find_width_of_character_at_span: start or end of span, cannot be multibyte" ) ;
660
+ return 1 ;
661
+ }
662
+
663
+ let source_len = ( local_begin. fm . end_pos - local_begin. fm . start_pos ) . to_usize ( ) ;
664
+ debug ! ( "find_width_of_character_at_span: source_len=`{:?}`" , source_len) ;
665
+ // Ensure indexes are also not malformed.
666
+ if start_index > end_index || end_index > source_len {
667
+ debug ! ( "find_width_of_character_at_span: source indexes are malformed" ) ;
668
+ return 1 ;
669
+ }
670
+
671
+ // We need to extend the snippet to the end of the src rather than to end_index so when
672
+ // searching forwards for boundaries we've got somewhere to search.
673
+ let snippet = if let Some ( ref src) = local_begin. fm . src {
674
+ let len = src. len ( ) ;
675
+ ( & src[ start_index..len] ) . to_string ( )
676
+ } else if let Some ( src) = local_begin. fm . external_src . borrow ( ) . get_source ( ) {
677
+ let len = src. len ( ) ;
678
+ ( & src[ start_index..len] ) . to_string ( )
679
+ } else {
680
+ return 1 ;
681
+ } ;
682
+ debug ! ( "find_width_of_character_at_span: snippet=`{:?}`" , snippet) ;
683
+
684
+ let file_start_pos = local_begin. fm . start_pos . to_usize ( ) ;
685
+ let file_end_pos = local_begin. fm . end_pos . to_usize ( ) ;
686
+ debug ! ( "find_width_of_character_at_span: file_start_pos=`{:?}` file_end_pos=`{:?}`" ,
687
+ file_start_pos, file_end_pos) ;
688
+
689
+ let mut target = if forwards { end_index + 1 } else { end_index - 1 } ;
690
+ debug ! ( "find_width_of_character_at_span: initial target=`{:?}`" , target) ;
691
+
692
+ while !snippet. is_char_boundary ( target - start_index)
693
+ && target >= file_start_pos && target <= file_end_pos {
694
+ target = if forwards { target + 1 } else { target - 1 } ;
695
+ debug ! ( "find_width_of_character_at_span: target=`{:?}`" , target) ;
696
+ }
697
+ debug ! ( "find_width_of_character_at_span: final target=`{:?}`" , target) ;
698
+
699
+ if forwards {
700
+ ( target - end_index) as u32
701
+ } else {
702
+ ( end_index - target) as u32
703
+ }
704
+ }
705
+
610
706
pub fn get_filemap ( & self , filename : & FileName ) -> Option < Rc < FileMap > > {
611
707
for fm in self . files . borrow ( ) . iter ( ) {
612
708
if * filename == fm. name {
0 commit comments