1use std::collections::BTreeSet;
5use std::ffi::OsStr;
6use std::fs;
7use std::io::Write;
8use std::path::{Path, PathBuf};
9
10const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
11 "rs", "stderr", "svg", "stdout", "fixed", "md", "ftl", ];
19
20const EXTENSION_EXCEPTION_PATHS: &[&str] = &[
21 "tests/ui/asm/named-asm-labels.s", "tests/ui/codegen/mismatched-data-layout.json", "tests/ui/check-cfg/my-awesome-platform.json", "tests/ui/argfile/commandline-argfile-badutf8.args", "tests/ui/argfile/commandline-argfile.args", "tests/ui/crate-loading/auxiliary/libfoo.rlib", "tests/ui/include-macros/data.bin", "tests/ui/include-macros/file.txt", "tests/ui/macros/macro-expanded-include/file.txt", "tests/ui/macros/not-utf8.bin", "tests/ui/macros/syntax-extension-source-utils-files/includeme.fragment", "tests/ui/proc-macro/auxiliary/included-file.txt", "tests/ui/unpretty/auxiliary/data.txt", "tests/ui/invalid/foo.natvis.xml", "tests/ui/sanitizer/dataflow-abilist.txt", "tests/ui/shell-argfiles/shell-argfiles.args", "tests/ui/shell-argfiles/shell-argfiles-badquotes.args", "tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args", "tests/ui/shell-argfiles/shell-argfiles-via-argfile.args", "tests/ui/std/windows-bat-args1.bat", "tests/ui/std/windows-bat-args2.bat", "tests/ui/std/windows-bat-args3.bat", ];
44
45pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
46 let issues_txt_header = r#"============================================================
47 ⚠️⚠️⚠️NOTHING SHOULD EVER BE ADDED TO THIS LIST⚠️⚠️⚠️
48============================================================
49"#;
50
51 let path = &root_path.join("tests");
52
53 let mut prev_line = "";
56 let mut is_sorted = true;
57 let allowed_issue_names: BTreeSet<_> = include_str!("issues.txt")
58 .strip_prefix(issues_txt_header)
59 .unwrap()
60 .lines()
61 .inspect(|&line| {
62 if prev_line > line {
63 is_sorted = false;
64 }
65
66 prev_line = line;
67 })
68 .collect();
69
70 if !is_sorted && !bless {
71 tidy_error!(
72 bad,
73 "`src/tools/tidy/src/issues.txt` is not in order, mostly because you modified it manually,
74 please only update it with command `x test tidy --bless`"
75 );
76 }
77
78 let mut remaining_issue_names: BTreeSet<&str> = allowed_issue_names.clone();
79
80 let (ui, ui_fulldeps) = (path.join("ui"), path.join("ui-fulldeps"));
81 let paths = [ui.as_path(), ui_fulldeps.as_path()];
82 crate::walk::walk_no_read(&paths, |_, _| false, &mut |entry| {
83 let file_path = entry.path();
84 if let Some(ext) = file_path.extension().and_then(OsStr::to_str) {
85 if !(EXPECTED_TEST_FILE_EXTENSIONS.contains(&ext)
88 || EXTENSION_EXCEPTION_PATHS.iter().any(|path| file_path.ends_with(path)))
89 {
90 tidy_error!(bad, "file {} has unexpected extension {}", file_path.display(), ext);
91 }
92
93 let testname =
96 file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0;
97 if ext == "stderr" || ext == "stdout" || ext == "fixed" {
98 if !file_path.with_file_name(testname).with_extension("rs").exists()
110 && !testname.contains("ignore-tidy")
111 {
112 tidy_error!(bad, "Stray file with UI testing output: {:?}", file_path);
113 }
114
115 if let Ok(metadata) = fs::metadata(file_path)
116 && metadata.len() == 0
117 {
118 tidy_error!(bad, "Empty file with UI testing output: {:?}", file_path);
119 }
120 }
121
122 if ext == "rs"
123 && let Some(test_name) = static_regex!(r"^issues?[-_]?(\d{3,})").captures(testname)
124 {
125 let stripped_path = file_path
127 .strip_prefix(path)
128 .unwrap()
129 .to_str()
130 .unwrap()
131 .replace(std::path::MAIN_SEPARATOR_STR, "/");
132
133 if !remaining_issue_names.remove(stripped_path.as_str())
134 && !stripped_path.starts_with("ui/issues/")
135 {
136 tidy_error!(
137 bad,
138 "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`",
139 issue_n = &test_name[1],
140 );
141 }
142 }
143 }
144 });
145
146 if bless && (!remaining_issue_names.is_empty() || !is_sorted) {
150 let tidy_src = root_path.join("src/tools/tidy/src");
151 let blessed_issues_path = tidy_src.join("issues_blessed.txt");
154 let mut blessed_issues_txt = fs::File::create(&blessed_issues_path).unwrap();
155 blessed_issues_txt.write_all(issues_txt_header.as_bytes()).unwrap();
156 for filename in allowed_issue_names.difference(&remaining_issue_names) {
158 writeln!(blessed_issues_txt, "{filename}").unwrap();
159 }
160 let old_issues_path = tidy_src.join("issues.txt");
161 fs::rename(blessed_issues_path, old_issues_path).unwrap();
162 } else {
163 for file_name in remaining_issue_names {
164 let mut p = PathBuf::from(path);
165 p.push(file_name);
166 tidy_error!(
167 bad,
168 "file `{}` no longer exists and should be removed from the exclusions in `src/tools/tidy/src/issues.txt`",
169 p.display()
170 );
171 }
172 }
173}