@@ -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,87 @@ 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+ return 1 ;
642+ }
643+
644+ let local_begin = self . lookup_byte_offset ( sp. lo ( ) ) ;
645+ let local_end = self . lookup_byte_offset ( sp. hi ( ) ) ;
646+
647+ let start_index = local_begin. pos . to_usize ( ) ;
648+ let end_index = local_end. pos . to_usize ( ) ;
649+
650+ // Disregard indexes that are at the start or end of their spans, they can't fit bigger
651+ // characters.
652+ if ( !forwards && end_index == usize:: min_value ( ) ) ||
653+ ( forwards && start_index == usize:: max_value ( ) ) {
654+ return 1 ;
655+ }
656+
657+ let source_len = ( local_begin. fm . end_pos - local_begin. fm . start_pos ) . to_usize ( ) ;
658+ // Ensure indexes are also not malformed.
659+ if start_index > end_index || end_index > source_len {
660+ return 1 ;
661+ }
662+
663+ // We need to extend the snippet to the end of the src rather than to end_index so when
664+ // searching forwards for boundaries we've got somewhere to search.
665+ let snippet = if let Some ( ref src) = local_begin. fm . src {
666+ let len = src. len ( ) ;
667+ ( & src[ start_index..len] ) . to_string ( )
668+ } else if let Some ( src) = local_begin. fm . external_src . borrow ( ) . get_source ( ) {
669+ let len = src. len ( ) ;
670+ ( & src[ start_index..len] ) . to_string ( )
671+ } else {
672+ return 1 ;
673+ } ;
674+ debug ! ( "DTW start {:?} end {:?}" , start_index, end_index) ;
675+ debug ! ( "DTW snippet {:?}" , snippet) ;
676+
677+ let mut target = if forwards { end_index + 1 } else { end_index - 1 } ;
678+ debug ! ( "DTW initial target {:?}" , target) ;
679+ while !snippet. is_char_boundary ( target - start_index) {
680+ target = if forwards { target + 1 } else { target - 1 } ;
681+ debug ! ( "DTW update target {:?}" , target) ;
682+ }
683+ debug ! ( "DTW final target {:?}" , target) ;
684+
685+ if forwards {
686+ ( target - end_index) as u32
687+ } else {
688+ ( end_index - target) as u32
689+ }
690+ }
691+
610692 pub fn get_filemap ( & self , filename : & FileName ) -> Option < Rc < FileMap > > {
611693 for fm in self . files . borrow ( ) . iter ( ) {
612694 if * filename == fm. name {
0 commit comments