summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author许杰友 Jieyou Xu (Joe) <jieyouxu@outlook.com>2024-02-10 14:33:31 +0000
committer许杰友 Jieyou Xu (Joe) <jieyouxu@outlook.com>2024-02-16 19:40:23 +0000
commite53d6dd35bb38b81dff4b00497f4c152e9009499 (patch)
treef97c2cd49d3d3b54b330c1e955c8cc7dba14f7ce
parentd2e8ecd8bd26a22111cdebfb813258450b07fbf0 (diff)
Implement infra support for migrating from `//` to `//@` ui test directives
-rw-r--r--src/tools/compiletest/src/header.rs710
-rw-r--r--src/tools/compiletest/src/header/tests.rs147
-rw-r--r--src/tools/tidy/src/style.rs13
-rw-r--r--src/tools/tidy/src/ui_tests.rs3
-rw-r--r--tests/ui/README.md35
-rw-r--r--tests/ui/symbol-names/x86-stdcall.rs1
6 files changed, 607 insertions, 302 deletions
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index daec3914145..4ceb8a646e0 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -6,6 +6,7 @@ use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::process::Command;
+use regex::Regex;
use tracing::*;
use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
@@ -46,18 +47,32 @@ impl EarlyProps {
pub fn from_reader<R: Read>(config: &Config, testfile: &Path, rdr: R) -> Self {
let mut props = EarlyProps::default();
- iter_header(testfile, rdr, &mut |_, ln, _| {
- config.push_name_value_directive(ln, directives::AUX_BUILD, &mut props.aux, |r| {
- r.trim().to_string()
- });
- config.push_name_value_directive(
- ln,
- directives::AUX_CRATE,
- &mut props.aux_crate,
- Config::parse_aux_crate,
- );
- config.parse_and_update_revisions(ln, &mut props.revisions);
- });
+ let mut poisoned = false;
+ iter_header(
+ config.mode,
+ &config.suite,
+ &mut poisoned,
+ testfile,
+ rdr,
+ &mut |_, _, ln, _| {
+ config.push_name_value_directive(ln, directives::AUX_BUILD, &mut props.aux, |r| {
+ r.trim().to_string()
+ });
+ config.push_name_value_directive(
+ ln,
+ directives::AUX_CRATE,
+ &mut props.aux_crate,
+ Config::parse_aux_crate,
+ );
+ config.parse_and_update_revisions(ln, &mut props.revisions);
+ },
+ );
+
+ if poisoned {
+ eprintln!("errors encountered during EarlyProps parsing: {}", testfile.display());
+ panic!("errors encountered during EarlyProps parsing");
+ }
+
return props;
}
}
@@ -306,205 +321,233 @@ impl TestProps {
if !testfile.is_dir() {
let file = File::open(testfile).unwrap();
- iter_header(testfile, file, &mut |revision, ln, _| {
- if revision.is_some() && revision != cfg {
- return;
- }
+ let mut poisoned = false;
- use directives::*;
+ iter_header(
+ config.mode,
+ &config.suite,
+ &mut poisoned,
+ testfile,
+ file,
+ &mut |revision, _, ln, _| {
+ if revision.is_some() && revision != cfg {
+ return;
+ }
- config.push_name_value_directive(
- ln,
- ERROR_PATTERN,
- &mut self.error_patterns,
- |r| r,
- );
- config.push_name_value_directive(
- ln,
- REGEX_ERROR_PATTERN,
- &mut self.regex_error_patterns,
- |r| r,
- );
+ use directives::*;
+
+ config.push_name_value_directive(
+ ln,
+ ERROR_PATTERN,
+ &mut self.error_patterns,
+ |r| r,
+ );
+ config.push_name_value_directive(
+ ln,
+ REGEX_ERROR_PATTERN,
+ &mut self.regex_error_patterns,
+ |r| r,
+ );
- fn split_flags(flags: &str) -> Vec<String> {
- // Individual flags can be single-quoted to preserve spaces; see
- // <https://github.com/rust-lang/rust/pull/115948/commits/957c5db6>.
- flags
- .split("'")
- .enumerate()
- .flat_map(
- |(i, f)| {
+ fn split_flags(flags: &str) -> Vec<String> {
+ // Individual flags can be single-quoted to preserve spaces; see
+ // <https://github.com/rust-lang/rust/pull/115948/commits/957c5db6>.
+ flags
+ .split("'")
+ .enumerate()
+ .flat_map(|(i, f)| {
if i % 2 == 1 { vec![f] } else { f.split_whitespace().collect() }
- },
- )
- .map(move |s| s.to_owned())
- .collect::<Vec<_>>()
- }
+ })
+ .map(move |s| s.to_owned())
+ .collect::<Vec<_>>()
+ }
- if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) {
- self.compile_flags.extend(split_flags(&flags));
- }
- if config.parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS).is_some() {
- panic!("`compiler-flags` directive should be spelled `compile-flags`");
- }
+ if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) {
+ self.compile_flags.extend(split_flags(&flags));
+ }
+ if config.parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS).is_some() {
+ panic!("`compiler-flags` directive should be spelled `compile-flags`");
+ }
- if let Some(edition) = config.parse_edition(ln) {
- self.compile_flags.push(format!("--edition={}", edition.trim()));
- has_edition = true;
- }
+ if let Some(edition) = config.parse_edition(ln) {
+ self.compile_flags.push(format!("--edition={}", edition.trim()));
+ has_edition = true;
+ }
- config.parse_and_update_revisions(ln, &mut self.revisions);
+ config.parse_and_update_revisions(ln, &mut self.revisions);
- config.set_name_value_directive(ln, RUN_FLAGS, &mut self.run_flags, |r| r);
+ config.set_name_value_directive(ln, RUN_FLAGS, &mut self.run_flags, |r| r);
- if self.pp_exact.is_none() {
- self.pp_exact = config.parse_pp_exact(ln, testfile);
- }
+ if self.pp_exact.is_none() {
+ self.pp_exact = config.parse_pp_exact(ln, testfile);
+ }
- config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice);
- config.set_name_directive(ln, BUILD_AUX_DOCS, &mut self.build_aux_docs);
- config.set_name_directive(ln, FORCE_HOST, &mut self.force_host);
- config.set_name_directive(ln, CHECK_STDOUT, &mut self.check_stdout);
- config.set_name_directive(ln, CHECK_RUN_RESULTS, &mut self.check_run_results);
- config.set_name_directive(
- ln,
- DONT_CHECK_COMPILER_STDOUT,
- &mut self.dont_check_compiler_stdout,
- );
- config.set_name_directive(
- ln,
- DONT_CHECK_COMPILER_STDERR,
- &mut self.dont_check_compiler_stderr,
- );
- config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic);
- config.set_name_directive(ln, PRETTY_EXPANDED, &mut self.pretty_expanded);
+ config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice);
+ config.set_name_directive(ln, BUILD_AUX_DOCS, &mut self.build_aux_docs);
+ config.set_name_directive(ln, FORCE_HOST, &mut self.force_host);
+ config.set_name_directive(ln, CHECK_STDOUT, &mut self.check_stdout);
+ config.set_name_directive(ln, CHECK_RUN_RESULTS, &mut self.check_run_results);
+ config.set_name_directive(
+ ln,
+ DONT_CHECK_COMPILER_STDOUT,
+ &mut self.dont_check_compiler_stdout,
+ );
+ config.set_name_directive(
+ ln,
+ DONT_CHECK_COMPILER_STDERR,
+ &mut self.dont_check_compiler_stderr,
+ );
+ config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic);
+ config.set_name_directive(ln, PRETTY_EXPANDED, &mut self.pretty_expanded);
- if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE) {
- self.pretty_mode = m;
- }
+ if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE) {
+ self.pretty_mode = m;
+ }
- config.set_name_directive(ln, PRETTY_COMPARE_ONLY, &mut self.pretty_compare_only);
- config.push_name_value_directive(ln, AUX_BUILD, &mut self.aux_builds, |r| {
- r.trim().to_string()
- });
- config.push_name_value_directive(
- ln,
- AUX_CRATE,
- &mut self.aux_crates,
- Config::parse_aux_crate,
- );
- config.push_name_value_directive(
- ln,
- EXEC_ENV,
- &mut self.exec_env,
- Config::parse_env,
- );
- config.push_name_value_directive(
- ln,
- UNSET_EXEC_ENV,
- &mut self.unset_exec_env,
- |r| r,
- );
- config.push_name_value_directive(
- ln,
- RUSTC_ENV,
- &mut self.rustc_env,
- Config::parse_env,
- );
- config.push_name_value_directive(
- ln,
- UNSET_RUSTC_ENV,
- &mut self.unset_rustc_env,
- |r| r,
- );
- config.push_name_value_directive(ln, FORBID_OUTPUT, &mut self.forbid_output, |r| r);
- config.set_name_directive(
- ln,
- CHECK_TEST_LINE_NUMBERS_MATCH,
- &mut self.check_test_line_numbers_match,
- );
+ config.set_name_directive(
+ ln,
+ PRETTY_COMPARE_ONLY,
+ &mut self.pretty_compare_only,
+ );
+ config.push_name_value_directive(ln, AUX_BUILD, &mut self.aux_builds, |r| {
+ r.trim().to_string()
+ });
+ config.push_name_value_directive(
+ ln,
+ AUX_CRATE,
+ &mut self.aux_crates,
+ Config::parse_aux_crate,
+ );
+ config.push_name_value_directive(
+ ln,
+ EXEC_ENV,
+ &mut self.exec_env,
+ Config::parse_env,
+ );
+ config.push_name_value_directive(
+ ln,
+ UNSET_EXEC_ENV,
+ &mut self.unset_exec_env,
+ |r| r,
+ );
+ config.push_name_value_directive(
+ ln,
+ RUSTC_ENV,
+ &mut self.rustc_env,
+ Config::parse_env,
+ );
+ config.push_name_value_directive(
+ ln,
+ UNSET_RUSTC_ENV,
+ &mut self.unset_rustc_env,
+ |r| r,
+ );
+ config.push_name_value_directive(
+ ln,
+ FORBID_OUTPUT,
+ &mut self.forbid_output,
+ |r| r,
+ );
+ config.set_name_directive(
+ ln,
+ CHECK_TEST_LINE_NUMBERS_MATCH,
+ &mut self.check_test_line_numbers_match,
+ );
- self.update_pass_mode(ln, cfg, config);
- self.update_fail_mode(ln, config);
+ self.update_pass_mode(ln, cfg, config);
+ self.update_fail_mode(ln, config);
- config.set_name_directive(ln, IGNORE_PASS, &mut self.ignore_pass);
+ config.set_name_directive(ln, IGNORE_PASS, &mut self.ignore_pass);
- if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stdout") {
- self.normalize_stdout.push(rule);
- }
- if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stderr") {
- self.normalize_stderr.push(rule);
- }
+ if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stdout") {
+ self.normalize_stdout.push(rule);
+ }
+ if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stderr") {
+ self.normalize_stderr.push(rule);
+ }
- if let Some(code) = config
- .parse_name_value_directive(ln, FAILURE_STATUS)
- .and_then(|code| code.trim().parse::<i32>().ok())
- {
- self.failure_status = Some(code);
- }
+ if let Some(code) = config
+ .parse_name_value_directive(ln, FAILURE_STATUS)
+ .and_then(|code| code.trim().parse::<i32>().ok())
+ {
+ self.failure_status = Some(code);
+ }
- config.set_name_directive(
- ln,
- DONT_CHECK_FAILURE_STATUS,
- &mut self.dont_check_failure_status,
- );
+ config.set_name_directive(
+ ln,
+ DONT_CHECK_FAILURE_STATUS,
+ &mut self.dont_check_failure_status,
+ );
- config.set_name_directive(ln, RUN_RUSTFIX, &mut self.run_rustfix);
- config.set_name_directive(
- ln,
- RUSTFIX_ONLY_MACHINE_APPLICABLE,
- &mut self.rustfix_only_machine_applicable,
- );
- config.set_name_value_directive(
- ln,
- ASSEMBLY_OUTPUT,
- &mut self.assembly_output,
- |r| r.trim().to_string(),
- );
- config.set_name_directive(ln, STDERR_PER_BITWIDTH, &mut self.stderr_per_bitwidth);
- config.set_name_directive(ln, INCREMENTAL, &mut self.incremental);
-
- // Unlike the other `name_value_directive`s this needs to be handled manually,
- // because it sets a `bool` flag.
- if let Some(known_bug) = config.parse_name_value_directive(ln, KNOWN_BUG) {
- let known_bug = known_bug.trim();
- if known_bug == "unknown"
- || known_bug.split(',').all(|issue_ref| {
- issue_ref
- .trim()
- .split_once('#')
- .filter(|(_, number)| {
- number.chars().all(|digit| digit.is_numeric())
- })
- .is_some()
- })
- {
- self.known_bug = true;
- } else {
+ config.set_name_directive(ln, RUN_RUSTFIX, &mut self.run_rustfix);
+ config.set_name_directive(
+ ln,
+ RUSTFIX_ONLY_MACHINE_APPLICABLE,
+ &mut self.rustfix_only_machine_applicable,
+ );
+ config.set_name_value_directive(
+ ln,
+ ASSEMBLY_OUTPUT,
+ &mut self.assembly_output,
+ |r| r.trim().to_string(),
+ );
+ config.set_name_directive(
+ ln,
+ STDERR_PER_BITWIDTH,
+ &mut self.stderr_per_bitwidth,
+ );
+ config.set_name_directive(ln, INCREMENTAL, &mut self.incremental);
+
+ // Unlike the other `name_value_directive`s this needs to be handled manually,
+ // because it sets a `bool` flag.
+ if let Some(known_bug) = config.parse_name_value_directive(ln, KNOWN_BUG) {
+ let known_bug = known_bug.trim();
+ if known_bug == "unknown"
+ || known_bug.split(',').all(|issue_ref| {
+ issue_ref
+ .trim()
+ .split_once('#')
+ .filter(|(_, number)| {
+ number.chars().all(|digit| digit.is_numeric())
+ })
+ .is_some()
+ })
+ {
+ self.known_bug = true;
+ } else {
+ panic!(
+ "Invalid known-bug value: {known_bug}\nIt requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
+ );
+ }
+ } else if config.parse_name_directive(ln, KNOWN_BUG) {
panic!(
- "Invalid known-bug value: {known_bug}\nIt requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
+ "Invalid known-bug attribute, requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
);
}
- } else if config.parse_name_directive(ln, KNOWN_BUG) {
- panic!(
- "Invalid known-bug attribute, requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
+
+ config.set_name_value_directive(
+ ln,
+ MIR_UNIT_TEST,
+ &mut self.mir_unit_test,
+ |s| s.trim().to_string(),
+ );
+ config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base);
+ config.set_name_directive(
+ ln,
+ COMPARE_OUTPUT_LINES_BY_SUBSET,
+ &mut self.compare_output_lines_by_subset,
);
- }
- config.set_name_value_directive(ln, MIR_UNIT_TEST, &mut self.mir_unit_test, |s| {
- s.trim().to_string()
- });
- config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base);
- config.set_name_directive(
- ln,
- COMPARE_OUTPUT_LINES_BY_SUBSET,
- &mut self.compare_output_lines_by_subset,
- );
+ if let Some(flags) = config.parse_name_value_directive(ln, LLVM_COV_FLAGS) {
+ self.llvm_cov_flags.extend(split_flags(&flags));
+ }
+ },
+ );
- if let Some(flags) = config.parse_name_value_directive(ln, LLVM_COV_FLAGS) {
- self.llvm_cov_flags.extend(split_flags(&flags));
- }
- });
+ if poisoned {
+ eprintln!("errors encountered during TestProps parsing: {}", testfile.display());
+ panic!("errors encountered during TestProps parsing");
+ }
}
if self.should_ice {
@@ -628,15 +671,143 @@ pub fn line_directive<'line>(
}
}
-fn iter_header<R: Read>(testfile: &Path, rdr: R, it: &mut dyn FnMut(Option<&str>, &str, usize)) {
- iter_header_extra(testfile, rdr, &[], it)
+fn iter_header<R: Read>(
+ mode: Mode,
+ suite: &str,
+ poisoned: &mut bool,
+ testfile: &Path,
+ rdr: R,
+ it: &mut dyn FnMut(Option<&str>, &str, &str, usize),
+) {
+ iter_header_extra(mode, suite, poisoned, testfile, rdr, &[], it)
}
+/// This is generated by collecting directives from ui tests and then extracting their directive
+/// names. This is **not** an exhaustive list of all possible directives. Instead, this is a
+/// best-effort approximation for diagnostics.
+const DIAGNOSTICS_DIRECTIVE_NAMES: &[&str] = &[
+ "aux-build",
+ "aux-crate",
+ "build-fail",
+ "build-pass",
+ "check-fail",
+ "check-pass",
+ "check-run-results",
+ "check-stdout",
+ "compile-flags",
+ "dont-check-compiler-stderr",
+ "dont-check-compiler-stdout",
+ "dont-check-failure-status",
+ "edition",
+ "error-pattern",
+ "exec-env",
+ "failure-status",
+ "forbid-output",
+ "force-host",
+ "ignore-32bit",
+ "ignore-64bit",
+ "ignore-aarch64",
+ "ignore-aarch64-unknown-linux-gnu",
+ "ignore-android",
+ "ignore-arm",
+ "ignore-compare-mode-next-solver",
+ "ignore-compare-mode-polonius",
+ "ignore-cross-compile",
+ "ignore-debug",
+ "ignore-emscripten",
+ "ignore-endian-big",
+ "ignore-freebsd",
+ "ignore-fuchsia",
+ "ignore-gnu",
+ "ignore-haiku",
+ "ignore-horizon",
+ "ignore-i686-pc-windows-msvc",
+ "ignore-ios",
+ "ignore-llvm-version",
+ "ignore-macos",
+ "ignore-msvc",
+ "ignore-musl",
+ "ignore-netbsd",
+ "ignore-nightly",
+ "ignore-nto",
+ "ignore-nvptx64",
+ "ignore-openbsd",
+ "ignore-pass",
+ "ignore-sgx",
+ "ignore-spirv",
+ "ignore-test",
+ "ignore-thumbv8m.base-none-eabi",
+ "ignore-thumbv8m.main-none-eabi",
+ "ignore-uwp",
+ "ignore-vxworks",
+ "ignore-wasm",
+ "ignore-wasm32",
+ "ignore-wasm32-bare",
+ "ignore-windows",
+ "ignore-x86",
+ "incremental",
+ "known-bug",
+ "min-llvm-version",
+ "needs-asm-support",
+ "needs-dlltool",
+ "needs-dynamic-linking",
+ "needs-llvm-components",
+ "needs-profiler-support",
+ "needs-relocation-model-pic",
+ "needs-run-enabled",
+ "needs-sanitizer-address",
+ "needs-sanitizer-cfi",
+ "needs-sanitizer-hwaddress",
+ "needs-sanitizer-leak",
+ "needs-sanitizer-memory",
+ "needs-sanitizer-support",
+ "needs-sanitizer-thread",
+ "needs-unwind",
+ "needs-xray",
+ "no-prefer-dynamic",
+ "normalize-stderr-32bit",
+ "normalize-stderr-64bit",
+ "normalize-stderr-test",
+ "normalize-stdout-test",
+ "only-32bit",
+ "only-64bit",
+ "only-aarch64",
+ "only-gnu",
+ "only-i686-pc-windows-msvc",
+ "only-linux",
+ "only-macos",
+ "only-msvc",
+ "only-nightly",
+ "only-wasm32",
+ "only-windows",
+ "only-x86",
+ "only-x86_64",
+ "only-x86_64-pc-windows-msvc",
+ "only-x86_64-unknown-linux-gnu",
+ "pp-exact",
+ "pretty-expanded",
+ "regex-error-pattern",
+ "remap-src-base",
+ "revisions",
+ "run-fail",
+ "run-flags",
+ "run-pass",
+ "run-rustfix",
+ "rustc-env",
+ "rustfix-only-machine-applicable",
+ "should-fail",
+ "stderr-per-bitwidth",
+ "unset-rustc-env",
+];
+
fn iter_header_extra(
+ mode: Mode,
+ suite: &str,
+ poisoned: &mut bool,
testfile: &Path,
rdr: impl Read,
extra_directives: &[&str],
- it: &mut dyn FnMut(Option<&str>, &str, usize),
+ it: &mut dyn FnMut(Option<&str>, &str, &str, usize),
) {
if testfile.is_dir() {
return;
@@ -645,15 +816,21 @@ fn iter_header_extra(
// Process any extra directives supplied by the caller (e.g. because they
// are implied by the test mode), with a dummy line number of 0.
for directive in extra_directives {
- it(None, directive, 0);
+ it(None, directive, directive, 0);
}
- let comment = if testfile.extension().is_some_and(|e| e == "rs") { "//" } else { "#" };
+ let comment = if testfile.extension().is_some_and(|e| e == "rs") {
+ if mode == Mode::Ui && suite == "ui" { "//@" } else { "//" }
+ } else {
+ "#"
+ };
let mut rdr = BufReader::with_capacity(1024, rdr);
let mut ln = String::new();
let mut line_number = 0;
+ let revision_magic_comment = Regex::new("//(\\[.*\\])?~.*").unwrap();
+
loop {
line_number += 1;
ln.clear();
@@ -664,11 +841,56 @@ fn iter_header_extra(
// Assume that any directives will be found before the first
// module or function. This doesn't seem to be an optimization
// with a warm page cache. Maybe with a cold one.
+ let orig_ln = &ln;
let ln = ln.trim();
if ln.starts_with("fn") || ln.starts_with("mod") {
return;
+
+ // First try to accept `ui_test` style comments
} else if let Some((lncfg, ln)) = line_directive(comment, ln) {
- it(lncfg, ln, line_number);
+ it(lncfg, orig_ln, ln, line_number);
+ } else if mode == Mode::Ui && suite == "ui" && !revision_magic_comment.is_match(ln) {
+ let Some((_, rest)) = line_directive("//", ln) else {
+ continue;
+ };
+
+ if rest.trim_start().starts_with(':') {
+ // This is likely a markdown link:
+ // `[link_name]: https://example.org`
+ continue;
+ }
+
+ let rest = rest.trim_start();
+
+ for candidate in DIAGNOSTICS_DIRECTIVE_NAMES.iter() {
+ if rest.starts_with(candidate) {
+ let Some(prefix_removed) = rest.strip_prefix(candidate) else {
+ // We have a comment that's *successfully* parsed as an legacy-style
+ // directive. We emit an error here to warn the user.
+ *poisoned = true;
+ eprintln!(
+ "error: detected legacy-style directives in ui test: {}:{}, please use `ui_test`-style directives `//@` instead:{:#?}",
+ testfile.display(),
+ line_number,
+ line_directive("//", ln),
+ );
+ return;
+ };
+
+ if prefix_removed.starts_with([' ', ':']) {
+ // We have a comment that's *successfully* parsed as an legacy-style
+ // directive. We emit an error here to warn the user.
+ *poisoned = true;
+ eprintln!(
+ "error: detected legacy-style directives in ui test: {}:{}, please use `ui_test`-style directives `//@` instead:{:#?}",
+ testfile.display(),
+ line_number,
+ line_directive("//", ln),
+ );
+ return;
+ }
+ }
+ }
}
}
}
@@ -946,49 +1168,77 @@ pub fn make_test_description<R: Read>(
_ => &[],
};
- iter_header_extra(path, src, extra_directives, &mut |revision, ln, line_number| {
- if revision.is_some() && revision != cfg {
- return;
- }
+ let mut local_poisoned = false;
+
+ iter_header_extra(
+ config.mode,
+ &config.suite,
+ &mut local_poisoned,
+ path,
+ src,
+ extra_directives,
+ &mut |revision, og_ln, ln, line_number| {
+ if revision.is_some() && revision != cfg {
+ return;
+ }
- macro_rules! decision {
- ($e:expr) => {
- match $e {
- IgnoreDecision::Ignore { reason } => {
- ignore = true;
- // The ignore reason must be a &'static str, so we have to leak memory to
- // create it. This is fine, as the header is parsed only at the start of
- // compiletest so it won't grow indefinitely.
- ignore_message = Some(&*Box::leak(Box::<str>::from(reason)));
+ macro_rules! decision {
+ ($e:expr) => {
+ match $e {
+ IgnoreDecision::Ignore { reason } => {
+ ignore = true;
+ // The ignore reason must be a &'static str, so we have to leak memory to
+ // create it. This is fine, as the header is parsed only at the start of
+ // compiletest so it won't grow indefinitely.
+ ignore_message = Some(&*Box::leak(Box::<str>::from(reason)));
+ }
+ IgnoreDecision::Error { message } => {
+ eprintln!("error: {}:{line_number}: {message}", path.display());
+ *poisoned = true;
+ return;
+ }
+ IgnoreDecision::Continue => {}
}
- IgnoreDecision::Error { message } => {
- eprintln!("error: {}:{line_number}: {message}", path.display());
- *poisoned = true;
- return;
- }
- IgnoreDecision::Continue => {}
+ };
+ }
+
+ if let Some((_, post)) = og_ln.trim_start().split_once("//") {
+ let post = post.trim_start();
+ if post.starts_with("ignore-tidy")
+ && config.mode == Mode::Ui
+ && config.suite == "ui"
+ {
+ // not handled by compiletest under the ui test mode and ui test suite.
+ } else {
+ decision!(cfg::handle_ignore(config, ln));
}
- };
- }
+ } else {
+ decision!(cfg::handle_ignore(config, ln));
+ }
- decision!(cfg::handle_ignore(config, ln));
- decision!(cfg::handle_only(config, ln));
- decision!(needs::handle_needs(&cache.needs, config, ln));
- decision!(ignore_llvm(config, ln));
- decision!(ignore_cdb(config, ln));
- decision!(ignore_gdb(config, ln));
- decision!(ignore_lldb(config, ln));
-
- if config.target == "wasm32-unknown-unknown" {
- if config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) {
- decision!(IgnoreDecision::Ignore {
- reason: "ignored when checking the run results on WASM".into(),
- });
+ decision!(cfg::handle_only(config, ln));
+ decision!(needs::handle_needs(&cache.needs, config, ln));
+ decision!(ignore_llvm(config, ln));
+ decision!(ignore_cdb(config, ln));
+ decision!(ignore_gdb(config, ln));
+ decision!(ignore_lldb(config, ln));
+
+ if config.target == "wasm32-unknown-unknown" {
+ if config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) {
+ decision!(IgnoreDecision::Ignore {
+ reason: "ignored when checking the run results on WASM".into(),
+ });
+ }
}
- }
- should_fail |= config.parse_name_directive(ln, "should-fail");
- });
+ should_fail |= config.parse_name_directive(ln, "should-fail");
+ },
+ );
+
+ if local_poisoned {
+ eprintln!("errors encountered when trying to make test description: {}", path.display());
+ panic!("errors encountered when trying to make test description");
+ }
// The `should-fail` annotation doesn't apply to pretty tests,
// since we run the pretty printer across all tests by default.
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index c859e8acade..274006ae8c1 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -210,7 +210,7 @@ fn should_fail() {
let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None);
assert_eq!(d.should_panic, test::ShouldPanic::No);
- let d = make_test_description(&config, tn, p, std::io::Cursor::new("// should-fail"), None);
+ let d = make_test_description(&config, tn, p, std::io::Cursor::new("//@ should-fail"), None);
assert_eq!(d.should_panic, test::ShouldPanic::Yes);
}
@@ -218,7 +218,7 @@ fn should_fail() {
fn revisions() {
let config: Config = cfg().build();
- assert_eq!(parse_rs(&config, "// revisions: a b c").revisions, vec!["a", "b", "c"],);
+ assert_eq!(parse_rs(&config, "//@ revisions: a b c").revisions, vec!["a", "b", "c"],);
assert_eq!(
parse_makefile(&config, "# revisions: hello there").revisions,
vec!["hello", "there"],
@@ -233,8 +233,8 @@ fn aux_build() {
parse_rs(
&config,
r"
- // aux-build: a.rs
- // aux-build: b.rs
+ //@ aux-build: a.rs
+ //@ aux-build: b.rs
"
)
.aux,
@@ -245,128 +245,128 @@ fn aux_build() {
#[test]
fn llvm_version() {
let config: Config = cfg().llvm_version("8.1.2").build();
- assert!(check_ignore(&config, "// min-llvm-version: 9.0"));
+ assert!(check_ignore(&config, "//@ min-llvm-version: 9.0"));
let config: Config = cfg().llvm_version("9.0.1").build();
- assert!(check_ignore(&config, "// min-llvm-version: 9.2"));
+ assert!(check_ignore(&config, "//@ min-llvm-version: 9.2"));
let config: Config = cfg().llvm_version("9.3.1").build();
- assert!(!check_ignore(&config, "// min-llvm-version: 9.2"));
+ assert!(!check_ignore(&config, "//@ min-llvm-version: 9.2"));
let config: Config = cfg().llvm_version("10.0.0").build();
- assert!(!check_ignore(&config, "// min-llvm-version: 9.0"));
+ assert!(!check_ignore(&config, "//@ min-llvm-version: 9.0"));
}
#[test]
fn system_llvm_version() {
let config: Config = cfg().system_llvm(true).llvm_version("17.0.0").build();
- assert!(check_ignore(&config, "// min-system-llvm-version: 18.0"));
+ assert!(check_ignore(&config, "//@ min-system-llvm-version: 18.0"));
let config: Config = cfg().system_llvm(true).llvm_version("18.0.0").build();
- assert!(!check_ignore(&config, "// min-system-llvm-version: 18.0"));
+ assert!(!check_ignore(&config, "//@ min-system-llvm-version: 18.0"));
let config: Config = cfg().llvm_version("17.0.0").build();
- assert!(!check_ignore(&config, "// min-system-llvm-version: 18.0"));
+ assert!(!check_ignore(&config, "//@ min-system-llvm-version: 18.0"));
}
#[test]
fn ignore_target() {
let config: Config = cfg().target("x86_64-unknown-linux-gnu").build();
- assert!(check_ignore(&config, "// ignore-x86_64-unknown-linux-gnu"));
- assert!(check_ignore(&config, "// ignore-x86_64"));
- assert!(check_ignore(&config, "// ignore-linux"));
- assert!(check_ignore(&config, "// ignore-gnu"));
- assert!(check_ignore(&config, "// ignore-64bit"));
+ assert!(check_ignore(&config, "//@ ignore-x86_64-unknown-linux-gnu"));
+ assert!(check_ignore(&config, "//@ ignore-x86_64"));
+ assert!(check_ignore(&config, "//@ ignore-linux"));
+ assert!(check_ignore(&config, "//@ ignore-gnu"));
+ assert!(check_ignore(&config, "//@ ignore-64bit"));
- assert!(!check_ignore(&config, "// ignore-x86"));
- assert!(!check_ignore(&config, "// ignore-windows"));
- assert!(!check_ignore(&config, "// ignore-msvc"));
- assert!(!check_ignore(&config, "// ignore-32bit"));
+ assert!(!check_ignore(&config, "//@ ignore-x86"));
+ assert!(!check_ignore(&config, "//@ ignore-windows"));
+ assert!(!check_ignore(&config, "//@ ignore-msvc"));
+ assert!(!check_ignore(&config, "//@ ignore-32bit"));
}
#[test]
fn only_target() {
let config: Config = cfg().target("x86_64-pc-windows-gnu").build();
- assert!(check_ignore(&config, "// only-x86"));
- assert!(check_ignore(&config, "// only-linux"));
- assert!(check_ignore(&config, "// only-msvc"));
- assert!(check_ignore(&config, "// only-32bit"));
+ assert!(check_ignore(&config, "//@ only-x86"));
+ assert!(check_ignore(&config, "//@ only-linux"));
+ assert!(check_ignore(&config, "//@ only-msvc"));
+ assert!(check_ignore(&config, "//@ only-32bit"));
- assert!(!check_ignore(&config, "// only-x86_64-pc-windows-gnu"));
- assert!(!check_ignore(&config, "// only-x86_64"));
- assert!(!check_ignore(&config, "// only-windows"));
- assert!(!check_ignore(&config, "// only-gnu"));
- assert!(!check_ignore(&config, "// only-64bit"));
+ assert!(!check_ignore(&config, "//@ only-x86_64-pc-windows-gnu"));
+ assert!(!check_ignore(&config, "//@ only-x86_64"));
+ assert!(!check_ignore(&config, "//@ only-windows"));
+ assert!(!check_ignore(&config, "//@ only-gnu"));
+ assert!(!check_ignore(&config, "//@ only-64bit"));
}
#[test]
fn stage() {
let config: Config = cfg().stage_id("stage1-x86_64-unknown-linux-gnu").build();
- assert!(check_ignore(&config, "// ignore-stage1"));
- assert!(!check_ignore(&config, "// ignore-stage2"));
+ assert!(check_ignore(&config, "//@ ignore-stage1"));
+ assert!(!check_ignore(&config, "//@ ignore-stage2"));
}
#[test]
fn cross_compile() {
let config: Config = cfg().host("x86_64-apple-darwin").target("wasm32-unknown-unknown").build();
- assert!(check_ignore(&config, "// ignore-cross-compile"));
+ assert!(check_ignore(&config, "//@ ignore-cross-compile"));
let config: Config = cfg().host("x86_64-apple-darwin").target("x86_64-apple-darwin").build();
- assert!(!check_ignore(&config, "// ignore-cross-compile"));
+ assert!(!check_ignore(&config, "//@ ignore-cross-compile"));
}
#[test]
fn debugger() {
let mut config = cfg().build();
config.debugger = None;
- assert!(!check_ignore(&config, "// ignore-cdb"));
+ assert!(!check_ignore(&config, "//@ ignore-cdb"));
config.debugger = Some(Debugger::Cdb);
- assert!(check_ignore(&config, "// ignore-cdb"));
+ assert!(check_ignore(&config, "//@ ignore-cdb"));
config.debugger = Some(Debugger::Gdb);
- assert!(check_ignore(&config, "// ignore-gdb"));
+ assert!(check_ignore(&config, "//@ ignore-gdb"));
config.debugger = Some(Debugger::Lldb);
- assert!(check_ignore(&config, "// ignore-lldb"));
+ assert!(check_ignore(&config, "//@ ignore-lldb"));
}
#[test]
fn git_hash() {
let config: Config = cfg().git_hash(false).build();
- assert!(check_ignore(&config, "// needs-git-hash"));
+ assert!(check_ignore(&config, "//@ needs-git-hash"));
let config: Config = cfg().git_hash(true).build();
- assert!(!check_ignore(&config, "// needs-git-hash"));
+ assert!(!check_ignore(&config, "//@ needs-git-hash"));
}
#[test]
fn sanitizers() {
// Target that supports all sanitizers:
let config: Config = cfg().target("x86_64-unknown-linux-gnu").build();
- assert!(!check_ignore(&config, "// needs-sanitizer-address"));
- assert!(!check_ignore(&config, "// needs-sanitizer-leak"));
- assert!(!check_ignore(&config, "// needs-sanitizer-memory"));
- assert!(!check_ignore(&config, "// needs-sanitizer-thread"));
+ assert!(!check_ignore(&config, "//@ needs-sanitizer-address"));
+ assert!(!check_ignore(&config, "//@ needs-sanitizer-leak"));
+ assert!(!check_ignore(&config, "//@ needs-sanitizer-memory"));
+ assert!(!check_ignore(&config, "//@ needs-sanitizer-thread"));
// Target that doesn't support sanitizers:
let config: Config = cfg().target("wasm32-unknown-emscripten").build();
- assert!(check_ignore(&config, "// needs-sanitizer-address"));
- assert!(check_ignore(&config, "// needs-sanitizer-leak"));
- assert!(check_ignore(&config, "// needs-sanitizer-memory"));
- assert!(check_ignore(&config, "// needs-sanitizer-thread"));
+ assert!(check_ignore(&config, "//@ needs-sanitizer-address"));
+ assert!(check_ignore(&config, "//@ needs-sanitizer-leak"));
+ assert!(check_ignore(&config, "//@ needs-sanitizer-memory"));
+ assert!(check_ignore(&config, "//@ needs-sanitizer-thread"));
}
#[test]
fn profiler_support() {
let config: Config = cfg().profiler_support(false).build();
- assert!(check_ignore(&config, "// needs-profiler-support"));
+ assert!(check_ignore(&config, "//@ needs-profiler-support"));
let config: Config = cfg().profiler_support(true).build();
- assert!(!check_ignore(&config, "// needs-profiler-support"));
+ assert!(!check_ignore(&config, "//@ needs-profiler-support"));
}
#[test]
@@ -382,7 +382,7 @@ fn asm_support() {
for (target, has_asm) in asms {
let config = cfg().target(target).build();
assert_eq!(config.has_asm_support(), has_asm);
- assert_eq!(check_ignore(&config, "// needs-asm-support"), !has_asm)
+ assert_eq!(check_ignore(&config, "//@ needs-asm-support"), !has_asm)
}
}
@@ -390,13 +390,13 @@ fn asm_support() {
fn channel() {
let config: Config = cfg().channel("beta").build();
- assert!(check_ignore(&config, "// ignore-beta"));
- assert!(check_ignore(&config, "// only-nightly"));
- assert!(check_ignore(&config, "// only-stable"));
+ assert!(check_ignore(&config, "//@ ignore-beta"));
+ assert!(check_ignore(&config, "//@ only-nightly"));
+ assert!(check_ignore(&config, "//@ only-stable"));
- assert!(!check_ignore(&config, "// only-beta"));
- assert!(!check_ignore(&config, "// ignore-nightly"));
- assert!(!check_ignore(&config, "// ignore-stable"));
+ assert!(!check_ignore(&config, "//@ only-beta"));
+ assert!(!check_ignore(&config, "//@ ignore-nightly"));
+ assert!(!check_ignore(&config, "//@ ignore-stable"));
}
#[test]
@@ -418,7 +418,7 @@ fn test_extract_version_range() {
#[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")]
fn test_duplicate_revisions() {
let config: Config = cfg().build();
- parse_rs(&config, "// revisions: rpass1 rpass1");
+ parse_rs(&config, "//@ revisions: rpass1 rpass1");
}
#[test]
@@ -432,7 +432,7 @@ fn ignore_arch() {
for (target, arch) in archs {
let config: Config = cfg().target(target).build();
assert!(config.matches_arch(arch), "{target} {arch}");
- assert!(check_ignore(&config, &format!("// ignore-{arch}")));
+ assert!(check_ignore(&config, &format!("//@ ignore-{arch}")));
}
}
@@ -447,7 +447,7 @@ fn matches_os() {
for (target, os) in oss {
let config = cfg().target(target).build();
assert!(config.matches_os(os), "{target} {os}");
- assert!(check_ignore(&config, &format!("// ignore-{os}")));
+ assert!(check_ignore(&config, &format!("//@ ignore-{os}")));
}
}
@@ -461,7 +461,7 @@ fn matches_env() {
for (target, env) in envs {
let config: Config = cfg().target(target).build();
assert!(config.matches_env(env), "{target} {env}");
- assert!(check_ignore(&config, &format!("// ignore-{env}")));
+ assert!(check_ignore(&config, &format!("//@ ignore-{env}")));
}
}
@@ -475,7 +475,7 @@ fn matches_abi() {
for (target, abi) in abis {
let config: Config = cfg().target(target).build();
assert!(config.matches_abi(abi), "{target} {abi}");
- assert!(check_ignore(&config, &format!("// ignore-{abi}")));
+ assert!(check_ignore(&config, &format!("//@ ignore-{abi}")));
}
}
@@ -491,7 +491,7 @@ fn is_big_endian() {
for (target, is_big) in endians {
let config = cfg().target(target).build();
assert_eq!(config.is_big_endian(), is_big, "{target} {is_big}");
- assert_eq!(check_ignore(&config, "// ignore-endian-big"), is_big);
+ assert_eq!(check_ignore(&config, "//@ ignore-endian-big"), is_big);
}
}
@@ -506,9 +506,9 @@ fn pointer_width() {
for (target, width) in widths {
let config: Config = cfg().target(target).build();
assert_eq!(config.get_pointer_width(), width, "{target} {width}");
- assert_eq!(check_ignore(&config, "// ignore-16bit"), width == 16);
- assert_eq!(check_ignore(&config, "// ignore-32bit"), width == 32);
- assert_eq!(check_ignore(&config, "// ignore-64bit"), width == 64);
+ assert_eq!(check_ignore(&config, "//@ ignore-16bit"), width == 16);
+ assert_eq!(check_ignore(&config, "//@ ignore-32bit"), width == 32);
+ assert_eq!(check_ignore(&config, "//@ ignore-64bit"), width == 64);
}
}
@@ -534,7 +534,7 @@ fn wasm_special() {
for (target, pattern, ignore) in ignores {
let config: Config = cfg().target(target).build();
assert_eq!(
- check_ignore(&config, &format!("// ignore-{pattern}")),
+ check_ignore(&config, &format!("//@ ignore-{pattern}")),
ignore,
"{target} {pattern}"
);
@@ -555,8 +555,8 @@ fn families() {
assert!(config.matches_family(family));
let other = if family == "windows" { "unix" } else { "windows" };
assert!(!config.matches_family(other));
- assert!(check_ignore(&config, &format!("// ignore-{family}")));
- assert!(!check_ignore(&config, &format!("// ignore-{other}")));
+ assert!(check_ignore(&config, &format!("//@ ignore-{family}")));
+ assert!(!check_ignore(&config, &format!("//@ ignore-{other}")));
}
}
@@ -566,10 +566,17 @@ fn ignore_mode() {
// Indicate profiler support so that "coverage-run" tests aren't skipped.
let config: Config = cfg().mode(mode).profiler_support(true).build();
let other = if mode == "coverage-run" { "coverage-map" } else { "coverage-run" };
+
assert_ne!(mode, other);
assert_eq!(config.mode, Mode::from_str(mode).unwrap());
assert_ne!(config.mode, Mode::from_str(other).unwrap());
- assert!(check_ignore(&config, &format!("// ignore-mode-{mode}")));
- assert!(!check_ignore(&config, &format!("// ignore-mode-{other}")));
+
+ if mode == "ui" {
+ assert!(check_ignore(&config, &format!("//@ ignore-mode-{mode}")));
+ assert!(!check_ignore(&config, &format!("//@ ignore-mode-{other}")));
+ } else {
+ assert!(check_ignore(&config, &format!("// ignore-mode-{mode}")));
+ assert!(!check_ignore(&config, &format!("// ignore-mode-{other}")));
+ }
}
}
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 8b0e80a94b0..a8aae6f5bc9 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -55,11 +55,14 @@ const ANNOTATIONS_TO_IGNORE: &[&str] = &[
"// CHECK",
"// EMIT_MIR",
"// compile-flags",
+ "//@ compile-flags",
"// error-pattern",
+ "//@ error-pattern",
"// gdb",
"// lldb",
"// cdb",
"// normalize-stderr-test",
+ "//@ normalize-stderr-test",
];
// Intentionally written in decimal rather than hex
@@ -128,7 +131,15 @@ fn should_ignore(line: &str) -> bool {
// This mirrors the regex in src/tools/compiletest/src/runtest.rs, please
// update both if either are changed.
let re = Regex::new("\\s*//(\\[.*\\])?~.*").unwrap();
- re.is_match(line) || ANNOTATIONS_TO_IGNORE.iter().any(|a| line.contains(a))
+ // For `ui_test`-style UI test directives, also ignore
+ // - `//@[rev] compile-flags`
+ // - `//@[rev] normalize-stderr-test`
+ let ui_test_long_directives =
+ Regex::new("\\s*//@(\\[.*\\]) (compile-flags|normalize-stderr-test|error-pattern).*")
+ .unwrap();
+ re.is_match(line)
+ || ANNOTATIONS_TO_IGNORE.iter().any(|a| line.contains(a))
+ || ui_test_long_directives.is_match(line)
}
/// Returns `true` if `line` is allowed to be longer than the normal limit.
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 1dbd221fde5..5517b9fdcec 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -14,8 +14,9 @@ use std::path::{Path, PathBuf};
// #73494.
const ENTRY_LIMIT: usize = 900;
// FIXME: The following limits should be reduced eventually.
+
const ISSUES_ENTRY_LIMIT: usize = 1781;
-const ROOT_ENTRY_LIMIT: usize = 870;
+const ROOT_ENTRY_LIMIT: usize = 871;
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files
diff --git a/tests/ui/README.md b/tests/ui/README.md
new file mode 100644
index 00000000000..c14d0ee78c8
--- /dev/null
+++ b/tests/ui/README.md
@@ -0,0 +1,35 @@
+# UI Tests
+
+This folder contains `rustc`'s
+[UI tests](https://rustc-dev-guide.rust-lang.org/tests/ui.html).
+
+## Test Directives (Headers)
+
+Typically, a UI test will have some test directives / headers which are
+special comments that tell compiletest how to build and intepret a test.
+
+As part of an on-going effort to rewrite compiletest
+(see <https://github.com/rust-lang/compiler-team/issues/536>), a major
+change proposal to change legacy compiletest-style headers `// <directive>`
+to [`ui_test`](https://github.com/oli-obk/ui_test)-style headers
+`//@ <directive>` was accepted (see
+<https://github.com/rust-lang/compiler-team/issues/512>.
+
+An example directive is `ignore-test`. In legacy compiletest style, the header
+would be written as
+
+```rs
+// ignore-test
+```
+
+but in `ui_test` style, the header would be written as
+
+```rs
+//@ ignore-test
+```
+
+compiletest is changed to accept only `//@` directives for UI tests
+(currently), and will reject and report an error if it encounters any
+comments `// <content>` that may be parsed as an legacy compiletest-style
+test header. To fix this, you should migrate to the `ui_test`-style header
+`//@ <content>`.
diff --git a/tests/ui/symbol-names/x86-stdcall.rs b/tests/ui/symbol-names/x86-stdcall.rs
index 43c086dc6bc..c0cb42023a1 100644
--- a/tests/ui/symbol-names/x86-stdcall.rs
+++ b/tests/ui/symbol-names/x86-stdcall.rs
@@ -1,3 +1,4 @@
+// ignore-tidy-linelength
// build-pass
// only-x86
// only-windows