diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 32104e6f00d44..00882bb287a4f 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -644,6 +644,8 @@ impl EmitterWriter { code_offset: usize, margin: Margin, ) { + // Tabs are assumed to have been replaced by spaces in calling code. + assert!(!source_string.contains('\t')); let line_len = source_string.len(); // Create the source line we will highlight. let left = margin.left(line_len); @@ -707,7 +709,7 @@ impl EmitterWriter { } let source_string = match file.get_line(line.line_index - 1) { - Some(s) => s, + Some(s) => replace_tabs(&*s), None => return Vec::new(), }; @@ -1376,8 +1378,17 @@ impl EmitterWriter { let file = annotated_file.file.clone(); let line = &annotated_file.lines[line_idx]; if let Some(source_string) = file.get_line(line.line_index - 1) { - let leading_whitespace = - source_string.chars().take_while(|c| c.is_whitespace()).count(); + let leading_whitespace = source_string + .chars() + .take_while(|c| c.is_whitespace()) + .map(|c| { + match c { + // Tabs are displayed as 4 spaces + '\t' => 4, + _ => 1, + } + }) + .sum(); if source_string.chars().any(|c| !c.is_whitespace()) { whitespace_margin = min(whitespace_margin, leading_whitespace); } @@ -1502,7 +1513,7 @@ impl EmitterWriter { self.draw_line( &mut buffer, - &unannotated_line, + &replace_tabs(&unannotated_line), annotated_file.lines[line_idx + 1].line_index - 1, last_buffer_line_num, width_offset, @@ -1598,7 +1609,7 @@ impl EmitterWriter { ); // print the suggestion draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); - buffer.append(row_num, line, Style::NoStyle); + buffer.append(row_num, &replace_tabs(line), Style::NoStyle); row_num += 1; } @@ -1930,6 +1941,10 @@ impl FileWithAnnotatedLines { } } +fn replace_tabs(str: &str) -> String { + str.replace('\t', " ") +} + fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { buffer.puts(line, col, "| ", Style::LineNumber); } diff --git a/compiler/rustc_errors/src/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs index f2d255d7d9524..a4dd0f391bd3a 100644 --- a/compiler/rustc_errors/src/styled_buffer.rs +++ b/compiler/rustc_errors/src/styled_buffer.rs @@ -13,34 +13,13 @@ impl StyledBuffer { StyledBuffer { text: vec![], styles: vec![] } } - fn replace_tabs(&mut self) { - for (line_pos, line) in self.text.iter_mut().enumerate() { - let mut tab_pos = vec![]; - for (pos, c) in line.iter().enumerate() { - if *c == '\t' { - tab_pos.push(pos); - } - } - // start with the tabs at the end of the line to replace them with 4 space chars - for pos in tab_pos.iter().rev() { - assert_eq!(line.remove(*pos), '\t'); - // fix the position of the style to match up after replacing the tabs - let s = self.styles[line_pos].remove(*pos); - for _ in 0..4 { - line.insert(*pos, ' '); - self.styles[line_pos].insert(*pos, s); - } - } - } - } + pub fn render(&self) -> Vec> { + // Tabs are assumed to have been replaced by spaces in calling code. + assert!(self.text.iter().all(|r| !r.contains(&'\t'))); - pub fn render(&mut self) -> Vec> { let mut output: Vec> = vec![]; let mut styled_vec: Vec = vec![]; - // before we render, replace tabs with spaces - self.replace_tabs(); - for (row, row_style) in self.text.iter().zip(&self.styles) { let mut current_style = Style::NoStyle; let mut current_text = String::new(); diff --git a/src/test/ui/terminal-width/tabs-trimming.rs b/src/test/ui/terminal-width/tabs-trimming.rs new file mode 100644 index 0000000000000..ade21753b457c --- /dev/null +++ b/src/test/ui/terminal-width/tabs-trimming.rs @@ -0,0 +1,13 @@ +// Test for #78438: ensure underline alignment with many tabs on the left, long line on the right + +// ignore-tidy-linelength +// ignore-tidy-tab + + fn main() { + let money = 42i32; + match money { + v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + //~^ ERROR variable `v` is not bound in all patterns + v => println!("Enough money {}", v), + } + } diff --git a/src/test/ui/terminal-width/tabs-trimming.stderr b/src/test/ui/terminal-width/tabs-trimming.stderr new file mode 100644 index 0000000000000..6c8d9afc73b2a --- /dev/null +++ b/src/test/ui/terminal-width/tabs-trimming.stderr @@ -0,0 +1,12 @@ +error[E0408]: variable `v` is not bound in all patterns + --> $DIR/tabs-trimming.rs:9:16 + | +LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... + | - ^ ^ pattern doesn't bind `v` + | | | + | | pattern doesn't bind `v` + | variable not in all patterns + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0408`.