about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-10-01 22:14:58 +1000
committerGitHub <noreply@github.com>2025-10-01 22:14:58 +1000
commit3102f00f8df83f9dbcfd8c87b1bbae5a0aebcb53 (patch)
treed592a1a035527f4ac39f0350d8084898cf1f6d55 /src
parentf8ae00a8c56421826551948132ded2c0c35e5703 (diff)
parente491056ac5722bd404dd6c326c76f457eb08b467 (diff)
downloadrust-3102f00f8df83f9dbcfd8c87b1bbae5a0aebcb53.tar.gz
rust-3102f00f8df83f9dbcfd8c87b1bbae5a0aebcb53.zip
Rollup merge of #147170 - Zalathar:directive, r=jieyouxu
compiletest: Pass around `DirectiveLine` instead of bare strings

This is an incremental step towards being able to clean up and centralize compiletest directive parsing.

My original plan was to add features to `DirectiveLine`, and then gradually migrate parsing code to use those features. However, that turned out to be impractical, because of how the existing directive parsers call each other. So instead this PR focuses on getting them to all take `DirectiveLine` instead of bare strings, to enable incremental work in the future.

Because this is part of an ongoing cleanup, I've prioritised clean diffs over nice code, because much of this code is going to be modified again when `DirectiveLine` is more capable.

r? jieyouxu
Diffstat (limited to 'src')
-rw-r--r--src/tools/compiletest/src/directives.rs217
-rw-r--r--src/tools/compiletest/src/directives/auxiliary.rs35
-rw-r--r--src/tools/compiletest/src/directives/cfg.rs14
-rw-r--r--src/tools/compiletest/src/directives/needs.rs6
-rw-r--r--src/tools/compiletest/src/runtest/debugger.rs27
-rw-r--r--src/tools/compiletest/src/runtest/debuginfo.rs6
6 files changed, 128 insertions, 177 deletions
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index e84a2278766..e6916610190 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -63,9 +63,10 @@ impl EarlyProps {
             &mut poisoned,
             testfile,
             rdr,
-            &mut |DirectiveLine { line_number, raw_directive: ln, .. }| {
-                parse_and_update_aux(config, ln, testfile, line_number, &mut props.aux);
-                config.parse_and_update_revisions(testfile, line_number, ln, &mut props.revisions);
+            // (dummy comment to force args into vertical layout)
+            &mut |ref ln: DirectiveLine<'_>| {
+                parse_and_update_aux(config, ln, testfile, &mut props.aux);
+                config.parse_and_update_revisions(testfile, ln, &mut props.revisions);
             },
         );
 
@@ -367,8 +368,8 @@ impl TestProps {
                 &mut poisoned,
                 testfile,
                 file,
-                &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| {
-                    if !directive.applies_to_test_revision(test_revision) {
+                &mut |ref ln: DirectiveLine<'_>| {
+                    if !ln.applies_to_test_revision(test_revision) {
                         return;
                     }
 
@@ -378,7 +379,6 @@ impl TestProps {
                         ln,
                         ERROR_PATTERN,
                         testfile,
-                        line_number,
                         &mut self.error_patterns,
                         |r| r,
                     );
@@ -386,7 +386,6 @@ impl TestProps {
                         ln,
                         REGEX_ERROR_PATTERN,
                         testfile,
-                        line_number,
                         &mut self.regex_error_patterns,
                         |r| r,
                     );
@@ -395,7 +394,6 @@ impl TestProps {
                         ln,
                         DOC_FLAGS,
                         testfile,
-                        line_number,
                         &mut self.doc_flags,
                         |r| r,
                     );
@@ -414,7 +412,7 @@ impl TestProps {
                     }
 
                     if let Some(flags) =
-                        config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile, line_number)
+                        config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile)
                     {
                         let flags = split_flags(&flags);
                         for flag in &flags {
@@ -425,39 +423,28 @@ impl TestProps {
                         self.compile_flags.extend(flags);
                     }
                     if config
-                        .parse_name_value_directive(
-                            ln,
-                            INCORRECT_COMPILER_FLAGS,
-                            testfile,
-                            line_number,
-                        )
+                        .parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS, testfile)
                         .is_some()
                     {
                         panic!("`compiler-flags` directive should be spelled `compile-flags`");
                     }
 
-                    if let Some(edition) = config.parse_edition(ln, testfile, line_number) {
+                    if let Some(edition) = config.parse_edition(ln, testfile) {
                         // The edition is added at the start, since flags from //@compile-flags must
                         // be passed to rustc last.
                         self.compile_flags.insert(0, format!("--edition={}", edition.trim()));
                         has_edition = true;
                     }
 
-                    config.parse_and_update_revisions(
-                        testfile,
-                        line_number,
-                        ln,
-                        &mut self.revisions,
-                    );
+                    config.parse_and_update_revisions(testfile, ln, &mut self.revisions);
 
-                    if let Some(flags) =
-                        config.parse_name_value_directive(ln, RUN_FLAGS, testfile, line_number)
+                    if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS, testfile)
                     {
                         self.run_flags.extend(split_flags(&flags));
                     }
 
                     if self.pp_exact.is_none() {
-                        self.pp_exact = config.parse_pp_exact(ln, testfile, line_number);
+                        self.pp_exact = config.parse_pp_exact(ln, testfile);
                     }
 
                     config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice);
@@ -479,9 +466,7 @@ impl TestProps {
                     );
                     config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic);
 
-                    if let Some(m) =
-                        config.parse_name_value_directive(ln, PRETTY_MODE, testfile, line_number)
-                    {
+                    if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE, testfile) {
                         self.pretty_mode = m;
                     }
 
@@ -492,13 +477,12 @@ impl TestProps {
                     );
 
                     // Call a helper method to deal with aux-related directives.
-                    parse_and_update_aux(config, ln, testfile, line_number, &mut self.aux);
+                    parse_and_update_aux(config, ln, testfile, &mut self.aux);
 
                     config.push_name_value_directive(
                         ln,
                         EXEC_ENV,
                         testfile,
-                        line_number,
                         &mut self.exec_env,
                         Config::parse_env,
                     );
@@ -506,7 +490,6 @@ impl TestProps {
                         ln,
                         UNSET_EXEC_ENV,
                         testfile,
-                        line_number,
                         &mut self.unset_exec_env,
                         |r| r.trim().to_owned(),
                     );
@@ -514,7 +497,6 @@ impl TestProps {
                         ln,
                         RUSTC_ENV,
                         testfile,
-                        line_number,
                         &mut self.rustc_env,
                         Config::parse_env,
                     );
@@ -522,7 +504,6 @@ impl TestProps {
                         ln,
                         UNSET_RUSTC_ENV,
                         testfile,
-                        line_number,
                         &mut self.unset_rustc_env,
                         |r| r.trim().to_owned(),
                     );
@@ -530,7 +511,6 @@ impl TestProps {
                         ln,
                         FORBID_OUTPUT,
                         testfile,
-                        line_number,
                         &mut self.forbid_output,
                         |r| r,
                     );
@@ -566,7 +546,7 @@ impl TestProps {
                     }
 
                     if let Some(code) = config
-                        .parse_name_value_directive(ln, FAILURE_STATUS, testfile, line_number)
+                        .parse_name_value_directive(ln, FAILURE_STATUS, testfile)
                         .and_then(|code| code.trim().parse::<i32>().ok())
                     {
                         self.failure_status = Some(code);
@@ -588,7 +568,6 @@ impl TestProps {
                         ln,
                         ASSEMBLY_OUTPUT,
                         testfile,
-                        line_number,
                         &mut self.assembly_output,
                         |r| r.trim().to_string(),
                     );
@@ -602,7 +581,7 @@ impl TestProps {
                     // 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, testfile, line_number)
+                        config.parse_name_value_directive(ln, KNOWN_BUG, testfile)
                     {
                         let known_bug = known_bug.trim();
                         if known_bug == "unknown"
@@ -632,24 +611,20 @@ impl TestProps {
                         ln,
                         TEST_MIR_PASS,
                         testfile,
-                        line_number,
                         &mut self.mir_unit_test,
                         |s| s.trim().to_string(),
                     );
                     config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base);
 
                     if let Some(flags) =
-                        config.parse_name_value_directive(ln, LLVM_COV_FLAGS, testfile, line_number)
+                        config.parse_name_value_directive(ln, LLVM_COV_FLAGS, testfile)
                     {
                         self.llvm_cov_flags.extend(split_flags(&flags));
                     }
 
-                    if let Some(flags) = config.parse_name_value_directive(
-                        ln,
-                        FILECHECK_FLAGS,
-                        testfile,
-                        line_number,
-                    ) {
+                    if let Some(flags) =
+                        config.parse_name_value_directive(ln, FILECHECK_FLAGS, testfile)
+                    {
                         self.filecheck_flags.extend(split_flags(&flags));
                     }
 
@@ -661,7 +636,6 @@ impl TestProps {
                         ln,
                         directives::CORE_STUBS_COMPILE_FLAGS,
                         testfile,
-                        line_number,
                     ) {
                         let flags = split_flags(&flags);
                         for flag in &flags {
@@ -672,12 +646,9 @@ impl TestProps {
                         self.core_stubs_compile_flags.extend(flags);
                     }
 
-                    if let Some(err_kind) = config.parse_name_value_directive(
-                        ln,
-                        DONT_REQUIRE_ANNOTATIONS,
-                        testfile,
-                        line_number,
-                    ) {
+                    if let Some(err_kind) =
+                        config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS, testfile)
+                    {
                         self.dont_require_annotations
                             .insert(ErrorKind::expect_from_user_str(err_kind.trim()));
                     }
@@ -734,7 +705,7 @@ impl TestProps {
         }
     }
 
-    fn update_fail_mode(&mut self, ln: &str, config: &Config) {
+    fn update_fail_mode(&mut self, ln: &DirectiveLine<'_>, config: &Config) {
         let check_ui = |mode: &str| {
             // Mode::Crashes may need build-fail in order to trigger llvm errors or stack overflows
             if config.mode != TestMode::Ui && config.mode != TestMode::Crashes {
@@ -769,7 +740,12 @@ impl TestProps {
         }
     }
 
-    fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) {
+    fn update_pass_mode(
+        &mut self,
+        ln: &DirectiveLine<'_>,
+        revision: Option<&str>,
+        config: &Config,
+    ) {
         let check_no_run = |s| match (config.mode, s) {
             (TestMode::Ui, _) => (),
             (TestMode::Crashes, _) => (),
@@ -814,7 +790,7 @@ impl TestProps {
         self.pass_mode
     }
 
-    pub fn update_add_core_stubs(&mut self, ln: &str, config: &Config) {
+    fn update_add_core_stubs(&mut self, ln: &DirectiveLine<'_>, config: &Config) {
         let add_core_stubs = config.parse_name_directive(ln, directives::ADD_CORE_STUBS);
         if add_core_stubs {
             if !matches!(config.mode, TestMode::Ui | TestMode::Codegen | TestMode::Assembly) {
@@ -905,10 +881,12 @@ pub(crate) struct CheckDirectiveResult<'ln> {
     trailing_directive: Option<&'ln str>,
 }
 
-pub(crate) fn check_directive<'a>(
-    directive_ln: &'a str,
+fn check_directive<'a>(
+    directive_ln: &DirectiveLine<'a>,
     mode: TestMode,
 ) -> CheckDirectiveResult<'a> {
+    let &DirectiveLine { raw_directive: directive_ln, .. } = directive_ln;
+
     let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, ""));
 
     let is_known_directive = KNOWN_DIRECTIVE_NAMES.contains(&directive_name)
@@ -980,7 +958,7 @@ fn iter_directives(
         // Perform unknown directive check on Rust files.
         if testfile.extension() == Some("rs") {
             let CheckDirectiveResult { is_known_directive, trailing_directive } =
-                check_directive(directive_line.raw_directive, mode);
+                check_directive(&directive_line, mode);
 
             if !is_known_directive {
                 *poisoned = true;
@@ -1014,8 +992,7 @@ impl Config {
     fn parse_and_update_revisions(
         &self,
         testfile: &Utf8Path,
-        line_number: usize,
-        line: &str,
+        line: &DirectiveLine<'_>,
         existing: &mut Vec<String>,
     ) {
         const FORBIDDEN_REVISION_NAMES: [&str; 2] = [
@@ -1028,8 +1005,7 @@ impl Config {
         const FILECHECK_FORBIDDEN_REVISION_NAMES: [&str; 9] =
             ["CHECK", "COM", "NEXT", "SAME", "EMPTY", "NOT", "COUNT", "DAG", "LABEL"];
 
-        if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile, line_number)
-        {
+        if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile) {
             if self.mode == TestMode::RunMake {
                 panic!("`run-make` mode tests do not support revisions: {}", testfile);
             }
@@ -1074,13 +1050,8 @@ impl Config {
         (name.to_owned(), value.to_owned())
     }
 
-    fn parse_pp_exact(
-        &self,
-        line: &str,
-        testfile: &Utf8Path,
-        line_number: usize,
-    ) -> Option<Utf8PathBuf> {
-        if let Some(s) = self.parse_name_value_directive(line, "pp-exact", testfile, line_number) {
+    fn parse_pp_exact(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option<Utf8PathBuf> {
+        if let Some(s) = self.parse_name_value_directive(line, "pp-exact", testfile) {
             Some(Utf8PathBuf::from(&s))
         } else if self.parse_name_directive(line, "pp-exact") {
             testfile.file_name().map(Utf8PathBuf::from)
@@ -1089,7 +1060,9 @@ impl Config {
         }
     }
 
-    fn parse_custom_normalization(&self, raw_directive: &str) -> Option<NormalizeRule> {
+    fn parse_custom_normalization(&self, line: &DirectiveLine<'_>) -> Option<NormalizeRule> {
+        let &DirectiveLine { raw_directive, .. } = line;
+
         // FIXME(Zalathar): Integrate name/value splitting into `DirectiveLine`
         // instead of doing it here.
         let (directive_name, raw_value) = raw_directive.split_once(':')?;
@@ -1110,24 +1083,23 @@ impl Config {
         Some(NormalizeRule { kind, regex, replacement })
     }
 
-    fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
+    fn parse_name_directive(&self, line: &DirectiveLine<'_>, directive: &str) -> bool {
+        let &DirectiveLine { raw_directive: line, .. } = line;
+
         // Ensure the directive is a whole word. Do not match "ignore-x86" when
         // the line says "ignore-x86_64".
         line.starts_with(directive)
             && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':'))
     }
 
-    fn parse_negative_name_directive(&self, line: &str, directive: &str) -> bool {
-        line.starts_with("no-") && self.parse_name_directive(&line[3..], directive)
-    }
-
-    pub fn parse_name_value_directive(
+    fn parse_name_value_directive(
         &self,
-        line: &str,
+        line: &DirectiveLine<'_>,
         directive: &str,
         testfile: &Utf8Path,
-        line_number: usize,
     ) -> Option<String> {
+        let &DirectiveLine { line_number, raw_directive: line, .. } = line;
+
         let colon = directive.len();
         if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
             let value = line[(colon + 1)..].to_owned();
@@ -1144,52 +1116,37 @@ impl Config {
         }
     }
 
-    fn parse_edition(&self, line: &str, testfile: &Utf8Path, line_number: usize) -> Option<String> {
-        self.parse_name_value_directive(line, "edition", testfile, line_number)
+    fn parse_edition(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option<String> {
+        self.parse_name_value_directive(line, "edition", testfile)
     }
 
-    fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) {
-        match value {
-            true => {
-                if self.parse_negative_name_directive(line, directive) {
-                    *value = false;
-                }
-            }
-            false => {
-                if self.parse_name_directive(line, directive) {
-                    *value = true;
-                }
-            }
-        }
+    fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) {
+        // If the flag is already true, don't bother looking at the directive.
+        *value = *value || self.parse_name_directive(line, directive);
     }
 
     fn set_name_value_directive<T>(
         &self,
-        line: &str,
+        line: &DirectiveLine<'_>,
         directive: &str,
         testfile: &Utf8Path,
-        line_number: usize,
         value: &mut Option<T>,
         parse: impl FnOnce(String) -> T,
     ) {
         if value.is_none() {
-            *value =
-                self.parse_name_value_directive(line, directive, testfile, line_number).map(parse);
+            *value = self.parse_name_value_directive(line, directive, testfile).map(parse);
         }
     }
 
     fn push_name_value_directive<T>(
         &self,
-        line: &str,
+        line: &DirectiveLine<'_>,
         directive: &str,
         testfile: &Utf8Path,
-        line_number: usize,
         values: &mut Vec<T>,
         parse: impl FnOnce(String) -> T,
     ) {
-        if let Some(value) =
-            self.parse_name_value_directive(line, directive, testfile, line_number).map(parse)
-        {
+        if let Some(value) = self.parse_name_value_directive(line, directive, testfile).map(parse) {
             values.push(value);
         }
     }
@@ -1482,8 +1439,8 @@ pub(crate) fn make_test_description<R: Read>(
         &mut local_poisoned,
         path,
         src,
-        &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| {
-            if !directive.applies_to_test_revision(test_revision) {
+        &mut |ref ln @ DirectiveLine { line_number, .. }| {
+            if !ln.applies_to_test_revision(test_revision) {
                 return;
             }
 
@@ -1507,9 +1464,9 @@ pub(crate) fn make_test_description<R: Read>(
             decision!(cfg::handle_ignore(config, ln));
             decision!(cfg::handle_only(config, ln));
             decision!(needs::handle_needs(&cache.needs, config, ln));
-            decision!(ignore_llvm(config, path, ln, line_number));
-            decision!(ignore_backends(config, path, ln, line_number));
-            decision!(needs_backends(config, path, ln, line_number));
+            decision!(ignore_llvm(config, path, ln));
+            decision!(ignore_backends(config, path, ln));
+            decision!(needs_backends(config, path, ln));
             decision!(ignore_cdb(config, ln));
             decision!(ignore_gdb(config, ln));
             decision!(ignore_lldb(config, ln));
@@ -1549,7 +1506,9 @@ pub(crate) fn make_test_description<R: Read>(
     }
 }
 
-fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision {
+fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     if config.debugger != Some(Debugger::Cdb) {
         return IgnoreDecision::Continue;
     }
@@ -1572,7 +1531,9 @@ fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision {
     IgnoreDecision::Continue
 }
 
-fn ignore_gdb(config: &Config, line: &str) -> IgnoreDecision {
+fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     if config.debugger != Some(Debugger::Gdb) {
         return IgnoreDecision::Continue;
     }
@@ -1620,7 +1581,9 @@ fn ignore_gdb(config: &Config, line: &str) -> IgnoreDecision {
     IgnoreDecision::Continue
 }
 
-fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision {
+fn ignore_lldb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     if config.debugger != Some(Debugger::Lldb) {
         return IgnoreDecision::Continue;
     }
@@ -1642,14 +1605,9 @@ fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision {
     IgnoreDecision::Continue
 }
 
-fn ignore_backends(
-    config: &Config,
-    path: &Utf8Path,
-    line: &str,
-    line_number: usize,
-) -> IgnoreDecision {
+fn ignore_backends(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision {
     if let Some(backends_to_ignore) =
-        config.parse_name_value_directive(line, "ignore-backends", path, line_number)
+        config.parse_name_value_directive(line, "ignore-backends", path)
     {
         for backend in backends_to_ignore.split_whitespace().map(|backend| {
             match CodegenBackend::try_from(backend) {
@@ -1669,15 +1627,8 @@ fn ignore_backends(
     IgnoreDecision::Continue
 }
 
-fn needs_backends(
-    config: &Config,
-    path: &Utf8Path,
-    line: &str,
-    line_number: usize,
-) -> IgnoreDecision {
-    if let Some(needed_backends) =
-        config.parse_name_value_directive(line, "needs-backends", path, line_number)
-    {
+fn needs_backends(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision {
+    if let Some(needed_backends) = config.parse_name_value_directive(line, "needs-backends", path) {
         if !needed_backends
             .split_whitespace()
             .map(|backend| match CodegenBackend::try_from(backend) {
@@ -1699,9 +1650,9 @@ fn needs_backends(
     IgnoreDecision::Continue
 }
 
-fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) -> IgnoreDecision {
+fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision {
     if let Some(needed_components) =
-        config.parse_name_value_directive(line, "needs-llvm-components", path, line_number)
+        config.parse_name_value_directive(line, "needs-llvm-components", path)
     {
         let components: HashSet<_> = config.llvm_components.split_whitespace().collect();
         if let Some(missing_component) = needed_components
@@ -1723,7 +1674,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
         // Note that these `min` versions will check for not just major versions.
 
         if let Some(version_string) =
-            config.parse_name_value_directive(line, "min-llvm-version", path, line_number)
+            config.parse_name_value_directive(line, "min-llvm-version", path)
         {
             let min_version = extract_llvm_version(&version_string);
             // Ignore if actual version is smaller than the minimum required version.
@@ -1735,7 +1686,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
                 };
             }
         } else if let Some(version_string) =
-            config.parse_name_value_directive(line, "max-llvm-major-version", path, line_number)
+            config.parse_name_value_directive(line, "max-llvm-major-version", path)
         {
             let max_version = extract_llvm_version(&version_string);
             // Ignore if actual major version is larger than the maximum required major version.
@@ -1749,7 +1700,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
                 };
             }
         } else if let Some(version_string) =
-            config.parse_name_value_directive(line, "min-system-llvm-version", path, line_number)
+            config.parse_name_value_directive(line, "min-system-llvm-version", path)
         {
             let min_version = extract_llvm_version(&version_string);
             // Ignore if using system LLVM and actual version
@@ -1762,7 +1713,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
                 };
             }
         } else if let Some(version_range) =
-            config.parse_name_value_directive(line, "ignore-llvm-version", path, line_number)
+            config.parse_name_value_directive(line, "ignore-llvm-version", path)
         {
             // Syntax is: "ignore-llvm-version: <version1> [- <version2>]"
             let (v_min, v_max) =
@@ -1788,7 +1739,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize)
                 }
             }
         } else if let Some(version_string) =
-            config.parse_name_value_directive(line, "exact-llvm-major-version", path, line_number)
+            config.parse_name_value_directive(line, "exact-llvm-major-version", path)
         {
             // Syntax is "exact-llvm-major-version: <version>"
             let version = extract_llvm_version(&version_string);
diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs
index 7c1ed2e7006..0675a6feac3 100644
--- a/src/tools/compiletest/src/directives/auxiliary.rs
+++ b/src/tools/compiletest/src/directives/auxiliary.rs
@@ -7,6 +7,7 @@ use camino::Utf8Path;
 
 use super::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE, PROC_MACRO};
 use crate::common::Config;
+use crate::directives::DirectiveLine;
 
 /// Properties parsed from `aux-*` test directives.
 #[derive(Clone, Debug, Default)]
@@ -45,40 +46,28 @@ impl AuxProps {
 /// and update [`AuxProps`] accordingly.
 pub(super) fn parse_and_update_aux(
     config: &Config,
-    ln: &str,
+    directive_line: &DirectiveLine<'_>,
     testfile: &Utf8Path,
-    line_number: usize,
     aux: &mut AuxProps,
 ) {
+    let &DirectiveLine { raw_directive: ln, .. } = directive_line;
+
     if !(ln.starts_with("aux-") || ln.starts_with("proc-macro")) {
         return;
     }
 
-    config.push_name_value_directive(ln, AUX_BUILD, testfile, line_number, &mut aux.builds, |r| {
+    let ln = directive_line;
+
+    config.push_name_value_directive(ln, AUX_BUILD, testfile, &mut aux.builds, |r| {
         r.trim().to_string()
     });
-    config.push_name_value_directive(ln, AUX_BIN, testfile, line_number, &mut aux.bins, |r| {
+    config
+        .push_name_value_directive(ln, AUX_BIN, testfile, &mut aux.bins, |r| r.trim().to_string());
+    config.push_name_value_directive(ln, AUX_CRATE, testfile, &mut aux.crates, parse_aux_crate);
+    config.push_name_value_directive(ln, PROC_MACRO, testfile, &mut aux.proc_macros, |r| {
         r.trim().to_string()
     });
-    config.push_name_value_directive(
-        ln,
-        AUX_CRATE,
-        testfile,
-        line_number,
-        &mut aux.crates,
-        parse_aux_crate,
-    );
-    config.push_name_value_directive(
-        ln,
-        PROC_MACRO,
-        testfile,
-        line_number,
-        &mut aux.proc_macros,
-        |r| r.trim().to_string(),
-    );
-    if let Some(r) =
-        config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND, testfile, line_number)
-    {
+    if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND, testfile) {
         aux.codegen_backend = Some(r.trim().to_owned());
     }
 }
diff --git a/src/tools/compiletest/src/directives/cfg.rs b/src/tools/compiletest/src/directives/cfg.rs
index 802a1d63d1f..62a4b88a33a 100644
--- a/src/tools/compiletest/src/directives/cfg.rs
+++ b/src/tools/compiletest/src/directives/cfg.rs
@@ -1,12 +1,14 @@
 use std::collections::HashSet;
 
 use crate::common::{CompareMode, Config, Debugger};
-use crate::directives::IgnoreDecision;
+use crate::directives::{DirectiveLine, IgnoreDecision};
 
 const EXTRA_ARCHS: &[&str] = &["spirv"];
 
-pub(super) fn handle_ignore(config: &Config, line: &str) -> IgnoreDecision {
+pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
     let parsed = parse_cfg_name_directive(config, line, "ignore");
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     match parsed.outcome {
         MatchOutcome::NoMatch => IgnoreDecision::Continue,
         MatchOutcome::Match => IgnoreDecision::Ignore {
@@ -21,8 +23,10 @@ pub(super) fn handle_ignore(config: &Config, line: &str) -> IgnoreDecision {
     }
 }
 
-pub(super) fn handle_only(config: &Config, line: &str) -> IgnoreDecision {
+pub(super) fn handle_only(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
     let parsed = parse_cfg_name_directive(config, line, "only");
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     match parsed.outcome {
         MatchOutcome::Match => IgnoreDecision::Continue,
         MatchOutcome::NoMatch => IgnoreDecision::Ignore {
@@ -43,9 +47,11 @@ pub(super) fn handle_only(config: &Config, line: &str) -> IgnoreDecision {
 /// or `only-windows`.
 fn parse_cfg_name_directive<'a>(
     config: &Config,
-    line: &'a str,
+    line: &'a DirectiveLine<'a>,
     prefix: &str,
 ) -> ParsedNameDirective<'a> {
+    let &DirectiveLine { raw_directive: line, .. } = line;
+
     if !line.as_bytes().starts_with(prefix.as_bytes()) {
         return ParsedNameDirective::not_a_directive();
     }
diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs
index 3b7a9478717..c8a729d8aab 100644
--- a/src/tools/compiletest/src/directives/needs.rs
+++ b/src/tools/compiletest/src/directives/needs.rs
@@ -1,10 +1,10 @@
 use crate::common::{Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer};
-use crate::directives::{IgnoreDecision, llvm_has_libzstd};
+use crate::directives::{DirectiveLine, IgnoreDecision, llvm_has_libzstd};
 
 pub(super) fn handle_needs(
     cache: &CachedNeedsConditions,
     config: &Config,
-    ln: &str,
+    ln: &DirectiveLine<'_>,
 ) -> IgnoreDecision {
     // Note that we intentionally still put the needs- prefix here to make the file show up when
     // grepping for a directive name, even though we could technically strip that.
@@ -181,6 +181,8 @@ pub(super) fn handle_needs(
         },
     ];
 
+    let &DirectiveLine { raw_directive: ln, .. } = ln;
+
     let (name, rest) = match ln.split_once([':', ' ']) {
         Some((name, rest)) => (name, Some(rest)),
         None => (ln, None),
diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs
index ba824124e87..3d439e98eb7 100644
--- a/src/tools/compiletest/src/runtest/debugger.rs
+++ b/src/tools/compiletest/src/runtest/debugger.rs
@@ -4,7 +4,6 @@ use std::io::{BufRead, BufReader};
 
 use camino::{Utf8Path, Utf8PathBuf};
 
-use crate::common::Config;
 use crate::runtest::ProcRes;
 
 /// Representation of information to invoke a debugger and check its output
@@ -20,11 +19,7 @@ pub(super) struct DebuggerCommands {
 }
 
 impl DebuggerCommands {
-    pub fn parse_from(
-        file: &Utf8Path,
-        config: &Config,
-        debugger_prefix: &str,
-    ) -> Result<Self, String> {
+    pub fn parse_from(file: &Utf8Path, debugger_prefix: &str) -> Result<Self, String> {
         let command_directive = format!("{debugger_prefix}-command");
         let check_directive = format!("{debugger_prefix}-check");
 
@@ -47,14 +42,10 @@ impl DebuggerCommands {
                 continue;
             };
 
-            if let Some(command) =
-                config.parse_name_value_directive(&line, &command_directive, file, line_no)
-            {
+            if let Some(command) = parse_name_value(&line, &command_directive) {
                 commands.push(command);
             }
-            if let Some(pattern) =
-                config.parse_name_value_directive(&line, &check_directive, file, line_no)
-            {
+            if let Some(pattern) = parse_name_value(&line, &check_directive) {
                 check_lines.push((line_no, pattern));
             }
         }
@@ -114,6 +105,18 @@ impl DebuggerCommands {
     }
 }
 
+/// Split off from the main `parse_name_value_directive`, so that improvements
+/// to directive handling aren't held back by debuginfo test commands.
+fn parse_name_value(line: &str, name: &str) -> Option<String> {
+    if let Some(after_name) = line.strip_prefix(name)
+        && let Some(value) = after_name.strip_prefix(':')
+    {
+        Some(value.to_owned())
+    } else {
+        None
+    }
+}
+
 /// Check that the pattern in `check_line` applies to `line`. Returns `true` if they do match.
 fn check_single_line(line: &str, check_line: &str) -> bool {
     // Allow check lines to leave parts unspecified (e.g., uninitialized
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
index 071c0863b7e..9175a38ffa5 100644
--- a/src/tools/compiletest/src/runtest/debuginfo.rs
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -59,7 +59,7 @@ impl TestCx<'_> {
         }
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "cdb")
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "cdb")
             .unwrap_or_else(|e| self.fatal(&e));
 
         // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
@@ -130,7 +130,7 @@ impl TestCx<'_> {
     }
 
     fn run_debuginfo_gdb_test_no_opt(&self) {
-        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "gdb")
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "gdb")
             .unwrap_or_else(|e| self.fatal(&e));
         let mut cmds = dbg_cmds.commands.join("\n");
 
@@ -397,7 +397,7 @@ impl TestCx<'_> {
         }
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "lldb")
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "lldb")
             .unwrap_or_else(|e| self.fatal(&e));
 
         // Write debugger script: