@@ -25,6 +25,7 @@ pub use self::ExpnFormat::*;
2525use rustc_data_structures:: fx:: FxHashMap ;
2626use rustc_data_structures:: stable_hasher:: StableHasher ;
2727use std:: cell:: { RefCell , Ref } ;
28+ use std:: cmp;
2829use std:: hash:: Hash ;
2930use std:: path:: { Path , PathBuf } ;
3031use std:: rc:: Rc ;
@@ -607,6 +608,101 @@ impl CodeMap {
607608 self . span_until_char ( sp, '{' )
608609 }
609610
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+
610706 pub fn get_filemap ( & self , filename : & FileName ) -> Option < Rc < FileMap > > {
611707 for fm in self . files . borrow ( ) . iter ( ) {
612708 if * filename == fm. name {
0 commit comments