]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/errors.rs
Rollup merge of #58202 - varkor:deprecated-future-external, r=GuillaumeGomez
[rust.git] / src / tools / compiletest / src / errors.rs
1 use self::WhichLine::*;
2
3 use std::fmt;
4 use std::fs::File;
5 use std::io::prelude::*;
6 use std::io::BufReader;
7 use std::path::Path;
8 use std::str::FromStr;
9
10 #[derive(Clone, Debug, PartialEq)]
11 pub enum ErrorKind {
12     Help,
13     Error,
14     Note,
15     Suggestion,
16     Warning,
17 }
18
19 impl FromStr for ErrorKind {
20     type Err = ();
21     fn from_str(s: &str) -> Result<Self, Self::Err> {
22         let s = s.to_uppercase();
23         let part0: &str = s.split(':').next().unwrap();
24         match part0 {
25             "HELP" => Ok(ErrorKind::Help),
26             "ERROR" => Ok(ErrorKind::Error),
27             "NOTE" => Ok(ErrorKind::Note),
28             "SUGGESTION" => Ok(ErrorKind::Suggestion),
29             "WARN" | "WARNING" => Ok(ErrorKind::Warning),
30             _ => Err(()),
31         }
32     }
33 }
34
35 impl fmt::Display for ErrorKind {
36     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37         match *self {
38             ErrorKind::Help => write!(f, "help message"),
39             ErrorKind::Error => write!(f, "error"),
40             ErrorKind::Note => write!(f, "note"),
41             ErrorKind::Suggestion => write!(f, "suggestion"),
42             ErrorKind::Warning => write!(f, "warning"),
43         }
44     }
45 }
46
47 #[derive(Debug)]
48 pub struct Error {
49     pub line_num: usize,
50     /// What kind of message we expect (e.g., warning, error, suggestion).
51     /// `None` if not specified or unknown message kind.
52     pub kind: Option<ErrorKind>,
53     pub msg: String,
54 }
55
56 #[derive(PartialEq, Debug)]
57 enum WhichLine {
58     ThisLine,
59     FollowPrevious(usize),
60     AdjustBackward(usize),
61 }
62
63 /// Looks for either "//~| KIND MESSAGE" or "//~^^... KIND MESSAGE"
64 /// The former is a "follow" that inherits its target from the preceding line;
65 /// the latter is an "adjusts" that goes that many lines up.
66 ///
67 /// Goal is to enable tests both like: //~^^^ ERROR go up three
68 /// and also //~^ ERROR message one for the preceding line, and
69 ///          //~| ERROR message two for that same line.
70 ///
71 /// If cfg is not None (i.e., in an incremental test), then we look
72 /// for `//[X]~` instead, where `X` is the current `cfg`.
73 pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
74     let rdr = BufReader::new(File::open(testfile).unwrap());
75
76     // `last_nonfollow_error` tracks the most recently seen
77     // line with an error template that did not use the
78     // follow-syntax, "//~| ...".
79     //
80     // (pnkfelix could not find an easy way to compose Iterator::scan
81     // and Iterator::filter_map to pass along this information into
82     // `parse_expected`. So instead I am storing that state here and
83     // updating it in the map callback below.)
84     let mut last_nonfollow_error = None;
85
86     let tag = match cfg {
87         Some(rev) => format!("//[{}]~", rev),
88         None => "//~".to_string(),
89     };
90
91     rdr.lines()
92         .enumerate()
93         .filter_map(|(line_num, line)| {
94             parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), &tag).map(
95                 |(which, error)| {
96                     match which {
97                         FollowPrevious(_) => {}
98                         _ => last_nonfollow_error = Some(error.line_num),
99                     }
100                     error
101                 },
102             )
103         })
104         .collect()
105 }
106
107 fn parse_expected(
108     last_nonfollow_error: Option<usize>,
109     line_num: usize,
110     line: &str,
111     tag: &str,
112 ) -> Option<(WhichLine, Error)> {
113     let start = line.find(tag)?;
114     let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' {
115         (true, 0)
116     } else {
117         (
118             false,
119             line[start + tag.len()..]
120                 .chars()
121                 .take_while(|c| *c == '^')
122                 .count(),
123         )
124     };
125     let kind_start = start + tag.len() + adjusts + (follow as usize);
126     let (kind, msg);
127     match line[kind_start..]
128         .split_whitespace()
129         .next()
130         .expect("Encountered unexpected empty comment")
131         .parse::<ErrorKind>()
132     {
133         Ok(k) => {
134             // If we find `//~ ERROR foo` or something like that:
135             kind = Some(k);
136             let letters = line[kind_start..].chars();
137             msg = letters
138                 .skip_while(|c| c.is_whitespace())
139                 .skip_while(|c| !c.is_whitespace())
140                 .collect::<String>();
141         }
142         Err(_) => {
143             // Otherwise we found `//~ foo`:
144             kind = None;
145             let letters = line[kind_start..].chars();
146             msg = letters
147                 .skip_while(|c| c.is_whitespace())
148                 .collect::<String>();
149         }
150     }
151     let msg = msg.trim().to_owned();
152
153     let (which, line_num) = if follow {
154         assert_eq!(adjusts, 0, "use either //~| or //~^, not both.");
155         let line_num = last_nonfollow_error.expect(
156             "encountered //~| without \
157              preceding //~^ line.",
158         );
159         (FollowPrevious(line_num), line_num)
160     } else {
161         let which = if adjusts > 0 {
162             AdjustBackward(adjusts)
163         } else {
164             ThisLine
165         };
166         let line_num = line_num - adjusts;
167         (which, line_num)
168     };
169
170     debug!(
171         "line={} tag={:?} which={:?} kind={:?} msg={:?}",
172         line_num, tag, which, kind, msg
173     );
174     Some((
175         which,
176         Error {
177             line_num,
178             kind,
179             msg,
180         },
181     ))
182 }