Skip to content

Commit f7233f8

Browse files
committed
Check that diagnostics happen in the line that they are annotated for
1 parent a3c9b4b commit f7233f8

File tree

5 files changed

+128
-19
lines changed

5 files changed

+128
-19
lines changed

ui_test/src/comments.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub(crate) struct ErrorMatch {
3434
pub matched: String,
3535
pub revision: Option<String>,
3636
pub definition_line: usize,
37+
/// The line this pattern is expecting to find a message in.
38+
pub line: usize,
3739
}
3840

3941
impl Comments {
@@ -47,9 +49,13 @@ impl Comments {
4749
pub(crate) fn parse(path: &Path, content: &str) -> Self {
4850
let mut this = Self::default();
4951
let error_pattern_regex =
50-
Regex::new(r"//(\[(?P<revision>[^\]]+)\])?~[|^]*\s*(ERROR|HELP|WARN)?:?(?P<text>.*)")
52+
Regex::new(r"//(\[(?P<revision>[^\]]+)\])?~(?P<offset>\||[\^]+)?\s*(ERROR|HELP|WARN)?:?(?P<text>.*)")
5153
.unwrap();
54+
55+
// The line that a `|` will refer to
56+
let mut fallthrough_to = None;
5257
for (l, line) in content.lines().enumerate() {
58+
let l = l + 1; // enumerate starts at 0, but line numbers start at 1
5359
if let Some(revisions) = line.strip_prefix("// revisions:") {
5460
assert_eq!(
5561
this.revisions,
@@ -113,7 +119,26 @@ impl Comments {
113119
let matched = captures["text"].trim().to_string();
114120

115121
let revision = captures.name("revision").map(|rev| rev.as_str().to_string());
116-
this.error_matches.push(ErrorMatch { matched, revision, definition_line: l });
122+
123+
let match_line = match captures.name("offset").map(|rev| rev.as_str()) {
124+
Some("|") => fallthrough_to.expect("`//~|` pattern without preceding line"),
125+
Some(pat) => {
126+
debug_assert!(pat.chars().all(|c| c == '^'));
127+
l - pat.len()
128+
}
129+
None => l,
130+
};
131+
132+
fallthrough_to = Some(match_line);
133+
134+
this.error_matches.push(ErrorMatch {
135+
matched,
136+
revision,
137+
definition_line: l,
138+
line: match_line,
139+
});
140+
} else {
141+
fallthrough_to = None;
117142
}
118143
}
119144
this

ui_test/src/comments/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn main() {
1313
";
1414
let comments = Comments::parse(Path::new("<dummy>"), s);
1515
println!("parsed comments: {:#?}", comments);
16-
assert_eq!(comments.error_matches[0].definition_line, 4);
16+
assert_eq!(comments.error_matches[0].definition_line, 5);
1717
assert_eq!(comments.error_matches[0].revision, None);
1818
assert_eq!(
1919
comments.error_matches[0].matched,

ui_test/src/lib.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -266,12 +266,13 @@ fn run_test(
266266
comments,
267267
);
268268
// Check error annotations in the source against output
269-
check_annotations(&diagnostics, &mut errors, config, revision, comments);
269+
check_annotations(&diagnostics, path, &mut errors, config, revision, comments);
270270
(miri, errors)
271271
}
272272

273273
fn check_annotations(
274274
diagnostics: &Diagnostics,
275+
path: &Path,
275276
errors: &mut Errors,
276277
config: &Config,
277278
revision: &str,
@@ -291,7 +292,7 @@ fn check_annotations(
291292
}
292293
found_annotation = true;
293294
}
294-
'outer: for &ErrorMatch { ref matched, revision: ref rev, definition_line } in
295+
'pattern: for &ErrorMatch { ref matched, revision: ref rev, definition_line, line } in
295296
&comments.error_matches
296297
{
297298
// FIXME: check that the error happens on the marked line
@@ -304,13 +305,16 @@ fn check_annotations(
304305

305306
found_annotation = true;
306307

307-
for msg in diagnostics
308-
.messages
309-
.iter()
310-
.flat_map(|msg| std::iter::once(msg).chain(msg.children.iter()))
311-
{
312-
if msg.message.contains(matched) {
313-
continue 'outer;
308+
for msg in &diagnostics.messages {
309+
// Allow any message or child message to match.
310+
if let Some(sub_msg) =
311+
std::iter::once(msg).chain(&msg.children).find(|msg| msg.message.contains(matched))
312+
{
313+
// Child messages often don't have spans, so re-use their parents' span (if any)
314+
if msg.line(path) == Some(line) || sub_msg.line(path) == Some(line) {
315+
// Actually a match for the expression we looked for, go to the next pattern
316+
continue 'pattern;
317+
}
314318
}
315319
}
316320

ui_test/src/rustc_stderr.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,41 @@
1-
use std::fmt::Write;
1+
use std::{
2+
fmt::Write,
3+
path::{Path, PathBuf},
4+
};
25

36
#[derive(serde::Deserialize, Debug)]
47
pub(crate) struct RustcMessage {
58
pub(crate) rendered: Option<String>,
69
pub(crate) spans: Vec<Span>,
7-
pub(crate) level: String,
810
pub(crate) message: String,
911
pub(crate) children: Vec<RustcMessage>,
1012
}
1113

14+
impl RustcMessage {
15+
pub fn line(&self, file: &Path) -> Option<usize> {
16+
self.spans.iter().find_map(|span| span.line(file))
17+
}
18+
}
19+
20+
impl Span {
21+
pub fn line(&self, file: &Path) -> Option<usize> {
22+
match &self.expansion {
23+
None => (self.file_name == file).then_some(self.line_start),
24+
Some(expansion) => expansion.span.line(file),
25+
}
26+
}
27+
}
28+
29+
#[derive(serde::Deserialize, Debug)]
30+
pub(crate) struct Expansion {
31+
span: Span,
32+
}
33+
1234
#[derive(serde::Deserialize, Debug)]
1335
pub(crate) struct Span {
1436
pub(crate) line_start: usize,
15-
pub(crate) is_primary: bool,
37+
pub(crate) file_name: PathBuf,
38+
pub(crate) expansion: Option<Box<Expansion>>,
1639
}
1740

1841
#[derive(Debug)]

ui_test/src/tests.rs

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::path::{Path, PathBuf};
22

3-
use crate::rustc_stderr::{Diagnostics, RustcMessage};
3+
use crate::rustc_stderr::{Diagnostics, RustcMessage, Span};
44

55
use super::{check_annotations, Comments, Config, Error, Mode, OutputConflictHandling};
66

@@ -48,13 +48,70 @@ error: aborting due to previous error
4848
RustcMessage {
4949
message:"Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(),
5050
rendered: None,
51-
spans: vec![],
52-
level: "".to_string(),
51+
spans: vec![Span {file_name:PathBuf::from("moobar"), line_start: 4, expansion: None }],
5352
children: vec![],
5453
}
5554
]
5655
};
57-
check_annotations(&diagnostics, &mut errors, &config, "", &comments);
56+
check_annotations(&diagnostics, Path::new("moobar"), &mut errors, &config, "", &comments);
57+
match &errors[..] {
58+
[Error::PatternNotFound { .. }] => {}
59+
_ => panic!("{:#?}", errors),
60+
}
61+
}
62+
63+
#[test]
64+
fn find_pattern() {
65+
let s = r"
66+
use std::mem;
67+
68+
fn main() {
69+
let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address 0x10 is unallocated)
70+
}
71+
";
72+
let comments = Comments::parse(Path::new("<dummy>"), s);
73+
let mut errors = vec![];
74+
let config = config();
75+
let unnormalized_stderr = r"
76+
error: Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)
77+
--> tests/compile-fail/validity/dangling_ref1.rs:6:29
78+
|
79+
LL | let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR encountered a dangling reference (address 0x10 is unallocated)
80+
| ^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (address 0x10 is unallocated)
81+
|
82+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
83+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
84+
85+
= note: inside `main` at tests/compile-fail/validity/dangling_ref1.rs:6:29
86+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
87+
error: aborting due to previous error
88+
";
89+
let diagnostics = Diagnostics { rendered: unnormalized_stderr.to_string(), messages: vec![
90+
RustcMessage {
91+
message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(),
92+
rendered: None,
93+
spans: vec![Span {file_name:PathBuf::from("moobar"), line_start: 5, expansion: None }],
94+
children: vec![],
95+
}
96+
]
97+
};
98+
check_annotations(&diagnostics, Path::new("moobar"), &mut errors, &config, "", &comments);
99+
match &errors[..] {
100+
[] => {}
101+
_ => panic!("{:#?}", errors),
102+
}
103+
104+
// only difference to above is a wrong line number
105+
let diagnostics = Diagnostics { rendered: unnormalized_stderr.to_string(), messages: vec![
106+
RustcMessage {
107+
message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(),
108+
rendered: None,
109+
spans: vec![Span {file_name:PathBuf::from("moobar"), line_start: 6, expansion: None }],
110+
children: vec![],
111+
}
112+
]
113+
};
114+
check_annotations(&diagnostics, Path::new("moobar"), &mut errors, &config, "", &comments);
58115
match &errors[..] {
59116
[Error::PatternNotFound { .. }] => {}
60117
_ => panic!("{:#?}", errors),

0 commit comments

Comments
 (0)