about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJ. Ryan Stinnett <jryans@gmail.com>2020-12-06 00:39:07 +0000
committerJ. Ryan Stinnett <jryans@gmail.com>2020-12-09 10:12:15 +0000
commit3537bd80ff1ef12c9fa994133969d8cfcaa356d3 (patch)
tree1797cfc244e96590fe3f061b961a63a3b64d74cd
parent8ca8b7724f753e71f26cb442e11bb1f1c40ebf0a (diff)
Replace tabs earlier in diagnostics
This replaces tabs earlier in the diagnostics emitting process, which allows
various margin calculations to ignore the existence of tabs. It does add a
string copy for the source lines that are emitted.
-rw-r--r--compiler/rustc_errors/src/emitter.rs25
-rw-r--r--compiler/rustc_errors/src/styled_buffer.rs27
2 files changed, 23 insertions, 29 deletions
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 32104e6f00d..00882bb287a 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 f2d255d7d95..a4dd0f391bd 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<Vec<StyledString>> {
+        // 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<Vec<StyledString>> {
         let mut output: Vec<Vec<StyledString>> = vec![];
         let mut styled_vec: Vec<StyledString> = 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();