Skip to content

Commit a09e9d4

Browse files
committed
compiletest: Improve diagnostics for line annotation mismatches
1 parent 61413ae commit a09e9d4

File tree

5 files changed

+180
-40
lines changed

5 files changed

+180
-40
lines changed

src/tools/compiletest/src/errors.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ pub enum ErrorKind {
1616
Suggestion,
1717
Warning,
1818
Raw,
19+
/// Used for better recovery and diagnostics in compiletest.
20+
Unknown,
1921
}
2022

2123
impl ErrorKind {
@@ -31,21 +33,25 @@ impl ErrorKind {
3133

3234
/// Either the canonical uppercase string, or some additional versions for compatibility.
3335
/// FIXME: consider keeping only the canonical versions here.
34-
pub fn from_user_str(s: &str) -> ErrorKind {
35-
match s {
36+
fn from_user_str(s: &str) -> Option<ErrorKind> {
37+
Some(match s {
3638
"HELP" | "help" => ErrorKind::Help,
3739
"ERROR" | "error" => ErrorKind::Error,
38-
// `MONO_ITEM` makes annotations in `codegen-units` tests syntactically correct,
39-
// but those tests never use the error kind later on.
40-
"NOTE" | "note" | "MONO_ITEM" => ErrorKind::Note,
40+
"NOTE" | "note" => ErrorKind::Note,
4141
"SUGGESTION" => ErrorKind::Suggestion,
4242
"WARN" | "WARNING" | "warn" | "warning" => ErrorKind::Warning,
4343
"RAW" => ErrorKind::Raw,
44-
_ => panic!(
44+
_ => return None,
45+
})
46+
}
47+
48+
pub fn expect_from_user_str(s: &str) -> ErrorKind {
49+
ErrorKind::from_user_str(s).unwrap_or_else(|| {
50+
panic!(
4551
"unexpected diagnostic kind `{s}`, expected \
46-
`ERROR`, `WARN`, `NOTE`, `HELP` or `SUGGESTION`"
47-
),
48-
}
52+
`ERROR`, `WARN`, `NOTE`, `HELP`, `SUGGESTION` or `RAW`"
53+
)
54+
})
4955
}
5056
}
5157

@@ -58,6 +64,7 @@ impl fmt::Display for ErrorKind {
5864
ErrorKind::Suggestion => write!(f, "SUGGESTION"),
5965
ErrorKind::Warning => write!(f, "WARN"),
6066
ErrorKind::Raw => write!(f, "RAW"),
67+
ErrorKind::Unknown => write!(f, "UNKNOWN"),
6168
}
6269
}
6370
}
@@ -75,11 +82,6 @@ pub struct Error {
7582
}
7683

7784
impl Error {
78-
pub fn render_for_expected(&self) -> String {
79-
use colored::Colorize;
80-
format!("{: <10}line {: >3}: {}", self.kind, self.line_num_str(), self.msg.cyan())
81-
}
82-
8385
pub fn line_num_str(&self) -> String {
8486
self.line_num.map_or("?".to_string(), |line_num| line_num.to_string())
8587
}
@@ -168,8 +170,10 @@ fn parse_expected(
168170
let rest = line[tag.end()..].trim_start();
169171
let (kind_str, _) =
170172
rest.split_once(|c: char| c != '_' && !c.is_ascii_alphabetic()).unwrap_or((rest, ""));
171-
let kind = ErrorKind::from_user_str(kind_str);
172-
let untrimmed_msg = &rest[kind_str.len()..];
173+
let (kind, untrimmed_msg) = match ErrorKind::from_user_str(kind_str) {
174+
Some(kind) => (kind, &rest[kind_str.len()..]),
175+
None => (ErrorKind::Unknown, rest),
176+
};
173177
let msg = untrimmed_msg.strip_prefix(':').unwrap_or(untrimmed_msg).trim().to_owned();
174178

175179
let line_num_adjust = &captures["adjust"];

src/tools/compiletest/src/header.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ impl TestProps {
593593
config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS)
594594
{
595595
self.dont_require_annotations
596-
.insert(ErrorKind::from_user_str(err_kind.trim()));
596+
.insert(ErrorKind::expect_from_user_str(err_kind.trim()));
597597
}
598598
},
599599
);

src/tools/compiletest/src/runtest.rs

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ impl<'test> TestCx<'test> {
704704
.map(|e| Error { msg: self.normalize_output(&e.msg, &[]), ..e });
705705

706706
let mut unexpected = Vec::new();
707+
let mut unimportant = Vec::new();
707708
let mut found = vec![false; expected_errors.len()];
708709
for actual_error in actual_errors {
709710
for pattern in &self.props.error_patterns {
@@ -738,14 +739,9 @@ impl<'test> TestCx<'test> {
738739
&& expected_kinds.contains(&actual_error.kind)
739740
&& !self.props.dont_require_annotations.contains(&actual_error.kind)
740741
{
741-
self.error(&format!(
742-
"{}:{}: unexpected {}: '{}'",
743-
file_name,
744-
actual_error.line_num_str(),
745-
actual_error.kind,
746-
actual_error.msg
747-
));
748742
unexpected.push(actual_error);
743+
} else {
744+
unimportant.push(actual_error);
749745
}
750746
}
751747
}
@@ -755,39 +751,77 @@ impl<'test> TestCx<'test> {
755751
// anything not yet found is a problem
756752
for (index, expected_error) in expected_errors.iter().enumerate() {
757753
if !found[index] {
758-
self.error(&format!(
759-
"{}:{}: expected {} not found: {}",
760-
file_name,
761-
expected_error.line_num_str(),
762-
expected_error.kind,
763-
expected_error.msg
764-
));
765754
not_found.push(expected_error);
766755
}
767756
}
768757

769758
if !unexpected.is_empty() || !not_found.is_empty() {
770759
self.error(&format!(
771-
"{} unexpected errors found, {} expected errors not found",
760+
"{} unexpected diagnostics reported, {} expected diagnostics not reported",
772761
unexpected.len(),
773762
not_found.len()
774763
));
775-
println!("status: {}\ncommand: {}\n", proc_res.status, proc_res.cmdline);
764+
let print = |e: &Error| {
765+
println!("{file_name}:{}: {}: {}", e.line_num_str(), e.kind, e.msg.cyan())
766+
};
767+
// Fuzzy matching quality:
768+
// - message and line / message and kind - great, suggested
769+
// - only message - good, suggested
770+
// - known line and kind - ok, suggested
771+
// - only known line - meh, but suggested
772+
// - others are not worth suggesting
776773
if !unexpected.is_empty() {
777-
println!("{}", "--- unexpected errors (from JSON output) ---".green());
774+
println!("{}", "--- reported but not expected (from JSON output) ---".green());
778775
for error in &unexpected {
779-
println!("{}", error.render_for_expected());
776+
print(error);
777+
for candidate in &not_found {
778+
if error.msg.contains(&candidate.msg) {
779+
let prefix = if candidate.line_num != error.line_num {
780+
"expected on a different line"
781+
} else {
782+
"expected with a different kind"
783+
}
784+
.red();
785+
print!(" {prefix}: ");
786+
print(candidate);
787+
} else if candidate.line_num.is_some()
788+
&& candidate.line_num == error.line_num
789+
{
790+
print!(" {}: ", "expected with a different message".red());
791+
print(candidate);
792+
}
793+
}
780794
}
781795
println!("{}", "---".green());
782796
}
783797
if !not_found.is_empty() {
784-
println!("{}", "--- not found errors (from test file) ---".red());
798+
println!("{}", "--- expected but not reported (from test file) ---".red());
785799
for error in &not_found {
786-
println!("{}", error.render_for_expected());
800+
print(error);
801+
for candidate in unexpected.iter().chain(&unimportant) {
802+
if candidate.msg.contains(&error.msg) {
803+
let prefix = if candidate.line_num != error.line_num {
804+
"reported on a different line"
805+
} else {
806+
"reported with a different kind"
807+
}
808+
.green();
809+
print!(" {prefix}: ");
810+
print(candidate);
811+
} else if candidate.line_num.is_some()
812+
&& candidate.line_num == error.line_num
813+
{
814+
print!(" {}: ", "reported with a different message".green());
815+
print(candidate);
816+
}
817+
}
787818
}
788-
println!("{}", "---\n".red());
819+
println!("{}", "---".red());
789820
}
790-
panic!("errors differ from expected");
821+
panic!(
822+
"errors differ from expected\nstatus: {}\ncommand: {}\n",
823+
proc_res.status, proc_res.cmdline
824+
);
791825
}
792826
}
793827

@@ -2073,7 +2107,6 @@ impl<'test> TestCx<'test> {
20732107
println!("{}", String::from_utf8_lossy(&output.stdout));
20742108
eprintln!("{}", String::from_utf8_lossy(&output.stderr));
20752109
} else {
2076-
use colored::Colorize;
20772110
eprintln!("warning: no pager configured, falling back to unified diff");
20782111
eprintln!(
20792112
"help: try configuring a git pager (e.g. `delta`) with `git config --global core.pager delta`"
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//@ should-fail
2+
3+
// The warning is reported with unknown line
4+
//@ compile-flags: -D raw_pointer_derive
5+
//~? WARN kind and unknown line match the reported warning, but we do not suggest it
6+
7+
// The error is expected but not reported at all.
8+
//~ ERROR this error does not exist
9+
10+
// The error is reported but not expected at all.
11+
// "`main` function not found in crate" (the main function is intentionally not added)
12+
13+
// An "unimportant" diagnostic is expected on a wrong line.
14+
//~ ERROR aborting due to
15+
16+
// An "unimportant" diagnostic is expected with a wrong kind.
17+
//~? ERROR For more information about an error
18+
19+
fn wrong_line_or_kind() {
20+
// A diagnostic expected on a wrong line.
21+
unresolved1;
22+
//~ ERROR cannot find value `unresolved1` in this scope
23+
24+
// A diagnostic expected with a wrong kind.
25+
unresolved2; //~ WARN cannot find value `unresolved2` in this scope
26+
27+
// A diagnostic expected with a missing kind (treated as a wrong kind).
28+
unresolved3; //~ cannot find value `unresolved3` in this scope
29+
30+
// A diagnostic expected with a wrong line and kind.
31+
unresolved4;
32+
//~ WARN cannot find value `unresolved4` in this scope
33+
}
34+
35+
fn wrong_message() {
36+
// A diagnostic expected with a wrong message, but the line is known and right.
37+
unresolvedA; //~ ERROR stub message 1
38+
39+
// A diagnostic expected with a wrong message, but the line is known and right,
40+
// even if the kind doesn't match.
41+
unresolvedB; //~ WARN stub message 2
42+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
warning: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
2+
|
3+
= note: requested on the command line with `-D raw_pointer_derive`
4+
= note: `#[warn(renamed_and_removed_lints)]` on by default
5+
6+
error[E0425]: cannot find value `unresolved1` in this scope
7+
--> $DIR/line-annotation-mismatches.rs:21:5
8+
|
9+
LL | unresolved1;
10+
| ^^^^^^^^^^^ not found in this scope
11+
12+
error[E0425]: cannot find value `unresolved2` in this scope
13+
--> $DIR/line-annotation-mismatches.rs:25:5
14+
|
15+
LL | unresolved2;
16+
| ^^^^^^^^^^^ not found in this scope
17+
18+
error[E0425]: cannot find value `unresolved3` in this scope
19+
--> $DIR/line-annotation-mismatches.rs:28:5
20+
|
21+
LL | unresolved3;
22+
| ^^^^^^^^^^^ not found in this scope
23+
24+
error[E0425]: cannot find value `unresolved4` in this scope
25+
--> $DIR/line-annotation-mismatches.rs:31:5
26+
|
27+
LL | unresolved4;
28+
| ^^^^^^^^^^^ not found in this scope
29+
30+
error[E0425]: cannot find value `unresolvedA` in this scope
31+
--> $DIR/line-annotation-mismatches.rs:37:5
32+
|
33+
LL | unresolvedA;
34+
| ^^^^^^^^^^^ not found in this scope
35+
36+
error[E0425]: cannot find value `unresolvedB` in this scope
37+
--> $DIR/line-annotation-mismatches.rs:41:5
38+
|
39+
LL | unresolvedB;
40+
| ^^^^^^^^^^^ not found in this scope
41+
42+
warning: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
43+
|
44+
= note: requested on the command line with `-D raw_pointer_derive`
45+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
46+
47+
error[E0601]: `main` function not found in crate `line_annotation_mismatches`
48+
--> $DIR/line-annotation-mismatches.rs:42:2
49+
|
50+
LL | }
51+
| ^ consider adding a `main` function to `$DIR/line-annotation-mismatches.rs`
52+
53+
warning: lint `raw_pointer_derive` has been removed: using derive with raw pointers is ok
54+
|
55+
= note: requested on the command line with `-D raw_pointer_derive`
56+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
57+
58+
error: aborting due to 7 previous errors; 3 warnings emitted
59+
60+
Some errors have detailed explanations: E0425, E0601.
61+
For more information about an error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)