diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 07c864c93a1b5..25777f4133b94 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1623,7 +1623,7 @@ impl EmitterWriter { let line_start = sm.lookup_char_pos(parts[0].span.lo()).line; draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1); let mut lines = complete.lines(); - for (line_pos, (line, parts)) in + for (line_pos, (line, highlight_parts)) in lines.by_ref().zip(highlights).take(MAX_SUGGESTION_HIGHLIGHT_LINES).enumerate() { // Print the span column to avoid confusion @@ -1658,7 +1658,7 @@ impl EmitterWriter { ); buffer.puts(row_num, max_line_num_len + 1, "+ ", Style::Addition); } else if is_multiline { - match &parts[..] { + match &highlight_parts[..] { [SubstitutionHighlight { start: 0, end }] if *end == line.len() => { buffer.puts(row_num, max_line_num_len + 1, "+ ", Style::Addition); } @@ -1676,16 +1676,24 @@ impl EmitterWriter { // print the suggestion buffer.append(row_num, &replace_tabs(line), Style::NoStyle); - if is_multiline { - for SubstitutionHighlight { start, end } in parts { - buffer.set_style_range( - row_num, - max_line_num_len + 3 + start, - max_line_num_len + 3 + end, - Style::Addition, - true, - ); - } + // Colorize addition/replacements with green. + for &SubstitutionHighlight { start, end } in highlight_parts { + // Account for tabs when highlighting (#87972). + let tabs: usize = line + .chars() + .take(start) + .map(|ch| match ch { + '\t' => 3, + _ => 0, + }) + .sum(); + buffer.set_style_range( + row_num, + max_line_num_len + 3 + start + tabs, + max_line_num_len + 3 + end + tabs, + Style::Addition, + true, + ); } row_num += 1; } @@ -1723,13 +1731,6 @@ impl EmitterWriter { assert!(underline_start >= 0 && underline_end >= 0); let padding: usize = max_line_num_len + 3; for p in underline_start..underline_end { - // Colorize addition/replacements with green. - buffer.set_style( - row_num - 1, - (padding as isize + p) as usize, - Style::Addition, - true, - ); if !show_diff { // If this is a replacement, underline with `^`, if this is an addition // underline with `+`. diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index ec29d8016ddf4..a48d4fe8bb55a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -283,6 +283,9 @@ impl CodeSuggestion { let mut buf = String::new(); let mut line_highlight = vec![]; + // We need to keep track of the difference between the existing code and the added + // or deleted code in order to point at the correct column *after* substitution. + let mut acc = 0; for part in &substitution.parts { let cur_lo = sm.lookup_char_pos(part.span.lo()); if prev_hi.line == cur_lo.line { @@ -290,9 +293,11 @@ impl CodeSuggestion { push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo)); while count > 0 { highlights.push(std::mem::take(&mut line_highlight)); + acc = 0; count -= 1; } } else { + acc = 0; highlights.push(std::mem::take(&mut line_highlight)); let mut count = push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); while count > 0 { @@ -316,18 +321,43 @@ impl CodeSuggestion { } } // Add a whole line highlight per line in the snippet. + let len: isize = part + .snippet + .split('\n') + .next() + .unwrap_or(&part.snippet) + .chars() + .map(|c| match c { + '\t' => 4, + _ => 1, + }) + .sum(); line_highlight.push(SubstitutionHighlight { - start: cur_lo.col.0, - end: cur_lo.col.0 - + part.snippet.split('\n').next().unwrap_or(&part.snippet).len(), + start: (cur_lo.col.0 as isize + acc) as usize, + end: (cur_lo.col.0 as isize + acc + len) as usize, }); + buf.push_str(&part.snippet); + let cur_hi = sm.lookup_char_pos(part.span.hi()); + if prev_hi.line == cur_lo.line { + // Account for the difference between the width of the current code and the + // snippet being suggested, so that the *later* suggestions are correctly + // aligned on the screen. + acc += len as isize - (cur_hi.col.0 - cur_lo.col.0) as isize; + } + prev_hi = cur_hi; + prev_line = sf.get_line(prev_hi.line - 1); for line in part.snippet.split('\n').skip(1) { + acc = 0; highlights.push(std::mem::take(&mut line_highlight)); - line_highlight.push(SubstitutionHighlight { start: 0, end: line.len() }); + let end: usize = line + .chars() + .map(|c| match c { + '\t' => 4, + _ => 1, + }) + .sum(); + line_highlight.push(SubstitutionHighlight { start: 0, end }); } - buf.push_str(&part.snippet); - prev_hi = sm.lookup_char_pos(part.span.hi()); - prev_line = sf.get_line(prev_hi.line - 1); } highlights.push(std::mem::take(&mut line_highlight)); let only_capitalization = is_case_difference(sm, &buf, bounding_span);