diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs index c795e69a44de..f15db7d9371d 100644 --- a/src/compiletest/errors.rs +++ b/src/compiletest/errors.rs @@ -7,6 +7,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +use self::WhichLine::*; use std::ascii::AsciiExt; use std::io::{BufferedReader, File}; @@ -18,28 +19,74 @@ pub struct ExpectedError { pub msg: String, } -pub static EXPECTED_PATTERN : &'static str = r"//~(?P\^*)\s*(?P\S*)\s*(?P.*)"; +/// Looks for either "//~| KIND MESSAGE" or "//~^^... KIND MESSAGE" +/// The former is a "follow" that inherits its target from the preceding line; +/// the latter is an "adjusts" that goes that many lines up. +/// +/// Goal is to enable tests both like: //~^^^ ERROR go up three +/// and also //~^ ERROR message one for the preceding line, and +/// //~| ERROR message two for that same line. + +pub static EXPECTED_PATTERN : &'static str = + r"//~(?P\|)?(?P\^*)\s*(?P\S*)\s*(?P.*)"; + +#[deriving(PartialEq, Show)] +enum WhichLine { ThisLine, FollowPrevious(uint), AdjustBackward(uint) } // Load any test directives embedded in the file pub fn load_errors(re: &Regex, testfile: &Path) -> Vec { let mut rdr = BufferedReader::new(File::open(testfile).unwrap()); + // `last_nonfollow_error` tracks the most recently seen + // line with an error template that did not use the + // follow-syntax, "//~| ...". + // + // (pnkfelix could not find an easy way to compose Iterator::scan + // and Iterator::filter_map to pass along this information into + // `parse_expected`. So instead I am storing that state here and + // updating it in the map callback below.) + let mut last_nonfollow_error = None; + rdr.lines().enumerate().filter_map(|(line_no, ln)| { - parse_expected(line_no + 1, ln.unwrap().as_slice(), re) + parse_expected(last_nonfollow_error, + line_no + 1, + ln.unwrap().as_slice(), re) + .map(|(which, error)| { + match which { + FollowPrevious(_) => {} + _ => last_nonfollow_error = Some(error.line), + } + error + }) }).collect() } -fn parse_expected(line_num: uint, line: &str, re: &Regex) -> Option { +fn parse_expected(last_nonfollow_error: Option, + line_num: uint, + line: &str, + re: &Regex) -> Option<(WhichLine, ExpectedError)> { re.captures(line).and_then(|caps| { let adjusts = caps.name("adjusts").len(); let kind = caps.name("kind").to_ascii_lower(); let msg = caps.name("msg").trim().to_string(); + let follow = caps.name("follow").len() > 0; - debug!("line={} kind={} msg={}", line_num, kind, msg); - Some(ExpectedError { - line: line_num - adjusts, - kind: kind, - msg: msg, - }) + let (which, line) = if follow { + assert!(adjusts == 0, "use either //~| or //~^, not both."); + let line = last_nonfollow_error.unwrap_or_else(|| { + panic!("encountered //~| without preceding //~^ line.") + }); + (FollowPrevious(line), line) + } else { + let which = + if adjusts > 0 { AdjustBackward(adjusts) } else { ThisLine }; + let line = line_num - adjusts; + (which, line) + }; + + debug!("line={} which={} kind={} msg={}", line_num, which, kind, msg); + Some((which, ExpectedError { line: line, + kind: kind, + msg: msg, })) }) }