]> git.proxmox.com Git - rustc.git/blob - src/tools/compiletest/src/errors.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / tools / compiletest / src / errors.rs
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 }