Skip to content

Commit

Permalink
Auto merge of #66524 - ecstatic-morse:compiletest-multiple-revisions,…
Browse files Browse the repository at this point in the history
… r=Centril

Support multiple revisions in `compiletest`

The `//[X]~` syntax filters errors for tests that are run across multiple cfgs with  `// revisions:`. This commit extends that syntax to accept `//[X,Y]~`, which will match multiple cfgs to the same error annotation. This is functionally the same as writing two comments, `//[X]~` and `//[Y]~`, but can fit on a single line.

While refactoring `compiletest` to support this, I also uncovered a small bug that was causing an incremental test to always pass, despite no errors being emitted.

r? @Centril
  • Loading branch information
bors committed Nov 22, 2019
2 parents abd6955 + c537f22 commit bd816fd
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 61 deletions.
3 changes: 1 addition & 2 deletions src/test/incremental/warnings-reemitted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
// compile-flags: -Coverflow-checks=on
// build-pass (FIXME(62277): could be check-pass?)

#![allow(warnings)]
#![warn(const_err)]

fn main() {
255u8 + 1; //~ WARNING this expression will panic at run-time
let _ = 255u8 + 1; //~ WARNING attempt to add with overflow
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,4 @@ fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) {
}

#[rustc_error]
fn main() { }
//[ok]~^ ERROR fatal error triggered by #[rustc_error]
//[oneuse]~^^ ERROR fatal error triggered by #[rustc_error]
fn main() { } //[ok,oneuse]~ ERROR fatal error triggered by #[rustc_error]
10 changes: 2 additions & 8 deletions src/test/ui/error-codes/E0161.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,8 @@

fn foo(x: Box<[i32]>) {
box *x;
//[migrate]~^ ERROR E0161
//[nll]~^^ ERROR E0161
//[zflags]~^^^ ERROR E0161
//[edition]~^^^^ ERROR E0161
//[migrateul]~^^^^^ ERROR E0161
//[nllul]~^^^^^^ ERROR E0161
//[zflagsul]~^^^^^^^ ERROR E0161
//[editionul]~^^^^^^^^ ERROR E0161
//[migrate,nll,zflags,edition]~^ ERROR E0161
//[migrateul,nllul,zflagsul,editionul]~^^ ERROR E0161
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ struct Foo<T> {
impl<T> Foo<T>
where
T: WithRegion<'_>
//[rust2015]~^ ERROR `'_` cannot be used here
//[rust2018]~^^ ERROR `'_` cannot be used here
//[rust2015,rust2018]~^ ERROR `'_` cannot be used here
{ }

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ trait Foo { }
impl<T> Foo for Vec<T>
where
T: WithType<&u32>
//[rust2015]~^ ERROR `&` without an explicit lifetime name cannot be used here
//[rust2018]~^^ ERROR `&` without an explicit lifetime name cannot be used here
//[rust2015,rust2018]~^ ERROR `&` without an explicit lifetime name cannot be used here
{ }

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ trait Foo { }
impl<T> Foo for Vec<T>
where
T: WithRegion<'_>
//[rust2015]~^ ERROR `'_` cannot be used here
//[rust2018]~^^ ERROR `'_` cannot be used here
//[rust2015,rust2018]~^ ERROR `'_` cannot be used here
{ }

fn main() {}
90 changes: 48 additions & 42 deletions src/tools/compiletest/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use std::io::BufReader;
use std::path::Path;
use std::str::FromStr;

use lazy_static::lazy_static;
use log::*;
use regex::Regex;

#[derive(Clone, Debug, PartialEq)]
pub enum ErrorKind {
Expand Down Expand Up @@ -85,20 +87,16 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
// updating it in the map callback below.)
let mut last_nonfollow_error = None;

let tag = match cfg {
Some(rev) => format!("//[{}]~", rev),
None => "//~".to_string(),
};

rdr.lines()
.enumerate()
.filter_map(|(line_num, line)| {
parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), &tag).map(
parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), cfg).map(
|(which, error)| {
match which {
FollowPrevious(_) => {}
_ => last_nonfollow_error = Some(error.line_num),
}

error
},
)
Expand All @@ -110,46 +108,54 @@ fn parse_expected(
last_nonfollow_error: Option<usize>,
line_num: usize,
line: &str,
tag: &str,
cfg: Option<&str>,
) -> Option<(WhichLine, Error)> {
let start = line.find(tag)?;
let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' {
(true, 0)
} else {
(
false,
line[start + tag.len()..]
.chars()
.take_while(|c| *c == '^')
.count(),
)
// Matches comments like:
// //~
// //~|
// //~^
// //~^^^^^
// //[cfg1]~
// //[cfg1,cfg2]~^^
lazy_static! {
static ref RE: Regex =
Regex::new(r"//(?:\[(?P<cfgs>[\w,]+)])?~(?P<adjust>\||\^*)").unwrap();
}

let captures = RE.captures(line)?;

match (cfg, captures.name("cfgs")) {
// Only error messages that contain our `cfg` betweeen the square brackets apply to us.
(Some(cfg), Some(filter)) if !filter.as_str().split(',').any(|s| s == cfg)
=> return None,
(Some(_), Some(_)) => {}

(None, Some(_)) => panic!("Only tests with revisions should use `//[X]~`"),

// If an error has no list of revisions, it applies to all revisions.
(Some(_), None) | (None, None) => {}
}

let (follow, adjusts) = match &captures["adjust"] {
"|" => (true, 0),
circumflexes => (false, circumflexes.len()),
};
let kind_start = start + tag.len() + adjusts + (follow as usize);
let (kind, msg);
match line[kind_start..]

// Get the part of the comment after the sigil (e.g. `~^^` or ~|).
let whole_match = captures.get(0).unwrap();
let (_, mut msg) = line.split_at(whole_match.end());

let first_word = msg
.split_whitespace()
.next()
.expect("Encountered unexpected empty comment")
.parse::<ErrorKind>()
{
Ok(k) => {
// If we find `//~ ERROR foo` or something like that:
kind = Some(k);
let letters = line[kind_start..].chars();
msg = letters
.skip_while(|c| c.is_whitespace())
.skip_while(|c| !c.is_whitespace())
.collect::<String>();
}
Err(_) => {
// Otherwise we found `//~ foo`:
kind = None;
let letters = line[kind_start..].chars();
msg = letters
.skip_while(|c| c.is_whitespace())
.collect::<String>();
}
.expect("Encountered unexpected empty comment");

// If we find `//~ ERROR foo` or something like that, skip the first word.
let kind = first_word.parse::<ErrorKind>().ok();
if let Some(_) = kind {
msg = &msg.trim_start().split_at(first_word.len()).1;
}

let msg = msg.trim().to_owned();

let (which, line_num) = if follow {
Expand All @@ -171,7 +177,7 @@ fn parse_expected(

debug!(
"line={} tag={:?} which={:?} kind={:?} msg={:?}",
line_num, tag, which, kind, msg
line_num, whole_match.as_str(), which, kind, msg
);
Some((
which,
Expand Down

0 comments on commit bd816fd

Please sign in to comment.