diff options
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 |
commit | e53d6dd35bb38b81dff4b00497f4c152e9009499 (patch) | |
tree | f97c2cd49d3d3b54b330c1e955c8cc7dba14f7ce | |
parent | d2e8ecd8bd26a22111cdebfb813258450b07fbf0 (diff) |
Implement infra support for migrating from `//` to `//@` ui test directives
-rw-r--r-- | src/tools/compiletest/src/header.rs | 710 | ||||
-rw-r--r-- | src/tools/compiletest/src/header/tests.rs | 147 | ||||
-rw-r--r-- | src/tools/tidy/src/style.rs | 13 | ||||
-rw-r--r-- | src/tools/tidy/src/ui_tests.rs | 3 | ||||
-rw-r--r-- | tests/ui/README.md | 35 | ||||
-rw-r--r-- | tests/ui/symbol-names/x86-stdcall.rs | 1 |
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 |