about summary refs log tree commit diff
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)
downloadrust-e53d6dd35bb38b81dff4b00497f4c152e9009499.tar.gz
rust-e53d6dd35bb38b81dff4b00497f4c152e9009499.zip
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