about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-06-29 11:18:07 +0200
committerGitHub <noreply@github.com>2019-06-29 11:18:07 +0200
commit0721364f0de6191cddeaaaeb112fb74aa1a9e30a (patch)
tree97d7e70fbfd8a91d0f13e1d35241283112684621
parent6c0ab739fb77c67d00ba5b5fa357deec404f9bce (diff)
parent93077f3f3922817a70013c403831bf1db4a30114 (diff)
downloadrust-0721364f0de6191cddeaaaeb112fb74aa1a9e30a.tar.gz
rust-0721364f0de6191cddeaaaeb112fb74aa1a9e30a.zip
Rollup merge of #61755 - Centril:compiletest-force-check, r=petrochenkov
Add `--pass $mode` to compiletest through `./x.py`

Adds a flag `--pass $mode` to compiletest, which is exposed through `./x.py`.

When `--pass $mode` is passed, `{check,build,compile,run}-pass` tests will be forced to run under the given `$mode` unless the directive `// ignore-pass` exists in the test file.

The modes are explained in https://github.com/rust-lang/rust/pull/61778:
- `check` has the same effect as `cargo check`
- `build` or `compile` have the same effect as `cargo build`
- `run` has the same effect as `cargo run`

On my machine, `./x.py -i test src/test/run-pass --stage 1 --pass check` takes 38 seconds whereas it takes 2 min 7 seconds without `--pass check`.

cc https://github.com/rust-lang/rust/issues/61712

r? @petrochenkov
-rw-r--r--src/bootstrap/builder/tests.rs2
-rw-r--r--src/bootstrap/flags.rs17
-rw-r--r--src/bootstrap/test.rs5
-rw-r--r--src/test/ui/consts/const-eval/promoted_errors.rs24
-rw-r--r--src/test/ui/consts/const-eval/promoted_errors.stderr42
-rw-r--r--src/test/ui/emit-artifact-notifications.rs2
-rw-r--r--src/test/ui/lint/lint-type-overflow2.rs16
-rw-r--r--src/test/ui/lint/lint-type-overflow2.stderr36
-rw-r--r--src/test/ui/print_type_sizes/generics.rs3
-rw-r--r--src/test/ui/print_type_sizes/niche-filling.rs3
-rw-r--r--src/test/ui/print_type_sizes/no_duplicates.rs3
-rw-r--r--src/test/ui/print_type_sizes/packed.rs3
-rw-r--r--src/test/ui/print_type_sizes/repr-align.rs3
-rw-r--r--src/test/ui/print_type_sizes/uninhabited.rs3
-rw-r--r--src/test/ui/proc-macro/auxiliary/generate-mod.rs1
-rw-r--r--src/test/ui/save-analysis/emit-notifications.rs3
-rw-r--r--src/tools/compiletest/src/common.rs33
-rw-r--r--src/tools/compiletest/src/header.rs31
-rw-r--r--src/tools/compiletest/src/main.rs16
-rw-r--r--src/tools/compiletest/src/runtest.rs36
20 files changed, 194 insertions, 88 deletions
diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs
index 46c58d118f7..cab7443bf3f 100644
--- a/src/bootstrap/builder/tests.rs
+++ b/src/bootstrap/builder/tests.rs
@@ -598,6 +598,7 @@ fn test_with_no_doc_stage0() {
         bless: false,
         compare_mode: None,
         rustfix_coverage: false,
+        pass: None,
     };
 
     let build = Build::new(config);
@@ -640,6 +641,7 @@ fn test_exclude() {
         bless: false,
         compare_mode: None,
         rustfix_coverage: false,
+        pass: None,
     };
 
     let build = Build::new(config);
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index 4774c0a51c0..179accda0c8 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -58,6 +58,7 @@ pub enum Subcommand {
         /// Whether to automatically update stderr/stdout files
         bless: bool,
         compare_mode: Option<String>,
+        pass: Option<String>,
         test_args: Vec<String>,
         rustc_args: Vec<String>,
         fail_fast: bool,
@@ -199,6 +200,12 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`"
                     "mode describing what file the actual ui output will be compared to",
                     "COMPARE MODE",
                 );
+                opts.optopt(
+                    "",
+                    "pass",
+                    "force {check,build,run}-pass tests to this mode.",
+                    "check | build | run"
+                );
                 opts.optflag(
                     "",
                     "rustfix-coverage",
@@ -401,6 +408,7 @@ Arguments:
                 paths,
                 bless: matches.opt_present("bless"),
                 compare_mode: matches.opt_str("compare-mode"),
+                pass: matches.opt_str("pass"),
                 test_args: matches.opt_strs("test-args"),
                 rustc_args: matches.opt_strs("rustc-args"),
                 fail_fast: !matches.opt_present("no-fail-fast"),
@@ -524,6 +532,15 @@ impl Subcommand {
             _ => None,
         }
     }
+
+    pub fn pass(&self) -> Option<&str> {
+        match *self {
+            Subcommand::Test {
+                ref pass, ..
+            } => pass.as_ref().map(|s| &s[..]),
+            _ => None,
+        }
+    }
 }
 
 fn split(s: &[String]) -> Vec<String> {
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 2f9bd067c31..1d54ca16a31 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1065,6 +1065,11 @@ impl Step for Compiletest {
             }
         });
 
+        if let Some(ref pass) = builder.config.cmd.pass() {
+            cmd.arg("--pass");
+            cmd.arg(pass);
+        }
+
         if let Some(ref nodejs) = builder.config.nodejs {
             cmd.arg("--nodejs").arg(nodejs);
         }
diff --git a/src/test/ui/consts/const-eval/promoted_errors.rs b/src/test/ui/consts/const-eval/promoted_errors.rs
index ebf80e7d2e6..fa8859cbb3b 100644
--- a/src/test/ui/consts/const-eval/promoted_errors.rs
+++ b/src/test/ui/consts/const-eval/promoted_errors.rs
@@ -1,19 +1,23 @@
-#![warn(const_err)]
-
-// compile-pass
 // compile-flags: -O
+
+#![deny(const_err)]
+
 fn main() {
     println!("{}", 0u32 - 1);
     let _x = 0u32 - 1;
-    //~^ WARN const_err
+    //~^ ERROR this expression will panic at runtime [const_err]
     println!("{}", 1/(1-1));
-    //~^ WARN const_err
+    //~^ ERROR this expression will panic at runtime [const_err]
+    //~| ERROR attempt to divide by zero [const_err]
+    //~| ERROR reaching this expression at runtime will panic or abort [const_err]
     let _x = 1/(1-1);
-    //~^ WARN const_err
-    //~| WARN const_err
+    //~^ ERROR const_err
+    //~| ERROR const_err
     println!("{}", 1/(false as u32));
-    //~^ WARN const_err
+    //~^ ERROR this expression will panic at runtime [const_err]
+    //~| ERROR attempt to divide by zero [const_err]
+    //~| ERROR reaching this expression at runtime will panic or abort [const_err]
     let _x = 1/(false as u32);
-    //~^ WARN const_err
-    //~| WARN const_err
+    //~^ ERROR const_err
+    //~| ERROR const_err
 }
diff --git a/src/test/ui/consts/const-eval/promoted_errors.stderr b/src/test/ui/consts/const-eval/promoted_errors.stderr
index c9f3a7659f9..12407accf09 100644
--- a/src/test/ui/consts/const-eval/promoted_errors.stderr
+++ b/src/test/ui/consts/const-eval/promoted_errors.stderr
@@ -1,72 +1,74 @@
-warning: this expression will panic at runtime
+error: this expression will panic at runtime
   --> $DIR/promoted_errors.rs:7:14
    |
 LL |     let _x = 0u32 - 1;
    |              ^^^^^^^^ attempt to subtract with overflow
    |
 note: lint level defined here
-  --> $DIR/promoted_errors.rs:1:9
+  --> $DIR/promoted_errors.rs:3:9
    |
-LL | #![warn(const_err)]
+LL | #![deny(const_err)]
    |         ^^^^^^^^^
 
-warning: attempt to divide by zero
+error: attempt to divide by zero
   --> $DIR/promoted_errors.rs:9:20
    |
 LL |     println!("{}", 1/(1-1));
    |                    ^^^^^^^
 
-warning: this expression will panic at runtime
+error: this expression will panic at runtime
   --> $DIR/promoted_errors.rs:9:20
    |
 LL |     println!("{}", 1/(1-1));
    |                    ^^^^^^^ attempt to divide by zero
 
-warning: attempt to divide by zero
-  --> $DIR/promoted_errors.rs:11:14
+error: attempt to divide by zero
+  --> $DIR/promoted_errors.rs:13:14
    |
 LL |     let _x = 1/(1-1);
    |              ^^^^^^^
 
-warning: this expression will panic at runtime
-  --> $DIR/promoted_errors.rs:11:14
+error: this expression will panic at runtime
+  --> $DIR/promoted_errors.rs:13:14
    |
 LL |     let _x = 1/(1-1);
    |              ^^^^^^^ attempt to divide by zero
 
-warning: attempt to divide by zero
-  --> $DIR/promoted_errors.rs:14:20
+error: attempt to divide by zero
+  --> $DIR/promoted_errors.rs:16:20
    |
 LL |     println!("{}", 1/(false as u32));
    |                    ^^^^^^^^^^^^^^^^
 
-warning: this expression will panic at runtime
-  --> $DIR/promoted_errors.rs:14:20
+error: this expression will panic at runtime
+  --> $DIR/promoted_errors.rs:16:20
    |
 LL |     println!("{}", 1/(false as u32));
    |                    ^^^^^^^^^^^^^^^^ attempt to divide by zero
 
-warning: attempt to divide by zero
-  --> $DIR/promoted_errors.rs:16:14
+error: attempt to divide by zero
+  --> $DIR/promoted_errors.rs:20:14
    |
 LL |     let _x = 1/(false as u32);
    |              ^^^^^^^^^^^^^^^^
 
-warning: this expression will panic at runtime
-  --> $DIR/promoted_errors.rs:16:14
+error: this expression will panic at runtime
+  --> $DIR/promoted_errors.rs:20:14
    |
 LL |     let _x = 1/(false as u32);
    |              ^^^^^^^^^^^^^^^^ attempt to divide by zero
 
-warning: reaching this expression at runtime will panic or abort
-  --> $DIR/promoted_errors.rs:14:20
+error: reaching this expression at runtime will panic or abort
+  --> $DIR/promoted_errors.rs:16:20
    |
 LL |     println!("{}", 1/(false as u32));
    |                    ^^^^^^^^^^^^^^^^ attempt to divide by zero
 
-warning: reaching this expression at runtime will panic or abort
+error: reaching this expression at runtime will panic or abort
   --> $DIR/promoted_errors.rs:9:20
    |
 LL |     println!("{}", 1/(1-1));
    |                    ^^^^^^^ attempt to divide by zero
 
+error: aborting due to 11 previous errors
+
diff --git a/src/test/ui/emit-artifact-notifications.rs b/src/test/ui/emit-artifact-notifications.rs
index c2c930c8b1b..2f17f95b7a3 100644
--- a/src/test/ui/emit-artifact-notifications.rs
+++ b/src/test/ui/emit-artifact-notifications.rs
@@ -1,5 +1,7 @@
 // compile-flags:--emit=metadata --error-format=json -Z emit-artifact-notifications
 // compile-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
 
 // A very basic test for the emission of artifact notifications in JSON output.
 
diff --git a/src/test/ui/lint/lint-type-overflow2.rs b/src/test/ui/lint/lint-type-overflow2.rs
index 507e8d07349..c1f874c079c 100644
--- a/src/test/ui/lint/lint-type-overflow2.rs
+++ b/src/test/ui/lint/lint-type-overflow2.rs
@@ -1,15 +1,13 @@
 // compile-flags: -O
-#![warn(overflowing_literals)]
-#![warn(const_err)]
-// compile-pass
 
-#[allow(unused_variables)]
+#![deny(overflowing_literals)]
+#![deny(const_err)]
 
 fn main() {
-    let x2: i8 = --128; //~ warn: literal out of range for i8
+    let x2: i8 = --128; //~ ERROR literal out of range for `i8`
 
-    let x = -3.40282357e+38_f32; //~ warn: literal out of range for f32
-    let x =  3.40282357e+38_f32; //~ warn: literal out of range for f32
-    let x = -1.7976931348623159e+308_f64; //~ warn: literal out of range for f64
-    let x =  1.7976931348623159e+308_f64; //~ warn: literal out of range for f64
+    let x = -3.40282357e+38_f32; //~ ERROR literal out of range for `f32`
+    let x =  3.40282357e+38_f32; //~ ERROR literal out of range for `f32`
+    let x = -1.7976931348623159e+308_f64; //~ ERROR literal out of range for `f64`
+    let x =  1.7976931348623159e+308_f64; //~ ERROR literal out of range for `f64`
 }
diff --git a/src/test/ui/lint/lint-type-overflow2.stderr b/src/test/ui/lint/lint-type-overflow2.stderr
index c76e9e25d5a..761b095464f 100644
--- a/src/test/ui/lint/lint-type-overflow2.stderr
+++ b/src/test/ui/lint/lint-type-overflow2.stderr
@@ -1,48 +1,38 @@
-warning: literal out of range for `i8`
-  --> $DIR/lint-type-overflow2.rs:9:20
+error: literal out of range for `i8`
+  --> $DIR/lint-type-overflow2.rs:7:20
    |
 LL |     let x2: i8 = --128;
    |                    ^^^
    |
 note: lint level defined here
-  --> $DIR/lint-type-overflow2.rs:2:9
+  --> $DIR/lint-type-overflow2.rs:3:9
    |
-LL | #![warn(overflowing_literals)]
+LL | #![deny(overflowing_literals)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
-warning: literal out of range for `f32`
-  --> $DIR/lint-type-overflow2.rs:11:14
+error: literal out of range for `f32`
+  --> $DIR/lint-type-overflow2.rs:9:14
    |
 LL |     let x = -3.40282357e+38_f32;
    |              ^^^^^^^^^^^^^^^^^^
 
-warning: literal out of range for `f32`
-  --> $DIR/lint-type-overflow2.rs:12:14
+error: literal out of range for `f32`
+  --> $DIR/lint-type-overflow2.rs:10:14
    |
 LL |     let x =  3.40282357e+38_f32;
    |              ^^^^^^^^^^^^^^^^^^
 
-warning: literal out of range for `f64`
-  --> $DIR/lint-type-overflow2.rs:13:14
+error: literal out of range for `f64`
+  --> $DIR/lint-type-overflow2.rs:11:14
    |
 LL |     let x = -1.7976931348623159e+308_f64;
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-warning: literal out of range for `f64`
-  --> $DIR/lint-type-overflow2.rs:14:14
+error: literal out of range for `f64`
+  --> $DIR/lint-type-overflow2.rs:12:14
    |
 LL |     let x =  1.7976931348623159e+308_f64;
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-warning: this expression will panic at runtime
-  --> $DIR/lint-type-overflow2.rs:9:18
-   |
-LL |     let x2: i8 = --128;
-   |                  ^^^^^ attempt to negate with overflow
-   |
-note: lint level defined here
-  --> $DIR/lint-type-overflow2.rs:3:9
-   |
-LL | #![warn(const_err)]
-   |         ^^^^^^^^^
+error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/print_type_sizes/generics.rs b/src/test/ui/print_type_sizes/generics.rs
index 360c9958686..ecfc03717db 100644
--- a/src/test/ui/print_type_sizes/generics.rs
+++ b/src/test/ui/print_type_sizes/generics.rs
@@ -1,5 +1,8 @@
 // compile-flags: -Z print-type-sizes
 // compile-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+//     FIXME: consider using an attribute instead of side-effects.
 
 // This file illustrates how generics are handled: types have to be
 // monomorphized, in the MIR of the original function in which they
diff --git a/src/test/ui/print_type_sizes/niche-filling.rs b/src/test/ui/print_type_sizes/niche-filling.rs
index 0127261b2b7..98b506b1f0d 100644
--- a/src/test/ui/print_type_sizes/niche-filling.rs
+++ b/src/test/ui/print_type_sizes/niche-filling.rs
@@ -1,5 +1,8 @@
 // compile-flags: -Z print-type-sizes
 // compile-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+//     FIXME: consider using an attribute instead of side-effects.
 
 // This file illustrates how niche-filling enums are handled,
 // modelled after cases like `Option<&u32>`, `Option<bool>` and such.
diff --git a/src/test/ui/print_type_sizes/no_duplicates.rs b/src/test/ui/print_type_sizes/no_duplicates.rs
index 7307c0fd8b4..f1b8a28ae30 100644
--- a/src/test/ui/print_type_sizes/no_duplicates.rs
+++ b/src/test/ui/print_type_sizes/no_duplicates.rs
@@ -1,5 +1,8 @@
 // compile-flags: -Z print-type-sizes
 // compile-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+//     FIXME: consider using an attribute instead of side-effects.
 
 // This file illustrates that when the same type occurs repeatedly
 // (even if multiple functions), it is only printed once in the
diff --git a/src/test/ui/print_type_sizes/packed.rs b/src/test/ui/print_type_sizes/packed.rs
index ec3efd6923a..a8d409a91a2 100644
--- a/src/test/ui/print_type_sizes/packed.rs
+++ b/src/test/ui/print_type_sizes/packed.rs
@@ -1,5 +1,8 @@
 // compile-flags: -Z print-type-sizes
 // compile-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+//     FIXME: consider using an attribute instead of side-effects.
 
 // This file illustrates how packing is handled; it should cause
 // the elimination of padding that would normally be introduced
diff --git a/src/test/ui/print_type_sizes/repr-align.rs b/src/test/ui/print_type_sizes/repr-align.rs
index fd452f411c5..3b5248b6f7e 100644
--- a/src/test/ui/print_type_sizes/repr-align.rs
+++ b/src/test/ui/print_type_sizes/repr-align.rs
@@ -1,5 +1,8 @@
 // compile-flags: -Z print-type-sizes
 // compile-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+//     FIXME: consider using an attribute instead of side-effects.
 
 // This file illustrates how padding is handled: alignment
 // requirements can lead to the introduction of padding, either before
diff --git a/src/test/ui/print_type_sizes/uninhabited.rs b/src/test/ui/print_type_sizes/uninhabited.rs
index 14245d0dc9a..c33965c4f53 100644
--- a/src/test/ui/print_type_sizes/uninhabited.rs
+++ b/src/test/ui/print_type_sizes/uninhabited.rs
@@ -1,5 +1,8 @@
 // compile-flags: -Z print-type-sizes
 // compile-pass
+// ignore-pass
+// ^-- needed because `--pass check` does not emit the output needed.
+//     FIXME: consider using an attribute instead of side-effects.
 
 #![feature(never_type)]
 #![feature(start)]
diff --git a/src/test/ui/proc-macro/auxiliary/generate-mod.rs b/src/test/ui/proc-macro/auxiliary/generate-mod.rs
index 8b41e8b3b3e..e950f7d62d6 100644
--- a/src/test/ui/proc-macro/auxiliary/generate-mod.rs
+++ b/src/test/ui/proc-macro/auxiliary/generate-mod.rs
@@ -1,6 +1,7 @@
 // run-pass
 // force-host
 // no-prefer-dynamic
+// ignore-pass
 
 #![crate_type = "proc-macro"]
 
diff --git a/src/test/ui/save-analysis/emit-notifications.rs b/src/test/ui/save-analysis/emit-notifications.rs
index 411acbb14db..ebc27174998 100644
--- a/src/test/ui/save-analysis/emit-notifications.rs
+++ b/src/test/ui/save-analysis/emit-notifications.rs
@@ -1,4 +1,7 @@
 // compile-pass
 // compile-flags: -Zsave-analysis -Zemit-artifact-notifications
 // compile-flags: --crate-type rlib --error-format=json
+// ignore-pass
+// ^-- needed because otherwise, the .stderr file changes with --pass check
+
 pub fn foo() {}
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 722979c3c14..a75d9f0b0bb 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -99,6 +99,36 @@ impl fmt::Display for Mode {
     }
 }
 
+#[derive(Clone, Copy, PartialEq, Debug, Hash)]
+pub enum PassMode {
+    Check,
+    Build,
+    Run,
+}
+
+impl FromStr for PassMode {
+    type Err = ();
+    fn from_str(s: &str) -> Result<Self, ()> {
+        match s {
+            "check" => Ok(PassMode::Check),
+            "build" => Ok(PassMode::Build),
+            "run" => Ok(PassMode::Run),
+            _ => Err(()),
+        }
+    }
+}
+
+impl fmt::Display for PassMode {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let s = match *self {
+            PassMode::Check => "check",
+            PassMode::Build => "build",
+            PassMode::Run => "run",
+        };
+        fmt::Display::fmt(s, f)
+    }
+}
+
 #[derive(Clone, Debug, PartialEq)]
 pub enum CompareMode {
     Nll,
@@ -184,6 +214,9 @@ pub struct Config {
     /// Exactly match the filter, rather than a substring
     pub filter_exact: bool,
 
+    /// Force the pass mode of a check/build/run-pass test to this mode.
+    pub force_pass_mode: Option<PassMode>,
+
     /// Write out a parseable log of tests that were run
     pub logfile: Option<PathBuf>,
 
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 6ce7461f759..52f777db2da 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
 
 use log::*;
 
-use crate::common::{self, CompareMode, Config, Mode};
+use crate::common::{self, CompareMode, Config, Mode, PassMode};
 use crate::util;
 
 use crate::extract_gdb_version;
@@ -290,13 +290,6 @@ impl EarlyProps {
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Debug)]
-pub enum PassMode {
-    Check,
-    Build,
-    Run,
-}
-
 #[derive(Clone, Debug)]
 pub struct TestProps {
     // Lines that should be expected, in order, on standard out
@@ -357,7 +350,9 @@ pub struct TestProps {
     // arguments. (In particular, it propagates to the aux-builds.)
     pub incremental_dir: Option<PathBuf>,
     // How far should the test proceed while still passing.
-    pub pass_mode: Option<PassMode>,
+    pass_mode: Option<PassMode>,
+    // Ignore `--pass` overrides from the command line for this test.
+    ignore_pass: bool,
     // rustdoc will test the output of the `--test` option
     pub check_test_line_numbers_match: bool,
     // Do not pass `-Z ui-testing` to UI tests
@@ -400,6 +395,7 @@ impl TestProps {
             forbid_output: vec![],
             incremental_dir: None,
             pass_mode: None,
+            ignore_pass: false,
             check_test_line_numbers_match: false,
             disable_ui_testing_normalization: false,
             normalize_stdout: vec![],
@@ -528,6 +524,10 @@ impl TestProps {
 
             self.update_pass_mode(ln, cfg, config);
 
+            if !self.ignore_pass {
+                self.ignore_pass = config.parse_ignore_pass(ln);
+            }
+
             if !self.disable_ui_testing_normalization {
                 self.disable_ui_testing_normalization =
                     config.parse_disable_ui_testing_normalization(ln);
@@ -608,6 +608,15 @@ impl TestProps {
             (_, None) => {}
         }
     }
+
+    pub fn pass_mode(&self, config: &Config) -> Option<PassMode> {
+        if !self.ignore_pass {
+            if let (mode @ Some(_), Some(_)) = (config.force_pass_mode, self.pass_mode) {
+                return mode;
+            }
+        }
+        self.pass_mode
+    }
 }
 
 fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut dyn FnMut(&str)) {
@@ -743,6 +752,10 @@ impl Config {
         self.parse_name_directive(line, "check-test-line-numbers-match")
     }
 
+    fn parse_ignore_pass(&self, line: &str) -> bool {
+        self.parse_name_directive(line, "ignore-pass")
+    }
+
     fn parse_assembly_output(&self, line: &str) -> Option<String> {
         self.parse_name_value_directive(line, "assembly-output")
             .map(|r| r.trim().to_string())
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index d0dc9d11d39..597fdf2d95e 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -5,7 +5,7 @@
 
 extern crate test;
 
-use crate::common::CompareMode;
+use crate::common::{CompareMode, PassMode};
 use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
 use crate::common::{Config, TestPaths};
 use crate::common::{DebugInfoCdb, DebugInfoGdbLldb, DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
@@ -128,6 +128,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "(compile-fail|run-fail|run-pass|\
              run-pass-valgrind|pretty|debug-info|incremental|mir-opt)",
         )
+        .optopt(
+            "",
+            "pass",
+            "force {check,build,run}-pass tests to this mode.",
+            "check | build | run"
+        )
         .optflag("", "ignored", "run tests marked as ignored")
         .optflag("", "exact", "filters match exactly")
         .optopt(
@@ -320,6 +326,10 @@ pub fn parse_config(args: Vec<String>) -> Config {
         run_ignored,
         filter: matches.free.first().cloned(),
         filter_exact: matches.opt_present("exact"),
+        force_pass_mode: matches.opt_str("pass").map(|mode|
+            mode.parse::<PassMode>()
+                .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
+        ),
         logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
         runtool: matches.opt_str("runtool"),
         host_rustcflags: matches.opt_str("host-rustcflags"),
@@ -382,6 +392,10 @@ pub fn log_config(config: &Config) {
         ),
     );
     logv(c, format!("filter_exact: {}", config.filter_exact));
+    logv(c, format!(
+        "force_pass_mode: {}",
+        opt_str(&config.force_pass_mode.map(|m| format!("{}", m))),
+    ));
     logv(c, format!("runtool: {}", opt_str(&config.runtool)));
     logv(
         c,
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 8b52a529d44..35caf82dd71 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1,6 +1,6 @@
 // ignore-tidy-filelength
 
-use crate::common::CompareMode;
+use crate::common::{CompareMode, PassMode};
 use crate::common::{expected_output_path, UI_EXTENSIONS, UI_FIXED, UI_STDERR, UI_STDOUT};
 use crate::common::{output_base_dir, output_base_name, output_testname_unique};
 use crate::common::{Codegen, CodegenUnits, Rustdoc};
@@ -10,7 +10,7 @@ use crate::common::{Config, TestPaths};
 use crate::common::{Incremental, MirOpt, RunMake, Ui, JsDocTest, Assembly};
 use diff;
 use crate::errors::{self, Error, ErrorKind};
-use crate::header::{TestProps, PassMode};
+use crate::header::TestProps;
 use crate::json;
 use regex::{Captures, Regex};
 use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
@@ -260,6 +260,10 @@ pub fn compute_stamp_hash(config: &Config) -> String {
         env::var_os("PYTHONPATH").hash(&mut hash);
     }
 
+    if let Ui | RunPass | Incremental | Pretty = config.mode {
+        config.force_pass_mode.hash(&mut hash);
+    }
+
     format!("{:x}", hash.finish())
 }
 
@@ -309,10 +313,13 @@ impl<'test> TestCx<'test> {
         }
     }
 
+    fn pass_mode(&self) -> Option<PassMode> {
+        self.props.pass_mode(self.config)
+    }
+
     fn should_run_successfully(&self) -> bool {
         match self.config.mode {
-            RunPass => true,
-            Ui => self.props.pass_mode == Some(PassMode::Run),
+            RunPass | Ui => self.pass_mode() == Some(PassMode::Run),
             mode => panic!("unimplemented for mode {:?}", mode),
         }
     }
@@ -322,7 +329,7 @@ impl<'test> TestCx<'test> {
             CompileFail => false,
             RunPass => true,
             JsDocTest => true,
-            Ui => self.props.pass_mode.is_some(),
+            Ui => self.pass_mode().is_some(),
             Incremental => {
                 let revision = self.revision
                     .expect("incremental tests require a list of revisions");
@@ -330,7 +337,7 @@ impl<'test> TestCx<'test> {
                     true
                 } else if revision.starts_with("cfail") {
                     // FIXME: would be nice if incremental revs could start with "cpass"
-                    self.props.pass_mode.is_some()
+                    self.pass_mode().is_some()
                 } else {
                     panic!("revision name must begin with rpass, rfail, or cfail");
                 }
@@ -1341,7 +1348,7 @@ impl<'test> TestCx<'test> {
     fn check_error_patterns(&self, output_to_check: &str, proc_res: &ProcRes) {
         debug!("check_error_patterns");
         if self.props.error_patterns.is_empty() {
-            if self.props.pass_mode.is_some() {
+            if self.pass_mode().is_some() {
                 return;
             } else {
                 self.fatal(&format!(
@@ -1871,7 +1878,11 @@ impl<'test> TestCx<'test> {
         result
     }
 
-    fn make_compile_args(&self, input_file: &Path, output_file: TargetLocation) -> Command {
+    fn make_compile_args(
+        &self,
+        input_file: &Path,
+        output_file: TargetLocation,
+    ) -> Command {
         let is_rustdoc = self.config.src_base.ends_with("rustdoc-ui") ||
                          self.config.src_base.ends_with("rustdoc-js");
         let mut rustc = if !is_rustdoc {
@@ -1968,14 +1979,7 @@ impl<'test> TestCx<'test> {
             }
         }
 
-        if self.props.pass_mode == Some(PassMode::Check) {
-            assert!(
-                !self
-                    .props
-                    .compile_flags
-                    .iter()
-                    .any(|s| s.starts_with("--emit"))
-            );
+        if let Some(PassMode::Check) = self.pass_mode() {
             rustc.args(&["--emit", "metadata"]);
         }