@@ -311,6 +311,9 @@ pub(crate) struct FormattingError {
311311 is_comment : bool ,
312312 is_string : bool ,
313313 pub ( crate ) line_buffer : String ,
314+ // Number of spaces a tab represents when computing visual columns.
315+ // Needed to translate visual column based overflow ranges into byte indices.
316+ tab_spaces : usize ,
314317}
315318
316319impl FormattingError {
@@ -321,6 +324,8 @@ impl FormattingError {
321324 kind,
322325 is_string : false ,
323326 line_buffer : psess. span_to_first_line_string ( span) ,
327+ // Default; actual value only matters for LineOverflow emitted via push_err
328+ tab_spaces : 4 ,
324329 }
325330 }
326331
@@ -347,7 +352,7 @@ impl FormattingError {
347352 // (space, target)
348353 pub ( crate ) fn format_len ( & self ) -> ( usize , usize ) {
349354 match self . kind {
350- ErrorKind :: LineOverflow ( found, max) => ( max , found - max) ,
355+ ErrorKind :: LineOverflow ( found, max) => self . line_overflow_byte_range ( found, max) ,
351356 ErrorKind :: TrailingWhitespace
352357 | ErrorKind :: DeprecatedAttr
353358 | ErrorKind :: BadAttr
@@ -365,6 +370,40 @@ impl FormattingError {
365370 _ => unreachable ! ( ) ,
366371 }
367372 }
373+
374+ // Compute (start_byte, length) tuple for a LineOverflow error converting visual columns
375+ // (tabs expanded to `tab_spaces`) into byte indices within the stored line buffer.
376+ fn line_overflow_byte_range ( & self , found : usize , max : usize ) -> ( usize , usize ) {
377+ if max >= found || self . line_buffer . is_empty ( ) {
378+ return ( 0 , 0 ) ;
379+ }
380+
381+ let mut visual_col = 0 ;
382+ let mut start_byte = None ;
383+ let mut end_byte = None ;
384+ for ( idx, ch) in self . line_buffer . char_indices ( ) {
385+ let ch_width = if ch == '\t' { self . tab_spaces } else { 1 } ;
386+ let next_col = visual_col + ch_width;
387+ if start_byte. is_none ( ) && next_col > max {
388+ start_byte = Some ( idx) ;
389+ } else if start_byte. is_none ( ) && next_col == max {
390+ // Start will be at next character (if any)
391+ }
392+
393+ if end_byte. is_none ( ) && next_col >= found {
394+ end_byte = Some ( idx + ch. len_utf8 ( ) ) ;
395+ break ;
396+ }
397+
398+ visual_col = next_col;
399+ }
400+
401+ let start = start_byte. unwrap_or_else ( || self . line_buffer . len ( ) . saturating_sub ( 1 ) ) ;
402+ let end = end_byte. unwrap_or ( self . line_buffer . len ( ) ) ;
403+ let len = end. saturating_sub ( start) . max ( 1 ) ;
404+
405+ ( start. min ( self . line_buffer . len ( ) . saturating_sub ( 1 ) ) , len)
406+ }
368407}
369408
370409pub ( crate ) type FormatErrorMap = HashMap < FileName , Vec < FormattingError > > ;
@@ -600,6 +639,7 @@ impl<'a> FormatLines<'a> {
600639 is_comment,
601640 is_string,
602641 line_buffer : self . line_buffer . clone ( ) ,
642+ tab_spaces : self . config . tab_spaces ( ) ,
603643 } ) ;
604644 }
605645
0 commit comments