about summary refs log tree commit diff
path: root/compiler/rustc_errors/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-07 09:26:42 +0000
committerbors <bors@rust-lang.org>2024-08-07 09:26:42 +0000
commit384f8586cb7d3770b2572e9a998a33706f0983ac (patch)
treeef23cabaebea1904b592c6c16b23d00ebe32d964 /compiler/rustc_errors/src
parentc755314d780b0ac0e712efc258533bbd8431d1f0 (diff)
parent630ad887720e8c1357e0196021e2246de3b96fdd (diff)
downloadrust-384f8586cb7d3770b2572e9a998a33706f0983ac.tar.gz
rust-384f8586cb7d3770b2572e9a998a33706f0983ac.zip
Auto merge of #3793 - rust-lang:rustup-2024-08-07, r=RalfJung
Automatic Rustup
Diffstat (limited to 'compiler/rustc_errors/src')
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs21
-rw-r--r--compiler/rustc_errors/src/emitter.rs105
2 files changed, 93 insertions, 33 deletions
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index e1dcbf5b280..67ca6d50cca 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -741,6 +741,16 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
         self
     }
 
+    #[rustc_lint_diagnostics]
+    pub fn highlighted_span_note(
+        &mut self,
+        span: impl Into<MultiSpan>,
+        msg: Vec<StringPart>,
+    ) -> &mut Self {
+        self.sub_with_highlights(Level::Note, msg, span.into());
+        self
+    }
+
     /// This is like [`Diag::note()`], but it's only printed once.
     #[rustc_lint_diagnostics]
     pub fn note_once(&mut self, msg: impl Into<SubdiagMessage>) -> &mut Self {
@@ -815,6 +825,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
         self
     }
 
+    /// Add a help message attached to this diagnostic with a customizable highlighted message.
+    #[rustc_lint_diagnostics]
+    pub fn highlighted_span_help(
+        &mut self,
+        span: impl Into<MultiSpan>,
+        msg: Vec<StringPart>,
+    ) -> &mut Self {
+        self.sub_with_highlights(Level::Help, msg, span.into());
+        self
+    }
+
     /// Prints the span with some help above it.
     /// This is like [`Diag::help()`], but it gets its own span.
     #[rustc_lint_diagnostics]
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 8963b009c31..88ed3128164 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -1346,10 +1346,11 @@ impl HumanEmitter {
                 buffer.append(0, ": ", header_style);
                 label_width += 2;
             }
-            for (text, _) in msgs.iter() {
+            let mut line = 0;
+            for (text, style) in msgs.iter() {
                 let text = self.translate_message(text, args).map_err(Report::new).unwrap();
                 // Account for newlines to align output to its label.
-                for (line, text) in normalize_whitespace(&text).lines().enumerate() {
+                for text in normalize_whitespace(&text).lines() {
                     buffer.append(
                         line,
                         &format!(
@@ -1357,8 +1358,38 @@ impl HumanEmitter {
                             if line == 0 { String::new() } else { " ".repeat(label_width) },
                             text
                         ),
-                        header_style,
+                        match style {
+                            Style::Highlight => *style,
+                            _ => header_style,
+                        },
                     );
+                    line += 1;
+                }
+                // We add lines above, but if the last line has no explicit newline (which would
+                // yield an empty line), then we revert one line up to continue with the next
+                // styled text chunk on the same line as the last one from the prior one. Otherwise
+                // every `text` would appear on their own line (because even though they didn't end
+                // in '\n', they advanced `line` by one).
+                if line > 0 {
+                    line -= 1;
+                }
+            }
+            if self.short_message {
+                let labels = msp
+                    .span_labels()
+                    .into_iter()
+                    .filter_map(|label| match label.label {
+                        Some(msg) if label.is_primary => {
+                            let text = self.translate_message(&msg, args).ok()?;
+                            if !text.trim().is_empty() { Some(text.to_string()) } else { None }
+                        }
+                        _ => None,
+                    })
+                    .collect::<Vec<_>>()
+                    .join(", ");
+                if !labels.is_empty() {
+                    buffer.append(line, ": ", Style::NoStyle);
+                    buffer.append(line, &labels, Style::NoStyle);
                 }
             }
         }
@@ -2564,22 +2595,13 @@ fn num_decimal_digits(num: usize) -> usize {
 
 // We replace some characters so the CLI output is always consistent and underlines aligned.
 // Keep the following list in sync with `rustc_span::char_width`.
+// ATTENTION: keep lexicografically sorted so that the binary search will work
 const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
-    ('\t', "    "),    // We do our own tab replacement
-    ('\u{200D}', ""), // Replace ZWJ with nothing for consistent terminal output of grapheme clusters.
-    ('\u{202A}', "�"), // The following unicode text flow control characters are inconsistently
-    ('\u{202B}', "�"), // supported across CLIs and can cause confusion due to the bytes on disk
-    ('\u{202D}', "�"), // not corresponding to the visible source code, so we replace them always.
-    ('\u{202E}', "�"),
-    ('\u{2066}', "�"),
-    ('\u{2067}', "�"),
-    ('\u{2068}', "�"),
-    ('\u{202C}', "�"),
-    ('\u{2069}', "�"),
+    // tidy-alphabetical-start
     // In terminals without Unicode support the following will be garbled, but in *all* terminals
     // the underlying codepoint will be as well. We could gate this replacement behind a "unicode
     // support" gate.
-    ('\u{0000}', "␀"),
+    ('\0', "␀"),
     ('\u{0001}', "␁"),
     ('\u{0002}', "␂"),
     ('\u{0003}', "␃"),
@@ -2588,11 +2610,12 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
     ('\u{0006}', "␆"),
     ('\u{0007}', "␇"),
     ('\u{0008}', "␈"),
-    ('\u{000B}', "␋"),
-    ('\u{000C}', "␌"),
-    ('\u{000D}', "␍"),
-    ('\u{000E}', "␎"),
-    ('\u{000F}', "␏"),
+    ('\u{0009}', "    "), // We do our own tab replacement
+    ('\u{000b}', "␋"),
+    ('\u{000c}', "␌"),
+    ('\u{000d}', "␍"),
+    ('\u{000e}', "␎"),
+    ('\u{000f}', "␏"),
     ('\u{0010}', "␐"),
     ('\u{0011}', "␑"),
     ('\u{0012}', "␒"),
@@ -2603,21 +2626,37 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
     ('\u{0017}', "␗"),
     ('\u{0018}', "␘"),
     ('\u{0019}', "␙"),
-    ('\u{001A}', "␚"),
-    ('\u{001B}', "␛"),
-    ('\u{001C}', "␜"),
-    ('\u{001D}', "␝"),
-    ('\u{001E}', "␞"),
-    ('\u{001F}', "␟"),
-    ('\u{007F}', "␡"),
+    ('\u{001a}', "␚"),
+    ('\u{001b}', "␛"),
+    ('\u{001c}', "␜"),
+    ('\u{001d}', "␝"),
+    ('\u{001e}', "␞"),
+    ('\u{001f}', "␟"),
+    ('\u{007f}', "␡"),
+    ('\u{200d}', ""), // Replace ZWJ for consistent terminal output of grapheme clusters.
+    ('\u{202a}', "�"), // The following unicode text flow control characters are inconsistently
+    ('\u{202b}', "�"), // supported across CLIs and can cause confusion due to the bytes on disk
+    ('\u{202c}', "�"), // not corresponding to the visible source code, so we replace them always.
+    ('\u{202d}', "�"),
+    ('\u{202e}', "�"),
+    ('\u{2066}', "�"),
+    ('\u{2067}', "�"),
+    ('\u{2068}', "�"),
+    ('\u{2069}', "�"),
+    // tidy-alphabetical-end
 ];
 
-fn normalize_whitespace(str: &str) -> String {
-    let mut s = str.to_string();
-    for (c, replacement) in OUTPUT_REPLACEMENTS {
-        s = s.replace(*c, replacement);
-    }
-    s
+fn normalize_whitespace(s: &str) -> String {
+    // Scan the input string for a character in the ordered table above. If it's present, replace
+    // it with it's alternative string (it can be more than 1 char!). Otherwise, retain the input
+    // char. At the end, allocate all chars into a string in one operation.
+    s.chars().fold(String::with_capacity(s.len()), |mut s, c| {
+        match OUTPUT_REPLACEMENTS.binary_search_by_key(&c, |(k, _)| *k) {
+            Ok(i) => s.push_str(OUTPUT_REPLACEMENTS[i].1),
+            _ => s.push(c),
+        }
+        s
+    })
 }
 
 fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {