Skip to content

Commit 3cb177a

Browse files
committed
Rework directive checks and add tests
1 parent 652c991 commit 3cb177a

File tree

7 files changed

+105
-76
lines changed

7 files changed

+105
-76
lines changed

src/tools/compiletest/src/header.rs

+50-76
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
1414
use crate::header::cfg::parse_cfg_name_directive;
1515
use crate::header::cfg::MatchOutcome;
1616
use crate::header::needs::CachedNeedsConditions;
17-
use crate::util::find_best_match_for_name;
17+
use crate::util::edit_distance::find_best_match_for_name;
1818
use crate::{extract_cdb_version, extract_gdb_version};
1919

2020
mod cfg;
@@ -673,8 +673,8 @@ pub fn line_directive<'line>(
673673
/// This is generated by collecting directives from ui tests and then extracting their directive
674674
/// names. This is **not** an exhaustive list of all possible directives. Instead, this is a
675675
/// best-effort approximation for diagnostics.
676-
// tidy-alphabetical-start
677676
const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
677+
// tidy-alphabetical-start
678678
"assembly-output",
679679
"aux-build",
680680
"aux-crate",
@@ -871,8 +871,8 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
871871
"unit-test",
872872
"unset-exec-env",
873873
"unset-rustc-env",
874+
// tidy-alphabetical-end
874875
];
875-
// tidy-alphabetical-end
876876

877877
/// The broken-down contents of a line containing a test header directive,
878878
/// which [`iter_header`] passes to its callback function.
@@ -902,6 +902,22 @@ struct HeaderLine<'ln> {
902902
directive: &'ln str,
903903
}
904904

905+
pub(crate) struct CheckDirectiveResult<'ln> {
906+
is_known_directive: bool,
907+
directive_name: &'ln str,
908+
}
909+
910+
// Returns `(is_known_directive, directive_name)`.
911+
pub(crate) fn check_directive(directive_ln: &str) -> CheckDirectiveResult<'_> {
912+
let directive_name =
913+
directive_ln.split_once([':', ' ']).map(|(pre, _)| pre).unwrap_or(directive_ln);
914+
915+
CheckDirectiveResult {
916+
is_known_directive: KNOWN_DIRECTIVE_NAMES.contains(&directive_name),
917+
directive_name: directive_ln,
918+
}
919+
}
920+
905921
fn iter_header(
906922
mode: Mode,
907923
_suite: &str,
@@ -941,6 +957,7 @@ fn iter_header(
941957
let mut ln = String::new();
942958
let mut line_number = 0;
943959

960+
// Match on error annotations like `//~ERROR`.
944961
static REVISION_MAGIC_COMMENT_RE: Lazy<Regex> =
945962
Lazy::new(|| Regex::new("//(\\[.*\\])?~.*").unwrap());
946963

@@ -959,33 +976,19 @@ fn iter_header(
959976
if ln.starts_with("fn") || ln.starts_with("mod") {
960977
return;
961978

962-
// First try to accept `ui_test` style comments
963-
} else if let Some((header_revision, original_directive_line)) = line_directive(comment, ln)
979+
// First try to accept `ui_test` style comments (`//@`)
980+
} else if let Some((header_revision, non_revisioned_directive_line)) =
981+
line_directive(comment, ln)
964982
{
965-
// Let Makefiles and non-rs files just get handled in the regular way.
966-
if testfile.extension().map(|e| e != "rs").unwrap_or(true) {
967-
it(HeaderLine {
968-
line_number,
969-
original_line,
970-
header_revision,
971-
directive: original_directive_line,
972-
});
973-
continue;
974-
}
983+
// Perform unknown directive check on Rust files.
984+
if testfile.extension().map(|e| e == "rs").unwrap_or(false) {
985+
let directive_ln = non_revisioned_directive_line.trim();
975986

976-
let directive_ln = original_directive_line.trim();
987+
let CheckDirectiveResult { is_known_directive, directive_name } = check_directive(directive_ln);
977988

978-
if let Some((directive_name, _)) = directive_ln.split_once([':', ' ']) {
979-
if KNOWN_DIRECTIVE_NAMES.contains(&directive_name) {
980-
it(HeaderLine {
981-
line_number,
982-
original_line,
983-
header_revision,
984-
directive: original_directive_line,
985-
});
986-
continue;
987-
} else {
989+
if !is_known_directive {
988990
*poisoned = true;
991+
989992
eprintln!(
990993
"error: detected unknown compiletest test directive `{}` in {}:{}",
991994
directive_ln,
@@ -998,32 +1001,19 @@ fn iter_header(
9981001
{
9991002
eprintln!("help: did you mean `{}` instead?", suggestion);
10001003
}
1001-
return;
1002-
}
1003-
} else if KNOWN_DIRECTIVE_NAMES.contains(&directive_ln) {
1004-
it(HeaderLine {
1005-
line_number,
1006-
original_line,
1007-
header_revision,
1008-
directive: original_directive_line,
1009-
});
1010-
continue;
1011-
} else {
1012-
*poisoned = true;
1013-
eprintln!(
1014-
"error: detected unknown compiletest test directive `{}` in {}:{}",
1015-
directive_ln,
1016-
testfile.display(),
1017-
line_number,
1018-
);
10191004

1020-
if let Some(suggestion) =
1021-
find_best_match_for_name(KNOWN_DIRECTIVE_NAMES, directive_ln, None)
1022-
{
1023-
eprintln!("help: did you mean `{}` instead?", suggestion);
1005+
return;
10241006
}
1025-
return;
10261007
}
1008+
1009+
it(HeaderLine {
1010+
line_number,
1011+
original_line,
1012+
header_revision,
1013+
directive: non_revisioned_directive_line,
1014+
});
1015+
// Then we try to check for legacy-style candidates, which are not the magic ~ERROR family
1016+
// error annotations.
10271017
} else if !REVISION_MAGIC_COMMENT_RE.is_match(ln) {
10281018
let Some((_, rest)) = line_directive("//", ln) else {
10291019
continue;
@@ -1037,34 +1027,18 @@ fn iter_header(
10371027

10381028
let rest = rest.trim_start();
10391029

1040-
for candidate in KNOWN_DIRECTIVE_NAMES.iter() {
1041-
if rest.starts_with(candidate) {
1042-
let Some(prefix_removed) = rest.strip_prefix(candidate) else {
1043-
// We have a comment that's *successfully* parsed as an legacy-style
1044-
// directive. We emit an error here to warn the user.
1045-
*poisoned = true;
1046-
eprintln!(
1047-
"error: detected legacy-style directives in compiletest test: {}:{}, please use `ui_test`-style directives `//@` instead:{:#?}",
1048-
testfile.display(),
1049-
line_number,
1050-
line_directive("//", ln),
1051-
);
1052-
return;
1053-
};
1030+
let CheckDirectiveResult { is_known_directive, directive_name } = check_directive(rest);
10541031

1055-
if prefix_removed.starts_with([' ', ':']) {
1056-
// We have a comment that's *successfully* parsed as an legacy-style
1057-
// directive. We emit an error here to warn the user.
1058-
*poisoned = true;
1059-
eprintln!(
1060-
"error: detected legacy-style directives in compiletest test: {}:{}, please use `ui_test`-style directives `//@` instead:{:#?}",
1061-
testfile.display(),
1062-
line_number,
1063-
line_directive("//", ln),
1064-
);
1065-
return;
1066-
}
1067-
}
1032+
if is_known_directive {
1033+
*poisoned = true;
1034+
eprintln!(
1035+
"error: detected legacy-style directive {} in compiletest test: {}:{}, please use `ui_test`-style directives `//@` instead: {:#?}",
1036+
directive_name,
1037+
testfile.display(),
1038+
line_number,
1039+
line_directive("//", ln),
1040+
);
1041+
return;
10681042
}
10691043
}
10701044
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//@ check-pass
2+
3+
//~ HELP
4+
fn main() { } //~ERROR
5+
//~^ ERROR
6+
//~| ERROR
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//! ignore-wasm
2+
//@ ignore-wasm
3+
//@ check-pass
4+
// regular comment
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// ignore-wasm
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# ignore-owo
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//@ needs-headpat

src/tools/compiletest/src/header/tests.rs

+42
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::str::FromStr;
55
use crate::common::{Config, Debugger, Mode};
66
use crate::header::{parse_normalization_string, EarlyProps, HeadersCache};
77

8+
use super::iter_header;
9+
810
fn make_test_description<R: Read>(
911
config: &Config,
1012
name: test::TestName,
@@ -582,3 +584,43 @@ fn ignore_mode() {
582584
assert!(!check_ignore(&config, &format!("//@ ignore-mode-{other}")));
583585
}
584586
}
587+
588+
fn run_path(poisoned: &mut bool, path: &Path, buf: &[u8]) {
589+
let rdr = std::io::Cursor::new(&buf);
590+
iter_header(Mode::Ui, "ui", poisoned, path, rdr, &mut |_| {});
591+
}
592+
593+
#[test]
594+
fn test_unknown_directive_check() {
595+
let mut poisoned = false;
596+
run_path(&mut poisoned, Path::new("a.rs"), include_bytes!("./directive_checks/unknown_directive.rs"));
597+
assert!(poisoned);
598+
}
599+
600+
#[test]
601+
fn test_known_legacy_directive_check() {
602+
let mut poisoned = false;
603+
run_path(&mut poisoned, Path::new("a.rs"), include_bytes!("./directive_checks/known_legacy_directive.rs"));
604+
assert!(poisoned);
605+
}
606+
607+
#[test]
608+
fn test_known_directive_check_no_error() {
609+
let mut poisoned = false;
610+
run_path(&mut poisoned, Path::new("a.rs"), include_bytes!("./directive_checks/known_directive.rs"));
611+
assert!(!poisoned);
612+
}
613+
614+
#[test]
615+
fn test_error_annotation_no_error() {
616+
let mut poisoned = false;
617+
run_path(&mut poisoned, Path::new("a.rs"), include_bytes!("./directive_checks/error_annotation.rs"));
618+
assert!(!poisoned);
619+
}
620+
621+
#[test]
622+
fn test_non_rs_unknown_directive_not_checked() {
623+
let mut poisoned = false;
624+
run_path(&mut poisoned, Path::new("a.Makefile"), include_bytes!("./directive_checks/not_rs.Makefile"));
625+
assert!(!poisoned);
626+
}

0 commit comments

Comments
 (0)