]> git.proxmox.com Git - rustc.git/blame - src/tools/compiletest/src/errors.rs
bump version to 1.80.1+dfsg1-1~bpo12+pve1
[rustc.git] / src / tools / compiletest / src / errors.rs
CommitLineData
1a4d82fc 1use self::WhichLine::*;
223e47cc 2
54a0048b 3use std::fmt;
c34b1796 4use std::fs::File;
c34b1796 5use std::io::prelude::*;
94b46f34 6use std::io::BufReader;
c34b1796 7use std::path::Path;
54a0048b 8use std::str::FromStr;
e8be2606 9use std::sync::OnceLock;
54a0048b 10
60c5eb7d 11use regex::Regex;
3dfed10e 12use tracing::*;
48663c56 13
c0240ec0 14#[derive(Copy, Clone, Debug, PartialEq)]
54a0048b
SL
15pub enum ErrorKind {
16 Help,
17 Error,
18 Note,
19 Suggestion,
20 Warning,
21}
22
23impl FromStr for ErrorKind {
24 type Err = ();
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
a7813a04
XL
26 let s = s.to_uppercase();
27 let part0: &str = s.split(':').next().unwrap();
28 match part0 {
54a0048b
SL
29 "HELP" => Ok(ErrorKind::Help),
30 "ERROR" => Ok(ErrorKind::Error),
31 "NOTE" => Ok(ErrorKind::Note),
32 "SUGGESTION" => Ok(ErrorKind::Suggestion),
94b46f34 33 "WARN" | "WARNING" => Ok(ErrorKind::Warning),
54a0048b
SL
34 _ => Err(()),
35 }
36 }
37}
38
39impl fmt::Display for ErrorKind {
9fa01778 40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54a0048b 41 match *self {
abe05a73 42 ErrorKind::Help => write!(f, "help message"),
54a0048b
SL
43 ErrorKind::Error => write!(f, "error"),
44 ErrorKind::Note => write!(f, "note"),
45 ErrorKind::Suggestion => write!(f, "suggestion"),
46 ErrorKind::Warning => write!(f, "warning"),
47 }
48 }
49}
223e47cc 50
a7813a04
XL
51#[derive(Debug)]
52pub struct Error {
54a0048b 53 pub line_num: usize,
0731742a 54 /// What kind of message we expect (e.g., warning, error, suggestion).
54a0048b
SL
55 /// `None` if not specified or unknown message kind.
56 pub kind: Option<ErrorKind>,
1a4d82fc
JJ
57 pub msg: String,
58}
223e47cc 59
31ef2f64
FG
60impl Error {
61 pub fn render_for_expected(&self) -> String {
62 use colored::Colorize;
63 format!(
64 "{: <10}line {: >3}: {}",
65 self.kind.map(|kind| kind.to_string()).unwrap_or_default().to_uppercase(),
66 self.line_num,
67 self.msg.cyan(),
68 )
69 }
70}
71
85aaf69f 72#[derive(PartialEq, Debug)]
5bcae85e
SL
73enum WhichLine {
74 ThisLine,
75 FollowPrevious(usize),
76 AdjustBackward(usize),
77}
85aaf69f 78
1a4d82fc
JJ
79/// Looks for either "//~| KIND MESSAGE" or "//~^^... KIND MESSAGE"
80/// The former is a "follow" that inherits its target from the preceding line;
81/// the latter is an "adjusts" that goes that many lines up.
82///
83/// Goal is to enable tests both like: //~^^^ ERROR go up three
84/// and also //~^ ERROR message one for the preceding line, and
85/// //~| ERROR message two for that same line.
54a0048b 86///
c620b35d
FG
87/// If revision is not None, then we look
88/// for `//[X]~` instead, where `X` is the current revision.
89pub fn load_errors(testfile: &Path, revision: Option<&str>) -> Vec<Error> {
c34b1796 90 let rdr = BufReader::new(File::open(testfile).unwrap());
223e47cc 91
1a4d82fc
JJ
92 // `last_nonfollow_error` tracks the most recently seen
93 // line with an error template that did not use the
94 // follow-syntax, "//~| ...".
95 //
96 // (pnkfelix could not find an easy way to compose Iterator::scan
97 // and Iterator::filter_map to pass along this information into
98 // `parse_expected`. So instead I am storing that state here and
99 // updating it in the map callback below.)
100 let mut last_nonfollow_error = None;
223e47cc 101
54a0048b 102 rdr.lines()
5bcae85e
SL
103 .enumerate()
104 .filter_map(|(line_num, line)| {
c620b35d 105 parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), revision).map(
94b46f34 106 |(which, error)| {
5bcae85e
SL
107 match which {
108 FollowPrevious(_) => {}
109 _ => last_nonfollow_error = Some(error.line_num),
110 }
60c5eb7d 111
5bcae85e 112 error
94b46f34
XL
113 },
114 )
5bcae85e
SL
115 })
116 .collect()
1a4d82fc 117}
223e47cc 118
94b46f34
XL
119fn parse_expected(
120 last_nonfollow_error: Option<usize>,
121 line_num: usize,
122 line: &str,
c620b35d 123 test_revision: Option<&str>,
94b46f34 124) -> Option<(WhichLine, Error)> {
60c5eb7d
XL
125 // Matches comments like:
126 // //~
127 // //~|
128 // //~^
129 // //~^^^^^
c620b35d
FG
130 // //[rev1]~
131 // //[rev1,rev2]~^^
e8be2606 132 static RE: OnceLock<Regex> = OnceLock::new();
60c5eb7d 133
e8be2606
FG
134 let captures = RE
135 .get_or_init(|| Regex::new(r"//(?:\[(?P<revs>[\w\-,]+)])?~(?P<adjust>\||\^*)").unwrap())
136 .captures(line)?;
60c5eb7d 137
c620b35d
FG
138 match (test_revision, captures.name("revs")) {
139 // Only error messages that contain our revision between the square brackets apply to us.
140 (Some(test_revision), Some(revision_filters)) => {
141 if !revision_filters.as_str().split(',').any(|r| r == test_revision) {
142 return None;
143 }
144 }
60c5eb7d
XL
145
146 (None, Some(_)) => panic!("Only tests with revisions should use `//[X]~`"),
147
148 // If an error has no list of revisions, it applies to all revisions.
149 (Some(_), None) | (None, None) => {}
150 }
151
152 let (follow, adjusts) = match &captures["adjust"] {
153 "|" => (true, 0),
154 circumflexes => (false, circumflexes.len()),
85aaf69f 155 };
60c5eb7d
XL
156
157 // Get the part of the comment after the sigil (e.g. `~^^` or ~|).
158 let whole_match = captures.get(0).unwrap();
159 let (_, mut msg) = line.split_at(whole_match.end());
160
dfeec247 161 let first_word = msg.split_whitespace().next().expect("Encountered unexpected empty comment");
60c5eb7d
XL
162
163 // If we find `//~ ERROR foo` or something like that, skip the first word.
164 let kind = first_word.parse::<ErrorKind>().ok();
3dfed10e 165 if kind.is_some() {
60c5eb7d 166 msg = &msg.trim_start().split_at(first_word.len()).1;
a7813a04 167 }
60c5eb7d 168
a7813a04 169 let msg = msg.trim().to_owned();
223e47cc 170
54a0048b 171 let (which, line_num) = if follow {
041b39d2 172 assert_eq!(adjusts, 0, "use either //~| or //~^, not both.");
94b46f34
XL
173 let line_num = last_nonfollow_error.expect(
174 "encountered //~| without \
175 preceding //~^ line.",
176 );
54a0048b 177 (FollowPrevious(line_num), line_num)
85aaf69f 178 } else {
dfeec247 179 let which = if adjusts > 0 { AdjustBackward(adjusts) } else { ThisLine };
54a0048b
SL
180 let line_num = line_num - adjusts;
181 (which, line_num)
85aaf69f 182 };
223e47cc 183
94b46f34
XL
184 debug!(
185 "line={} tag={:?} which={:?} kind={:?} msg={:?}",
dfeec247
XL
186 line_num,
187 whole_match.as_str(),
94b46f34 188 which,
dfeec247
XL
189 kind,
190 msg
191 );
192 Some((which, Error { line_num, kind, msg }))
223e47cc 193}
e8be2606
FG
194
195#[cfg(test)]
196mod tests;