Include a file path in DirectiveLine

This avoids the need to laboriously pass a test/aux file path into dozens of
directive parsing methods that already take a `DirectiveLine`.
This commit is contained in:
Zalathar
2025-10-19 20:51:42 +11:00
parent e1243553b3
commit 37a0e62cd2
5 changed files with 75 additions and 112 deletions
+51 -89
View File
@@ -66,8 +66,8 @@ pub(crate) fn from_file_directives(
file_directives,
// (dummy comment to force args into vertical layout)
&mut |ln: &DirectiveLine<'_>| {
parse_and_update_aux(config, ln, testfile, &mut props.aux);
config.parse_and_update_revisions(testfile, ln, &mut props.revisions);
parse_and_update_aux(config, ln, &mut props.aux);
config.parse_and_update_revisions(ln, &mut props.revisions);
},
);
@@ -376,25 +376,17 @@ fn load_from(&mut self, testfile: &Utf8Path, test_revision: Option<&str>, config
config.push_name_value_directive(
ln,
ERROR_PATTERN,
testfile,
&mut self.error_patterns,
|r| r,
);
config.push_name_value_directive(
ln,
REGEX_ERROR_PATTERN,
testfile,
&mut self.regex_error_patterns,
|r| r,
);
config.push_name_value_directive(
ln,
DOC_FLAGS,
testfile,
&mut self.doc_flags,
|r| r,
);
config.push_name_value_directive(ln, DOC_FLAGS, &mut self.doc_flags, |r| r);
fn split_flags(flags: &str) -> Vec<String> {
// Individual flags can be single-quoted to preserve spaces; see
@@ -409,9 +401,7 @@ fn split_flags(flags: &str) -> Vec<String> {
.collect::<Vec<_>>()
}
if let Some(flags) =
config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile)
{
if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) {
let flags = split_flags(&flags);
for (i, flag) in flags.iter().enumerate() {
if flag == "--edition" || flag.starts_with("--edition=") {
@@ -428,14 +418,11 @@ fn split_flags(flags: &str) -> Vec<String> {
}
self.compile_flags.extend(flags);
}
if config
.parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS, testfile)
.is_some()
{
if config.parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS).is_some() {
panic!("`compiler-flags` directive should be spelled `compile-flags`");
}
if let Some(range) = parse_edition_range(config, ln, testfile) {
if let Some(range) = parse_edition_range(config, ln) {
// The edition is added at the start, since flags from //@compile-flags must
// be passed to rustc last.
self.compile_flags.insert(
@@ -445,15 +432,14 @@ fn split_flags(flags: &str) -> Vec<String> {
has_edition = true;
}
config.parse_and_update_revisions(testfile, ln, &mut self.revisions);
config.parse_and_update_revisions(ln, &mut self.revisions);
if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS, testfile)
{
if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS) {
self.run_flags.extend(split_flags(&flags));
}
if self.pp_exact.is_none() {
self.pp_exact = config.parse_pp_exact(ln, testfile);
self.pp_exact = config.parse_pp_exact(ln);
}
config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice);
@@ -475,7 +461,7 @@ fn split_flags(flags: &str) -> Vec<String> {
);
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) {
if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE) {
self.pretty_mode = m;
}
@@ -486,40 +472,35 @@ fn split_flags(flags: &str) -> Vec<String> {
);
// Call a helper method to deal with aux-related directives.
parse_and_update_aux(config, ln, testfile, &mut self.aux);
parse_and_update_aux(config, ln, &mut self.aux);
config.push_name_value_directive(
ln,
EXEC_ENV,
testfile,
&mut self.exec_env,
Config::parse_env,
);
config.push_name_value_directive(
ln,
UNSET_EXEC_ENV,
testfile,
&mut self.unset_exec_env,
|r| r.trim().to_owned(),
);
config.push_name_value_directive(
ln,
RUSTC_ENV,
testfile,
&mut self.rustc_env,
Config::parse_env,
);
config.push_name_value_directive(
ln,
UNSET_RUSTC_ENV,
testfile,
&mut self.unset_rustc_env,
|r| r.trim().to_owned(),
);
config.push_name_value_directive(
ln,
FORBID_OUTPUT,
testfile,
&mut self.forbid_output,
|r| r,
);
@@ -555,7 +536,7 @@ fn split_flags(flags: &str) -> Vec<String> {
}
if let Some(code) = config
.parse_name_value_directive(ln, FAILURE_STATUS, testfile)
.parse_name_value_directive(ln, FAILURE_STATUS)
.and_then(|code| code.trim().parse::<i32>().ok())
{
self.failure_status = Some(code);
@@ -576,7 +557,6 @@ fn split_flags(flags: &str) -> Vec<String> {
config.set_name_value_directive(
ln,
ASSEMBLY_OUTPUT,
testfile,
&mut self.assembly_output,
|r| r.trim().to_string(),
);
@@ -589,9 +569,7 @@ fn split_flags(flags: &str) -> Vec<String> {
// 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)
{
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| {
@@ -619,21 +597,16 @@ fn split_flags(flags: &str) -> Vec<String> {
config.set_name_value_directive(
ln,
TEST_MIR_PASS,
testfile,
&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)
{
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, FILECHECK_FLAGS, testfile)
{
if let Some(flags) = config.parse_name_value_directive(ln, FILECHECK_FLAGS) {
self.filecheck_flags.extend(split_flags(&flags));
}
@@ -642,7 +615,7 @@ fn split_flags(flags: &str) -> Vec<String> {
self.update_add_core_stubs(ln, config);
if let Some(flags) =
config.parse_name_value_directive(ln, CORE_STUBS_COMPILE_FLAGS, testfile)
config.parse_name_value_directive(ln, CORE_STUBS_COMPILE_FLAGS)
{
let flags = split_flags(&flags);
for flag in &flags {
@@ -654,7 +627,7 @@ fn split_flags(flags: &str) -> Vec<String> {
}
if let Some(err_kind) =
config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS, testfile)
config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS)
{
self.dont_require_annotations
.insert(ErrorKind::expect_from_user_str(err_kind.trim()));
@@ -870,7 +843,7 @@ fn iter_directives(
];
// Process the extra implied directives, with a dummy line number of 0.
for directive_str in extra_directives {
let directive_line = line_directive(0, directive_str)
let directive_line = line_directive(testfile, 0, directive_str)
.unwrap_or_else(|| panic!("bad extra-directive line: {directive_str:?}"));
it(&directive_line);
}
@@ -911,12 +884,7 @@ fn iter_directives(
}
impl Config {
fn parse_and_update_revisions(
&self,
testfile: &Utf8Path,
line: &DirectiveLine<'_>,
existing: &mut Vec<String>,
) {
fn parse_and_update_revisions(&self, line: &DirectiveLine<'_>, existing: &mut Vec<String>) {
const FORBIDDEN_REVISION_NAMES: [&str; 2] = [
// `//@ revisions: true false` Implying `--cfg=true` and `--cfg=false` makes it very
// weird for the test, since if the test writer wants a cfg of the same revision name
@@ -927,7 +895,9 @@ fn parse_and_update_revisions(
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) {
if let Some(raw) = self.parse_name_value_directive(line, "revisions") {
let &DirectiveLine { file_path: testfile, .. } = line;
if self.mode == TestMode::RunMake {
panic!("`run-make` mode tests do not support revisions: {}", testfile);
}
@@ -972,11 +942,11 @@ fn parse_env(nv: String) -> (String, String) {
(name.to_owned(), value.to_owned())
}
fn parse_pp_exact(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option<Utf8PathBuf> {
if let Some(s) = self.parse_name_value_directive(line, "pp-exact", testfile) {
fn parse_pp_exact(&self, line: &DirectiveLine<'_>) -> Option<Utf8PathBuf> {
if let Some(s) = self.parse_name_value_directive(line, "pp-exact") {
Some(Utf8PathBuf::from(&s))
} else if self.parse_name_directive(line, "pp-exact") {
testfile.file_name().map(Utf8PathBuf::from)
line.file_path.file_name().map(Utf8PathBuf::from)
} else {
None
}
@@ -1013,9 +983,8 @@ fn parse_name_value_directive(
&self,
line: &DirectiveLine<'_>,
directive: &str,
testfile: &Utf8Path,
) -> Option<String> {
let &DirectiveLine { line_number, .. } = line;
let &DirectiveLine { file_path, line_number, .. } = line;
if line.name != directive {
return None;
@@ -1029,7 +998,7 @@ fn parse_name_value_directive(
let value = expand_variables(value.to_owned(), self);
if value.is_empty() {
error!("{testfile}:{line_number}: empty value for directive `{directive}`");
error!("{file_path}:{line_number}: empty value for directive `{directive}`");
help!("expected syntax is: `{directive}: value`");
panic!("empty directive value detected");
}
@@ -1046,12 +1015,11 @@ fn set_name_value_directive<T>(
&self,
line: &DirectiveLine<'_>,
directive: &str,
testfile: &Utf8Path,
value: &mut Option<T>,
parse: impl FnOnce(String) -> T,
) {
if value.is_none() {
*value = self.parse_name_value_directive(line, directive, testfile).map(parse);
*value = self.parse_name_value_directive(line, directive).map(parse);
}
}
@@ -1059,11 +1027,10 @@ fn push_name_value_directive<T>(
&self,
line: &DirectiveLine<'_>,
directive: &str,
testfile: &Utf8Path,
values: &mut Vec<T>,
parse: impl FnOnce(String) -> T,
) {
if let Some(value) = self.parse_name_value_directive(line, directive, testfile).map(parse) {
if let Some(value) = self.parse_name_value_directive(line, directive).map(parse) {
values.push(value);
}
}
@@ -1380,9 +1347,9 @@ macro_rules! decision {
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));
decision!(ignore_backends(config, path, ln));
decision!(needs_backends(config, path, ln));
decision!(ignore_llvm(config, ln));
decision!(ignore_backends(config, ln));
decision!(needs_backends(config, ln));
decision!(ignore_cdb(config, ln));
decision!(ignore_gdb(config, ln));
decision!(ignore_lldb(config, ln));
@@ -1523,10 +1490,9 @@ fn ignore_lldb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
IgnoreDecision::Continue
}
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)
{
fn ignore_backends(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
let path = line.file_path;
if let Some(backends_to_ignore) = config.parse_name_value_directive(line, "ignore-backends") {
for backend in backends_to_ignore.split_whitespace().map(|backend| {
match CodegenBackend::try_from(backend) {
Ok(backend) => backend,
@@ -1545,8 +1511,9 @@ fn ignore_backends(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -
IgnoreDecision::Continue
}
fn needs_backends(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision {
if let Some(needed_backends) = config.parse_name_value_directive(line, "needs-backends", path) {
fn needs_backends(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
let path = line.file_path;
if let Some(needed_backends) = config.parse_name_value_directive(line, "needs-backends") {
if !needed_backends
.split_whitespace()
.map(|backend| match CodegenBackend::try_from(backend) {
@@ -1568,9 +1535,10 @@ fn needs_backends(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) ->
IgnoreDecision::Continue
}
fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> IgnoreDecision {
fn ignore_llvm(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
let path = line.file_path;
if let Some(needed_components) =
config.parse_name_value_directive(line, "needs-llvm-components", path)
config.parse_name_value_directive(line, "needs-llvm-components")
{
let components: HashSet<_> = config.llvm_components.split_whitespace().collect();
if let Some(missing_component) = needed_components
@@ -1579,8 +1547,8 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> Ig
{
if env::var_os("COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS").is_some() {
panic!(
"missing LLVM component {}, and COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS is set: {}",
missing_component, path
"missing LLVM component {missing_component}, \
and COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS is set: {path}",
);
}
return IgnoreDecision::Ignore {
@@ -1591,9 +1559,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> Ig
if let Some(actual_version) = &config.llvm_version {
// 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)
{
if let Some(version_string) = config.parse_name_value_directive(line, "min-llvm-version") {
let min_version = extract_llvm_version(&version_string);
// Ignore if actual version is smaller than the minimum required version.
if *actual_version < min_version {
@@ -1604,7 +1570,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> Ig
};
}
} else if let Some(version_string) =
config.parse_name_value_directive(line, "max-llvm-major-version", path)
config.parse_name_value_directive(line, "max-llvm-major-version")
{
let max_version = extract_llvm_version(&version_string);
// Ignore if actual major version is larger than the maximum required major version.
@@ -1618,7 +1584,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> Ig
};
}
} else if let Some(version_string) =
config.parse_name_value_directive(line, "min-system-llvm-version", path)
config.parse_name_value_directive(line, "min-system-llvm-version")
{
let min_version = extract_llvm_version(&version_string);
// Ignore if using system LLVM and actual version
@@ -1631,7 +1597,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> Ig
};
}
} else if let Some(version_range) =
config.parse_name_value_directive(line, "ignore-llvm-version", path)
config.parse_name_value_directive(line, "ignore-llvm-version")
{
// Syntax is: "ignore-llvm-version: <version1> [- <version2>]"
let (v_min, v_max) =
@@ -1657,7 +1623,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &DirectiveLine<'_>) -> Ig
}
}
} else if let Some(version_string) =
config.parse_name_value_directive(line, "exact-llvm-major-version", path)
config.parse_name_value_directive(line, "exact-llvm-major-version")
{
// Syntax is "exact-llvm-major-version: <version>"
let version = extract_llvm_version(&version_string);
@@ -1680,13 +1646,9 @@ enum IgnoreDecision {
Error { message: String },
}
fn parse_edition_range(
config: &Config,
line: &DirectiveLine<'_>,
testfile: &Utf8Path,
) -> Option<EditionRange> {
let raw = config.parse_name_value_directive(line, "edition", testfile)?;
let line_number = line.line_number;
fn parse_edition_range(config: &Config, line: &DirectiveLine<'_>) -> Option<EditionRange> {
let raw = config.parse_name_value_directive(line, "edition")?;
let &DirectiveLine { file_path: testfile, line_number, .. } = line;
// Edition range is half-open: `[lower_bound, upper_bound)`
if let Some((lower_bound, upper_bound)) = raw.split_once("..") {
@@ -3,8 +3,6 @@
use std::iter;
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;
@@ -47,7 +45,6 @@ pub(crate) fn all_aux_path_strings(&self) -> impl Iterator<Item = &str> {
pub(super) fn parse_and_update_aux(
config: &Config,
directive_line: &DirectiveLine<'_>,
testfile: &Utf8Path,
aux: &mut AuxProps,
) {
if !(directive_line.name.starts_with("aux-") || directive_line.name == "proc-macro") {
@@ -56,16 +53,12 @@ pub(super) fn parse_and_update_aux(
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_BUILD, &mut aux.builds, |r| r.trim().to_string());
config.push_name_value_directive(ln, AUX_BIN, &mut aux.bins, |r| r.trim().to_string());
config.push_name_value_directive(ln, AUX_CRATE, &mut aux.crates, parse_aux_crate);
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()
});
if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND, testfile) {
.push_name_value_directive(ln, PROC_MACRO, &mut aux.proc_macros, |r| r.trim().to_string());
if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) {
aux.codegen_backend = Some(r.trim().to_owned());
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ pub(crate) fn from_file_contents(path: &'a Utf8Path, file_contents: &'a str) ->
for (line_number, ln) in (1..).zip(file_contents.lines()) {
let ln = ln.trim();
if let Some(directive_line) = line_directive(line_number, ln) {
if let Some(directive_line) = line_directive(path, line_number, ln) {
lines.push(directive_line);
}
}
+16 -8
View File
@@ -1,13 +1,16 @@
use std::fmt;
use camino::Utf8Path;
const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@";
/// If the given line begins with the appropriate comment prefix for a directive,
/// returns a struct containing various parts of the directive.
pub(crate) fn line_directive<'line>(
pub(crate) fn line_directive<'a>(
file_path: &'a Utf8Path,
line_number: usize,
original_line: &'line str,
) -> Option<DirectiveLine<'line>> {
original_line: &'a str,
) -> Option<DirectiveLine<'a>> {
// Ignore lines that don't start with the comment prefix.
let after_comment =
original_line.trim_start().strip_prefix(COMPILETEST_DIRECTIVE_PREFIX)?.trim_start();
@@ -33,7 +36,7 @@ pub(crate) fn line_directive<'line>(
// The directive name ends at the first occurrence of colon, space, or end-of-string.
let name = raw_directive.split([':', ' ']).next().expect("split is never empty");
Some(DirectiveLine { line_number, revision, raw_directive, name })
Some(DirectiveLine { file_path, line_number, revision, raw_directive, name })
}
/// The (partly) broken-down contents of a line containing a test directive,
@@ -51,25 +54,30 @@ pub(crate) fn line_directive<'line>(
/// ^^^^^^^^^^^^^^^^^ raw_directive
/// ^^^^^^^^^^^^^ name
/// ```
pub(crate) struct DirectiveLine<'ln> {
pub(crate) struct DirectiveLine<'a> {
/// Path of the file containing this line.
///
/// Mostly used for diagnostics, but some directives (e.g. `//@ pp-exact`)
/// also use it to compute a value based on the filename.
pub(crate) file_path: &'a Utf8Path,
pub(crate) line_number: usize,
/// Some test directives start with a revision name in square brackets
/// (e.g. `[foo]`), and only apply to that revision of the test.
/// If present, this field contains the revision name (e.g. `foo`).
pub(crate) revision: Option<&'ln str>,
pub(crate) revision: Option<&'a str>,
/// The main part of the directive, after removing the comment prefix
/// and the optional revision specifier.
///
/// This is "raw" because the directive's name and colon-separated value
/// (if present) have not yet been extracted or checked.
raw_directive: &'ln str,
raw_directive: &'a str,
/// Name of the directive.
///
/// Invariant: `self.raw_directive.starts_with(self.name)`
pub(crate) name: &'ln str,
pub(crate) name: &'a str,
}
impl<'ln> DirectiveLine<'ln> {
@@ -956,9 +956,9 @@ fn parse_edition_range(line: &str) -> Option<EditionRange> {
let config = cfg().build();
let line_with_comment = format!("//@ {line}");
let line = line_directive(0, &line_with_comment).unwrap();
let line = line_directive(Utf8Path::new("tmp.rs"), 0, &line_with_comment).unwrap();
super::parse_edition_range(&config, &line, "tmp.rs".into())
super::parse_edition_range(&config, &line)
}
#[test]