compiletest: Stricter parsing for diagnostic kinds

This commit is contained in:
Vadim Petrochenkov 2025-04-07 15:44:12 +03:00
parent b86b3fb640
commit 5c160f511e
16 changed files with 74 additions and 72 deletions

View File

@ -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

View File

@ -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 {

View File

@ -2,7 +2,7 @@
use std::future::Future;
fn foo<T: Send, U>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
//~^ Error future cannot be sent between threads safely
//~^ ERROR future cannot be sent between threads safely
async { (ty, ty1) }
}

View File

@ -19,5 +19,5 @@ async fn wrong_mutex() {
}
fn main() {
fake_spawn(wrong_mutex()); //~ Error future cannot be sent between threads safely
fake_spawn(wrong_mutex()); //~ ERROR future cannot be sent between threads safely
}

View File

@ -5,18 +5,18 @@ pub struct Example4<const N: usize = 13, const M: usize = 4>;
fn main() {
let e: Example<13> = ();
//~^ Error: mismatched types
//~^ ERROR mismatched types
//~| expected struct `Example`
let e: Example2<u32, 13> = ();
//~^ Error: mismatched types
//~^ ERROR mismatched types
//~| expected struct `Example2`
let e: Example3<13, u32> = ();
//~^ Error: mismatched types
//~^ ERROR mismatched types
//~| expected struct `Example3`
let e: Example3<7> = ();
//~^ Error: mismatched types
//~^ ERROR mismatched types
//~| expected struct `Example3<7>`
let e: Example4<7> = ();
//~^ Error: mismatched types
//~^ ERROR mismatched types
//~| expected struct `Example4<7>`
}

View File

@ -13,7 +13,7 @@ trait Foo {
[Adt; std::mem::size_of::<Self::Assoc>()]: ,
{
<[Adt; std::mem::size_of::<Self::Assoc>()] as Foo>::bar()
//~^ Error: the trait bound
//~^ ERROR the trait bound
}
fn bar() {}

View File

@ -15,15 +15,15 @@ where
// errors are bad but seems to be pre-existing issue #86198
assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
//~^ Error: mismatched types
//~^^ Error: unconstrained generic constant
//~^ ERROR mismatched types
//~^^ ERROR unconstrained generic constant
assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
//~^ Error: mismatched types
//~^^ Error: unconstrained generic constant
//~^ ERROR mismatched types
//~^^ ERROR unconstrained generic constant
assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
//~^ Error: mismatched types
//~^ ERROR mismatched types
assert_impl::<HasCastInTraitImpl<14, 13>>();
//~^ Error: mismatched types
//~^ ERROR mismatched types
}
pub fn use_trait_impl_2<const N: usize>()
where
@ -33,15 +33,15 @@ where
// errors are bad but seems to be pre-existing issue #86198
assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as u128 }>>();
//~^ Error: mismatched types
//~^^ Error: unconstrained generic constant
//~^ ERROR mismatched types
//~^^ ERROR unconstrained generic constant
assert_impl::<HasCastInTraitImpl<{ N + 1 }, { N as _ }>>();
//~^ Error: mismatched types
//~^^ Error: unconstrained generic constant
//~^ ERROR mismatched types
//~^^ ERROR unconstrained generic constant
assert_impl::<HasCastInTraitImpl<13, { 12 as u128 }>>();
//~^ Error: mismatched types
//~^ ERROR mismatched types
assert_impl::<HasCastInTraitImpl<14, 13>>();
//~^ Error: mismatched types
//~^ ERROR mismatched types
}
fn main() {}

View File

@ -9,8 +9,8 @@ pub trait True {}
impl<const LHS: u32, const RHS: u32> True for IsLessOrEqual<LHS, RHS> where
Condition<{ LHS <= RHS }>: True
//[min]~^ Error generic parameters may not be used in const operations
//[min]~| Error generic parameters may not be used in const operations
//[min]~^ ERROR generic parameters may not be used in const operations
//[min]~| ERROR generic parameters may not be used in const operations
{
}
impl True for Condition<true> {}
@ -21,8 +21,8 @@ where
IsLessOrEqual<I, 8>: True,
IsLessOrEqual<J, 8>: True,
IsLessOrEqual<{ 8 - I }, { 8 - J }>: True,
//[min]~^ Error generic parameters may not be used in const operations
//[min]~| Error generic parameters may not be used in const operations
//[min]~^ ERROR generic parameters may not be used in const operations
//[min]~| ERROR generic parameters may not be used in const operations
// Condition<{ 8 - I <= 8 - J }>: True,
{
fn print() {

View File

@ -14,7 +14,7 @@ trait Foo {
[(); std::mem::size_of::<Self::Assoc>()]: ,
{
Self::AssocInstance == [(); std::mem::size_of::<Self::Assoc>()];
//~^ Error: mismatched types
//~^ ERROR mismatched types
}
}

View File

@ -6,7 +6,7 @@ trait Foo {
impl Foo for () {
fn foo<U: Debug>(&self, _: &U) { }
//~^ Error method `foo` has incompatible signature for trait
//~^ ERROR method `foo` has incompatible signature for trait
}
trait Bar {
@ -15,7 +15,7 @@ trait Bar {
impl Bar for () {
fn bar(&self, _: &impl Debug) { }
//~^ Error method `bar` has incompatible signature for trait
//~^ ERROR method `bar` has incompatible signature for trait
}
trait Baz {
@ -24,7 +24,7 @@ trait Baz {
impl Baz for () {
fn baz<T: Debug>(&self, _: &impl Debug, _: &T) { }
//~^ Error method `baz` has incompatible signature for trait
//~^ ERROR method `baz` has incompatible signature for trait
}
// With non-local trait (#49841):
@ -35,7 +35,7 @@ struct X;
impl Hash for X {
fn hash(&self, hasher: &mut impl Hasher) {}
//~^ Error method `hash` has incompatible signature for trait
//~^ ERROR method `hash` has incompatible signature for trait
}
fn main() {}

View File

@ -3,5 +3,5 @@ struct Foo;
fn main() {
let mut a = Foo;
let ref b = Foo;
a += *b; //~ Error: binary assignment operation `+=` cannot be applied to type `Foo`
a += *b; //~ ERROR binary assignment operation `+=` cannot be applied to type `Foo`
}

View File

@ -102,5 +102,5 @@ fn main() {
();
();
();
dbg!(lib::Dummy); //~ Error: `Dummy` doesn't implement `Debug`
dbg!(lib::Dummy); //~ ERROR `Dummy` doesn't implement `Debug`
}

View File

@ -10,7 +10,7 @@ pub fn a() {
#[export_name="fail"]
pub fn b() {
//~^ Error symbol `fail` is already defined
//~^ ERROR symbol `fail` is already defined
}
fn main() {}

View File

@ -4,15 +4,15 @@ struct A {
impl A {
fn new(cofig: String) -> Self {
Self { config } //~ Error cannot find value `config` in this scope
Self { config } //~ ERROR cannot find value `config` in this scope
}
fn do_something(cofig: String) {
println!("{config}"); //~ Error cannot find value `config` in this scope
println!("{config}"); //~ ERROR cannot find value `config` in this scope
}
fn self_is_available(self, cofig: String) {
println!("{config}"); //~ Error cannot find value `config` in this scope
println!("{config}"); //~ ERROR cannot find value `config` in this scope
}
}

View File

@ -5,7 +5,7 @@ trait Cat {
fn uwu<T: Cat>(c: T) {
c.nya();
//~^ ERROR no method named `nya` found for type parameter `T` in the current scope
//~| Suggestion T::nya()
//~| SUGGESTION T::nya()
}
fn main() {}

View File

@ -17,7 +17,7 @@ impl Foo for S2 {
type Item = impl Debug;
fn foo<T: Debug>(_: T) -> Self::Item {
//~^ Error type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
//~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
S::<T>(Default::default())
}
}