Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 40507bd

Browse files
committedMar 25, 2025·
Auto merge of rust-lang#138865 - petrochenkov:errwhere, r=jieyouxu
compiletest: Support matching on diagnostics without a span Using `//~? ERROR my message` on any line of the test. The new checks are exhaustive, like all other `//~` checks, and unlike the `error-pattern` directive that is sometimes used now to check for span-less diagnostics. This will allow to eliminate most on `error-pattern` directives in compile-fail tests (except those that are intentionally imprecise due to platform-specific diagnostics). I didn't migrate any of `error-pattern`s in this PR though, except those where the migration was necessary for the tests to pass.
2 parents 48994b1 + 8d5109a commit 40507bd

File tree

116 files changed

+371
-105
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+371
-105
lines changed
 

‎src/doc/rustc-dev-guide/src/tests/ui.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ several ways to match the message with the line (see the examples below):
202202
* `~|`: Associates the error level and message with the *same* line as the
203203
*previous comment*. This is more convenient than using multiple carets when
204204
there are multiple messages associated with the same line.
205+
* `~?`: Used to match error levels and messages with errors not having line
206+
information. These can be placed on any line in the test file, but are
207+
conventionally placed at the end.
205208

206209
Example:
207210

@@ -270,10 +273,23 @@ fn main() {
270273
//~| ERROR this pattern has 1 field, but the corresponding tuple struct has 3 fields [E0023]
271274
```
272275

276+
#### Error without line information
277+
278+
Use `//~?` to match an error without line information.
279+
`//~?` is precise and will not match errors if their line information is available.
280+
It should be preferred to using `error-pattern`, which is imprecise and non-exhaustive.
281+
282+
```rust,ignore
283+
//@ compile-flags: --print yyyy
284+
285+
//~? ERROR unknown print request: `yyyy`
286+
```
287+
273288
### `error-pattern`
274289

275-
The `error-pattern` [directive](directives.md) can be used for messages that don't
276-
have a specific span.
290+
The `error-pattern` [directive](directives.md) can be used for runtime messages, which don't
291+
have a specific span, or for compile time messages if imprecise matching is required due to
292+
multi-line platform specific diagnostics.
277293

278294
Let's think about this test:
279295

@@ -300,7 +316,9 @@ fn main() {
300316
}
301317
```
302318

303-
But for strict testing, try to use the `ERROR` annotation as much as possible.
319+
But for strict testing, try to use the `ERROR` annotation as much as possible,
320+
including `//~?` annotations for diagnostics without span.
321+
For compile time diagnostics `error-pattern` should very rarely be necessary.
304322

305323
### Error levels
306324

@@ -353,7 +371,7 @@ would be a `.mir.stderr` and `.thir.stderr` file with the different outputs of
353371
the different revisions.
354372

355373
> Note: cfg revisions also work inside the source code with `#[cfg]` attributes.
356-
>
374+
>
357375
> By convention, the `FALSE` cfg is used to have an always-false config.
358376
359377
## Controlling pass/fail expectations

‎src/tools/compiletest/src/errors.rs

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ use std::sync::OnceLock;
99
use regex::Regex;
1010
use tracing::*;
1111

12-
use self::WhichLine::*;
13-
1412
#[derive(Copy, Clone, Debug, PartialEq)]
1513
pub enum ErrorKind {
1614
Help,
@@ -50,7 +48,7 @@ impl fmt::Display for ErrorKind {
5048

5149
#[derive(Debug)]
5250
pub struct Error {
53-
pub line_num: usize,
51+
pub line_num: Option<usize>,
5452
/// What kind of message we expect (e.g., warning, error, suggestion).
5553
/// `None` if not specified or unknown message kind.
5654
pub kind: Option<ErrorKind>,
@@ -63,17 +61,14 @@ impl Error {
6361
format!(
6462
"{: <10}line {: >3}: {}",
6563
self.kind.map(|kind| kind.to_string()).unwrap_or_default().to_uppercase(),
66-
self.line_num,
64+
self.line_num_str(),
6765
self.msg.cyan(),
6866
)
6967
}
70-
}
7168

72-
#[derive(PartialEq, Debug)]
73-
enum WhichLine {
74-
ThisLine,
75-
FollowPrevious(usize),
76-
AdjustBackward(usize),
69+
pub fn line_num_str(&self) -> String {
70+
self.line_num.map_or("?".to_string(), |line_num| line_num.to_string())
71+
}
7772
}
7873

7974
/// Looks for either "//~| KIND MESSAGE" or "//~^^... KIND MESSAGE"
@@ -105,12 +100,10 @@ pub fn load_errors(testfile: &Path, revision: Option<&str>) -> Vec<Error> {
105100
.filter(|(_, line)| line.is_ok())
106101
.filter_map(|(line_num, line)| {
107102
parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), revision).map(
108-
|(which, error)| {
109-
match which {
110-
FollowPrevious(_) => {}
111-
_ => last_nonfollow_error = Some(error.line_num),
103+
|(follow_prev, error)| {
104+
if !follow_prev {
105+
last_nonfollow_error = error.line_num;
112106
}
113-
114107
error
115108
},
116109
)
@@ -123,18 +116,19 @@ fn parse_expected(
123116
line_num: usize,
124117
line: &str,
125118
test_revision: Option<&str>,
126-
) -> Option<(WhichLine, Error)> {
119+
) -> Option<(bool, Error)> {
127120
// Matches comments like:
128121
// //~
129122
// //~|
130123
// //~^
131124
// //~^^^^^
125+
// //~?
132126
// //[rev1]~
133127
// //[rev1,rev2]~^^
134128
static RE: OnceLock<Regex> = OnceLock::new();
135129

136130
let captures = RE
137-
.get_or_init(|| Regex::new(r"//(?:\[(?P<revs>[\w\-,]+)])?~(?P<adjust>\||\^*)").unwrap())
131+
.get_or_init(|| Regex::new(r"//(?:\[(?P<revs>[\w\-,]+)])?~(?P<adjust>\?|\||\^*)").unwrap())
138132
.captures(line)?;
139133

140134
match (test_revision, captures.name("revs")) {
@@ -151,11 +145,6 @@ fn parse_expected(
151145
(Some(_), None) | (None, None) => {}
152146
}
153147

154-
let (follow, adjusts) = match &captures["adjust"] {
155-
"|" => (true, 0),
156-
circumflexes => (false, circumflexes.len()),
157-
};
158-
159148
// Get the part of the comment after the sigil (e.g. `~^^` or ~|).
160149
let whole_match = captures.get(0).unwrap();
161150
let (_, mut msg) = line.split_at(whole_match.end());
@@ -170,28 +159,24 @@ fn parse_expected(
170159

171160
let msg = msg.trim().to_owned();
172161

173-
let (which, line_num) = if follow {
174-
assert_eq!(adjusts, 0, "use either //~| or //~^, not both.");
175-
let line_num = last_nonfollow_error.expect(
176-
"encountered //~| without \
177-
preceding //~^ line.",
178-
);
179-
(FollowPrevious(line_num), line_num)
162+
let line_num_adjust = &captures["adjust"];
163+
let (follow_prev, line_num) = if line_num_adjust == "|" {
164+
(true, Some(last_nonfollow_error.expect("encountered //~| without preceding //~^ line")))
165+
} else if line_num_adjust == "?" {
166+
(false, None)
180167
} else {
181-
let which = if adjusts > 0 { AdjustBackward(adjusts) } else { ThisLine };
182-
let line_num = line_num - adjusts;
183-
(which, line_num)
168+
(false, Some(line_num - line_num_adjust.len()))
184169
};
185170

186171
debug!(
187-
"line={} tag={:?} which={:?} kind={:?} msg={:?}",
172+
"line={:?} tag={:?} follow_prev={:?} kind={:?} msg={:?}",
188173
line_num,
189174
whole_match.as_str(),
190-
which,
175+
follow_prev,
191176
kind,
192177
msg
193178
);
194-
Some((which, Error { line_num, kind, msg }))
179+
Some((follow_prev, Error { line_num, kind, msg }))
195180
}
196181

197182
#[cfg(test)]

0 commit comments

Comments
 (0)
This repository has been archived.