about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2025-04-07 15:44:12 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2025-04-07 19:13:31 +0300
commit5c160f511e321a89eef01fcf17c6cc4c0f4e5c00 (patch)
tree016abaa173e463d7a708e6def26c9b41a18d0a32 /src
parentb86b3fb640e4f914db9013872e8ff67b74ba286d (diff)
downloadrust-5c160f511e321a89eef01fcf17c6cc4c0f4e5c00.tar.gz
rust-5c160f511e321a89eef01fcf17c6cc4c0f4e5c00.zip
compiletest: Stricter parsing for diagnostic kinds
Diffstat (limited to 'src')
-rw-r--r--src/tools/compiletest/src/errors.rs66
-rw-r--r--src/tools/compiletest/src/json.rs6
2 files changed, 37 insertions, 35 deletions
diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs
index e1face32716..9b59e4968a3 100644
--- a/src/tools/compiletest/src/errors.rs
+++ b/src/tools/compiletest/src/errors.rs
@@ -3,7 +3,6 @@ use std::fs::File;
 use std::io::BufReader;
 use std::io::prelude::*;
 use std::path::Path;
-use std::str::FromStr;
 use std::sync::OnceLock;
 
 use regex::Regex;
@@ -18,30 +17,39 @@ pub enum ErrorKind {
     Warning,
 }
 
-impl FromStr for ErrorKind {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let s = s.to_uppercase();
-        let part0: &str = s.split(':').next().unwrap();
-        match part0 {
-            "HELP" => Ok(ErrorKind::Help),
-            "ERROR" => Ok(ErrorKind::Error),
-            "NOTE" => Ok(ErrorKind::Note),
-            "SUGGESTION" => Ok(ErrorKind::Suggestion),
-            "WARN" | "WARNING" => Ok(ErrorKind::Warning),
-            _ => Err(()),
+impl ErrorKind {
+    pub fn from_compiler_str(s: &str) -> ErrorKind {
+        match s {
+            "help" => ErrorKind::Help,
+            "error" | "error: internal compiler error" => ErrorKind::Error,
+            "note" | "failure-note" => ErrorKind::Note,
+            "warning" => ErrorKind::Warning,
+            _ => panic!("unexpected compiler diagnostic kind `{s}`"),
         }
     }
+
+    /// Either the canonical uppercase string, or some additional versions for compatibility.
+    /// FIXME: consider keeping only the canonical versions here.
+    fn from_user_str(s: &str) -> Option<ErrorKind> {
+        Some(match s {
+            "HELP" | "help" => ErrorKind::Help,
+            "ERROR" | "error" => ErrorKind::Error,
+            "NOTE" | "note" => ErrorKind::Note,
+            "SUGGESTION" => ErrorKind::Suggestion,
+            "WARN" | "WARNING" | "warn" | "warning" => ErrorKind::Warning,
+            _ => return None,
+        })
+    }
 }
 
 impl fmt::Display for ErrorKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
-            ErrorKind::Help => write!(f, "help message"),
-            ErrorKind::Error => write!(f, "error"),
-            ErrorKind::Note => write!(f, "note"),
-            ErrorKind::Suggestion => write!(f, "suggestion"),
-            ErrorKind::Warning => write!(f, "warning"),
+            ErrorKind::Help => write!(f, "HELP"),
+            ErrorKind::Error => write!(f, "ERROR"),
+            ErrorKind::Note => write!(f, "NOTE"),
+            ErrorKind::Suggestion => write!(f, "SUGGESTION"),
+            ErrorKind::Warning => write!(f, "WARN"),
         }
     }
 }
@@ -64,7 +72,7 @@ impl Error {
         use colored::Colorize;
         format!(
             "{: <10}line {: >3}: {}",
-            self.kind.map(|kind| kind.to_string()).unwrap_or_default().to_uppercase(),
+            self.kind.map(|kind| kind.to_string()).unwrap_or_default(),
             self.line_num_str(),
             self.msg.cyan(),
         )
@@ -154,18 +162,12 @@ fn parse_expected(
     }
 
     // Get the part of the comment after the sigil (e.g. `~^^` or ~|).
-    let whole_match = captures.get(0).unwrap();
-    let (_, mut msg) = line.split_at(whole_match.end());
-
-    let first_word = msg.split_whitespace().next().expect("Encountered unexpected empty comment");
-
-    // If we find `//~ ERROR foo` or something like that, skip the first word.
-    let kind = first_word.parse::<ErrorKind>().ok();
-    if kind.is_some() {
-        msg = &msg.trim_start().split_at(first_word.len()).1;
-    }
-
-    let msg = msg.trim().to_owned();
+    let tag = captures.get(0).unwrap();
+    let rest = line[tag.end()..].trim_start();
+    let (kind_str, _) = rest.split_once(|c: char| !c.is_ascii_alphabetic()).unwrap_or((rest, ""));
+    let kind = ErrorKind::from_user_str(kind_str);
+    let untrimmed_msg = if kind.is_some() { &rest[kind_str.len()..] } else { rest };
+    let msg = untrimmed_msg.strip_prefix(':').unwrap_or(untrimmed_msg).trim().to_owned();
 
     let line_num_adjust = &captures["adjust"];
     let (follow_prev, line_num) = if line_num_adjust == "|" {
@@ -181,7 +183,7 @@ fn parse_expected(
     debug!(
         "line={:?} tag={:?} follow_prev={:?} kind={:?} msg={:?}",
         line_num,
-        whole_match.as_str(),
+        tag.as_str(),
         follow_prev,
         kind,
         msg
diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs
index 2a6fcd6cf81..62fe538ee32 100644
--- a/src/tools/compiletest/src/json.rs
+++ b/src/tools/compiletest/src/json.rs
@@ -1,7 +1,6 @@
 //! These structs are a subset of the ones found in `rustc_errors::json`.
 
 use std::path::{Path, PathBuf};
-use std::str::FromStr;
 use std::sync::OnceLock;
 
 use regex::Regex;
@@ -230,7 +229,7 @@ fn push_actual_errors(
     // Convert multi-line messages into multiple errors.
     // We expect to replace these with something more structured anyhow.
     let mut message_lines = diagnostic.message.lines();
-    let kind = ErrorKind::from_str(&diagnostic.level).ok();
+    let kind = Some(ErrorKind::from_compiler_str(&diagnostic.level));
     let first_line = message_lines.next().unwrap_or(&diagnostic.message);
     if primary_spans.is_empty() {
         static RE: OnceLock<Regex> = OnceLock::new();
@@ -240,7 +239,8 @@ fn push_actual_errors(
             line_num: None,
             kind,
             msg: with_code(None, first_line),
-            require_annotation: !RE.get_or_init(re_init).is_match(first_line),
+            require_annotation: diagnostic.level != "failure-note"
+                && !RE.get_or_init(re_init).is_match(first_line),
         });
     } else {
         for span in primary_spans {