Skip to content

Commit 33c99a0

Browse files
committed
Use new DirectiveLine features in directive parsing
1 parent 6783e94 commit 33c99a0

File tree

6 files changed

+73
-89
lines changed

6 files changed

+73
-89
lines changed

src/tools/compiletest/src/directives.rs

Lines changed: 50 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -835,9 +835,7 @@ fn check_directive<'a>(
835835
directive_ln: &DirectiveLine<'a>,
836836
mode: TestMode,
837837
) -> CheckDirectiveResult<'a> {
838-
let &DirectiveLine { raw_directive: directive_ln, .. } = directive_ln;
839-
840-
let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, ""));
838+
let &DirectiveLine { name: directive_name, .. } = directive_ln;
841839

842840
let is_known_directive = KNOWN_DIRECTIVE_NAMES.contains(&directive_name)
843841
|| match mode {
@@ -846,14 +844,13 @@ fn check_directive<'a>(
846844
_ => false,
847845
};
848846

849-
let trailing = post.trim().split_once(' ').map(|(pre, _)| pre).unwrap_or(post);
850-
let trailing_directive = {
851-
// 1. is the directive name followed by a space? (to exclude `:`)
852-
directive_ln.get(directive_name.len()..).is_some_and(|s| s.starts_with(' '))
853-
// 2. is what is after that directive also a directive (ex: "only-x86 only-arm")
854-
&& KNOWN_DIRECTIVE_NAMES.contains(&trailing)
855-
}
856-
.then_some(trailing);
847+
// If it looks like the user tried to put two directives on the same line
848+
// (e.g. `//@ only-linux only-x86_64`), signal an error, because the
849+
// second "directive" would actually be ignored with no effect.
850+
let trailing_directive = directive_ln
851+
.remark_after_space()
852+
.map(|remark| remark.trim_start().split(' ').next().unwrap())
853+
.filter(|token| KNOWN_DIRECTIVE_NAMES.contains(token));
857854

858855
CheckDirectiveResult { is_known_directive, trailing_directive }
859856
}
@@ -915,7 +912,7 @@ fn iter_directives(
915912

916913
error!(
917914
"{testfile}:{line_number}: detected unknown compiletest test directive `{}`",
918-
directive_line.raw_directive,
915+
directive_line.display(),
919916
);
920917

921918
return;
@@ -1011,35 +1008,30 @@ impl Config {
10111008
}
10121009

10131010
fn parse_custom_normalization(&self, line: &DirectiveLine<'_>) -> Option<NormalizeRule> {
1014-
let &DirectiveLine { raw_directive, .. } = line;
1015-
1016-
// FIXME(Zalathar): Integrate name/value splitting into `DirectiveLine`
1017-
// instead of doing it here.
1018-
let (directive_name, raw_value) = raw_directive.split_once(':')?;
1011+
let &DirectiveLine { name, .. } = line;
10191012

1020-
let kind = match directive_name {
1013+
let kind = match name {
10211014
"normalize-stdout" => NormalizeKind::Stdout,
10221015
"normalize-stderr" => NormalizeKind::Stderr,
10231016
"normalize-stderr-32bit" => NormalizeKind::Stderr32bit,
10241017
"normalize-stderr-64bit" => NormalizeKind::Stderr64bit,
10251018
_ => return None,
10261019
};
10271020

1028-
let Some((regex, replacement)) = parse_normalize_rule(raw_value) else {
1029-
error!("couldn't parse custom normalization rule: `{raw_directive}`");
1030-
help!("expected syntax is: `{directive_name}: \"REGEX\" -> \"REPLACEMENT\"`");
1021+
let Some((regex, replacement)) = line.value_after_colon().and_then(parse_normalize_rule)
1022+
else {
1023+
error!("couldn't parse custom normalization rule: `{}`", line.display());
1024+
help!("expected syntax is: `{name}: \"REGEX\" -> \"REPLACEMENT\"`");
10311025
panic!("invalid normalization rule detected");
10321026
};
10331027
Some(NormalizeRule { kind, regex, replacement })
10341028
}
10351029

10361030
fn parse_name_directive(&self, line: &DirectiveLine<'_>, directive: &str) -> bool {
1037-
let &DirectiveLine { raw_directive: line, .. } = line;
1038-
1039-
// Ensure the directive is a whole word. Do not match "ignore-x86" when
1040-
// the line says "ignore-x86_64".
1041-
line.starts_with(directive)
1042-
&& matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':'))
1031+
// FIXME(Zalathar): Ideally, this should raise an error if a name-only
1032+
// directive is followed by a colon, since that's the wrong syntax.
1033+
// But we would need to fix tests that rely on the current behaviour.
1034+
line.name == directive
10431035
}
10441036

10451037
fn parse_name_value_directive(
@@ -1048,22 +1040,26 @@ impl Config {
10481040
directive: &str,
10491041
testfile: &Utf8Path,
10501042
) -> Option<String> {
1051-
let &DirectiveLine { line_number, raw_directive: line, .. } = line;
1052-
1053-
let colon = directive.len();
1054-
if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
1055-
let value = line[(colon + 1)..].to_owned();
1056-
debug!("{}: {}", directive, value);
1057-
let value = expand_variables(value, self);
1058-
if value.is_empty() {
1059-
error!("{testfile}:{line_number}: empty value for directive `{directive}`");
1060-
help!("expected syntax is: `{directive}: value`");
1061-
panic!("empty directive value detected");
1062-
}
1063-
Some(value)
1064-
} else {
1065-
None
1043+
let &DirectiveLine { line_number, .. } = line;
1044+
1045+
if line.name != directive {
1046+
return None;
1047+
};
1048+
1049+
// FIXME(Zalathar): This silently discards directives with a matching
1050+
// name but no colon. Unfortunately, some directives (e.g. "pp-exact")
1051+
// currently rely on _not_ panicking here.
1052+
let value = line.value_after_colon()?;
1053+
debug!("{}: {}", directive, value);
1054+
let value = expand_variables(value.to_owned(), self);
1055+
1056+
if value.is_empty() {
1057+
error!("{testfile}:{line_number}: empty value for directive `{directive}`");
1058+
help!("expected syntax is: `{directive}: value`");
1059+
panic!("empty directive value detected");
10661060
}
1061+
1062+
Some(value)
10671063
}
10681064

10691065
fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) {
@@ -1453,14 +1449,14 @@ pub(crate) fn make_test_description<R: Read>(
14531449
}
14541450

14551451
fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
1456-
let &DirectiveLine { raw_directive: line, .. } = line;
1457-
14581452
if config.debugger != Some(Debugger::Cdb) {
14591453
return IgnoreDecision::Continue;
14601454
}
14611455

14621456
if let Some(actual_version) = config.cdb_version {
1463-
if let Some(rest) = line.strip_prefix("min-cdb-version:").map(str::trim) {
1457+
if line.name == "min-cdb-version"
1458+
&& let Some(rest) = line.value_after_colon().map(str::trim)
1459+
{
14641460
let min_version = extract_cdb_version(rest).unwrap_or_else(|| {
14651461
panic!("couldn't parse version range: {:?}", rest);
14661462
});
@@ -1478,14 +1474,14 @@ fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
14781474
}
14791475

14801476
fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
1481-
let &DirectiveLine { raw_directive: line, .. } = line;
1482-
14831477
if config.debugger != Some(Debugger::Gdb) {
14841478
return IgnoreDecision::Continue;
14851479
}
14861480

14871481
if let Some(actual_version) = config.gdb_version {
1488-
if let Some(rest) = line.strip_prefix("min-gdb-version:").map(str::trim) {
1482+
if line.name == "min-gdb-version"
1483+
&& let Some(rest) = line.value_after_colon().map(str::trim)
1484+
{
14891485
let (start_ver, end_ver) = extract_version_range(rest, extract_gdb_version)
14901486
.unwrap_or_else(|| {
14911487
panic!("couldn't parse version range: {:?}", rest);
@@ -1501,7 +1497,9 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
15011497
reason: format!("ignored when the GDB version is lower than {rest}"),
15021498
};
15031499
}
1504-
} else if let Some(rest) = line.strip_prefix("ignore-gdb-version:").map(str::trim) {
1500+
} else if line.name == "ignore-gdb-version"
1501+
&& let Some(rest) = line.value_after_colon().map(str::trim)
1502+
{
15051503
let (min_version, max_version) = extract_version_range(rest, extract_gdb_version)
15061504
.unwrap_or_else(|| {
15071505
panic!("couldn't parse version range: {:?}", rest);
@@ -1528,14 +1526,14 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
15281526
}
15291527

15301528
fn ignore_lldb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
1531-
let &DirectiveLine { raw_directive: line, .. } = line;
1532-
15331529
if config.debugger != Some(Debugger::Lldb) {
15341530
return IgnoreDecision::Continue;
15351531
}
15361532

15371533
if let Some(actual_version) = config.lldb_version {
1538-
if let Some(rest) = line.strip_prefix("min-lldb-version:").map(str::trim) {
1534+
if line.name == "min-lldb-version"
1535+
&& let Some(rest) = line.value_after_colon().map(str::trim)
1536+
{
15391537
let min_version = rest.parse().unwrap_or_else(|e| {
15401538
panic!("Unexpected format of LLDB version string: {}\n{:?}", rest, e);
15411539
});

src/tools/compiletest/src/directives/auxiliary.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ pub(super) fn parse_and_update_aux(
5050
testfile: &Utf8Path,
5151
aux: &mut AuxProps,
5252
) {
53-
let &DirectiveLine { raw_directive: ln, .. } = directive_line;
54-
55-
if !(ln.starts_with("aux-") || ln.starts_with("proc-macro")) {
53+
if !(directive_line.name.starts_with("aux-") || directive_line.name == "proc-macro") {
5654
return;
5755
}
5856

src/tools/compiletest/src/directives/cfg.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use crate::directives::{DirectiveLine, IgnoreDecision};
66
const EXTRA_ARCHS: &[&str] = &["spirv"];
77

88
pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
9-
let parsed = parse_cfg_name_directive(config, line, "ignore");
10-
let &DirectiveLine { raw_directive: line, .. } = line;
9+
let parsed = parse_cfg_name_directive(config, line, "ignore-");
10+
let line = line.display();
1111

1212
match parsed.outcome {
1313
MatchOutcome::NoMatch => IgnoreDecision::Continue,
@@ -24,8 +24,8 @@ pub(super) fn handle_ignore(config: &Config, line: &DirectiveLine<'_>) -> Ignore
2424
}
2525

2626
pub(super) fn handle_only(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
27-
let parsed = parse_cfg_name_directive(config, line, "only");
28-
let &DirectiveLine { raw_directive: line, .. } = line;
27+
let parsed = parse_cfg_name_directive(config, line, "only-");
28+
let line = line.display();
2929

3030
match parsed.outcome {
3131
MatchOutcome::Match => IgnoreDecision::Continue,
@@ -50,18 +50,14 @@ fn parse_cfg_name_directive<'a>(
5050
line: &'a DirectiveLine<'a>,
5151
prefix: &str,
5252
) -> ParsedNameDirective<'a> {
53-
let &DirectiveLine { raw_directive: line, .. } = line;
54-
55-
if !line.as_bytes().starts_with(prefix.as_bytes()) {
56-
return ParsedNameDirective::not_a_directive();
57-
}
58-
if line.as_bytes().get(prefix.len()) != Some(&b'-') {
53+
let Some(name) = line.name.strip_prefix(prefix) else {
5954
return ParsedNameDirective::not_a_directive();
60-
}
61-
let line = &line[prefix.len() + 1..];
55+
};
6256

63-
let (name, comment) =
64-
line.split_once(&[':', ' ']).map(|(l, c)| (l, Some(c))).unwrap_or((line, None));
57+
// FIXME(Zalathar): This currently allows either a space or a colon, and
58+
// treats any "value" after a colon as though it were a remark.
59+
// We should instead forbid the colon syntax for these directives.
60+
let comment = line.remark_after_space().or_else(|| line.value_after_colon());
6561

6662
// Some of the matchers might be "" depending on what the target information is. To avoid
6763
// problems we outright reject empty directives.
@@ -269,7 +265,7 @@ fn parse_cfg_name_directive<'a>(
269265
message: "when performing tests on dist toolchain"
270266
}
271267

272-
if prefix == "ignore" && outcome == MatchOutcome::Invalid {
268+
if prefix == "ignore-" && outcome == MatchOutcome::Invalid {
273269
// Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest.
274270
if name.starts_with("tidy-") {
275271
outcome = MatchOutcome::External;

src/tools/compiletest/src/directives/line.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#![expect(dead_code)] // (removed later in this PR)
2-
31
use std::fmt;
42

53
const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@";
@@ -66,7 +64,7 @@ pub(crate) struct DirectiveLine<'ln> {
6664
///
6765
/// This is "raw" because the directive's name and colon-separated value
6866
/// (if present) have not yet been extracted or checked.
69-
pub(crate) raw_directive: &'ln str,
67+
raw_directive: &'ln str,
7068

7169
/// Name of the directive.
7270
///

src/tools/compiletest/src/directives/needs.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,17 +181,10 @@ pub(super) fn handle_needs(
181181
},
182182
];
183183

184-
let &DirectiveLine { raw_directive: ln, .. } = ln;
184+
let &DirectiveLine { name, .. } = ln;
185185

186-
let (name, rest) = match ln.split_once([':', ' ']) {
187-
Some((name, rest)) => (name, Some(rest)),
188-
None => (ln, None),
189-
};
190-
191-
// FIXME(jieyouxu): tighten up this parsing to reject using both `:` and ` ` as means to
192-
// delineate value.
193186
if name == "needs-target-has-atomic" {
194-
let Some(rest) = rest else {
187+
let Some(rest) = ln.value_after_colon() else {
195188
return IgnoreDecision::Error {
196189
message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(),
197190
};
@@ -233,7 +226,7 @@ pub(super) fn handle_needs(
233226

234227
// FIXME(jieyouxu): share multi-value directive logic with `needs-target-has-atomic` above.
235228
if name == "needs-crate-type" {
236-
let Some(rest) = rest else {
229+
let Some(rest) = ln.value_after_colon() else {
237230
return IgnoreDecision::Error {
238231
message:
239232
"expected `needs-crate-type` to have a comma-separated list of crate types"
@@ -292,7 +285,7 @@ pub(super) fn handle_needs(
292285
break;
293286
} else {
294287
return IgnoreDecision::Ignore {
295-
reason: if let Some(comment) = rest {
288+
reason: if let Some(comment) = ln.remark_after_space() {
296289
format!("{} ({})", need.ignore_reason, comment.trim())
297290
} else {
298291
need.ignore_reason.into()

src/tools/compiletest/src/directives/tests.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ use std::io::Read;
33
use camino::Utf8Path;
44
use semver::Version;
55

6-
use super::{
6+
use crate::common::{Config, Debugger, TestMode};
7+
use crate::directives::{
78
DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version,
8-
extract_version_range, iter_directives, parse_normalize_rule,
9+
extract_version_range, iter_directives, line_directive, parse_edition, parse_normalize_rule,
910
};
10-
use crate::common::{Config, Debugger, TestMode};
11-
use crate::directives::parse_edition;
1211
use crate::executor::{CollectedTestDesc, ShouldPanic};
1312

1413
fn make_test_description<R: Read>(
@@ -955,7 +954,9 @@ fn test_needs_target_std() {
955954

956955
fn parse_edition_range(line: &str) -> Option<EditionRange> {
957956
let config = cfg().build();
958-
let line = super::DirectiveLine { line_number: 0, revision: None, raw_directive: line };
957+
958+
let line_with_comment = format!("//@ {line}");
959+
let line = line_directive(0, &line_with_comment).unwrap();
959960

960961
super::parse_edition_range(&config, &line, "tmp.rs".into())
961962
}

0 commit comments

Comments
 (0)