]> git.proxmox.com Git - rustc.git/blob - src/tools/compiletest/src/errors.rs
Imported Upstream version 1.10.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" => Ok(ErrorKind::Warning),
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"),
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 { ThisLine, FollowPrevious(usize), AdjustBackward(usize) }
68
69 /// Looks for either "//~| KIND MESSAGE" or "//~^^... KIND MESSAGE"
70 /// The former is a "follow" that inherits its target from the preceding line;
71 /// the latter is an "adjusts" that goes that many lines up.
72 ///
73 /// Goal is to enable tests both like: //~^^^ ERROR go up three
74 /// and also //~^ ERROR message one for the preceding line, and
75 /// //~| ERROR message two for that same line.
76 ///
77 /// If cfg is not None (i.e., in an incremental test), then we look
78 /// for `//[X]~` instead, where `X` is the current `cfg`.
79 pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
80 let rdr = BufReader::new(File::open(testfile).unwrap());
81
82 // `last_nonfollow_error` tracks the most recently seen
83 // line with an error template that did not use the
84 // follow-syntax, "//~| ...".
85 //
86 // (pnkfelix could not find an easy way to compose Iterator::scan
87 // and Iterator::filter_map to pass along this information into
88 // `parse_expected`. So instead I am storing that state here and
89 // updating it in the map callback below.)
90 let mut last_nonfollow_error = None;
91
92 let tag = match cfg {
93 Some(rev) => format!("//[{}]~", rev),
94 None => format!("//~")
95 };
96
97 rdr.lines()
98 .enumerate()
99 .filter_map(|(line_num, line)| {
100 parse_expected(last_nonfollow_error,
101 line_num + 1,
102 &line.unwrap(),
103 &tag)
104 .map(|(which, error)| {
105 match which {
106 FollowPrevious(_) => {}
107 _ => last_nonfollow_error = Some(error.line_num),
108 }
109 error
110 })
111 })
112 .collect()
113 }
114
115 fn parse_expected(last_nonfollow_error: Option<usize>,
116 line_num: usize,
117 line: &str,
118 tag: &str)
119 -> Option<(WhichLine, Error)> {
120 let start = match line.find(tag) { Some(i) => i, None => return None };
121 let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' {
122 (true, 0)
123 } else {
124 (false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count())
125 };
126 let kind_start = start + tag.len() + adjusts + (follow as usize);
127 let (kind, msg);
128 match
129 line[kind_start..].split_whitespace()
130 .next()
131 .expect("Encountered unexpected empty comment")
132 .parse::<ErrorKind>()
133 {
134 Ok(k) => {
135 // If we find `//~ ERROR foo` or something like that:
136 kind = Some(k);
137 let letters = line[kind_start..].chars();
138 msg = letters.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.skip_while(|c| c.is_whitespace())
147 .collect::<String>();
148 }
149 }
150 let msg = msg.trim().to_owned();
151
152 let (which, line_num) = if follow {
153 assert!(adjusts == 0, "use either //~| or //~^, not both.");
154 let line_num = last_nonfollow_error.expect("encountered //~| without \
155 preceding //~^ line.");
156 (FollowPrevious(line_num), line_num)
157 } else {
158 let which =
159 if adjusts > 0 { AdjustBackward(adjusts) } else { ThisLine };
160 let line_num = line_num - adjusts;
161 (which, line_num)
162 };
163
164 debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}",
165 line_num, tag, which, kind, msg);
166 Some((which, Error { line_num: line_num,
167 kind: kind,
168 msg: msg, }))
169 }