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