Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ca18f54

Browse files
authoredApr 13, 2025
Rollup merge of #139695 - jieyouxu:compiletest-utf8path, r=Kobzol
compiletest: consistently use `camino::{Utf8Path,Utf8PathBuf}` throughout compiletest already practically assumes UTF-8 paths everywhere. Use `camino`'s `{Utf8Path,Utf8PathBuf}` consistently throughout compiletest to enforce UTF-8 path assumptions. r? `@Kobzol` (or compiler/bootstrap)
2 parents 20684e9 + c003957 commit ca18f54

File tree

24 files changed

+411
-433
lines changed

24 files changed

+411
-433
lines changed
 

‎Cargo.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,7 @@ version = "0.0.0"
719719
dependencies = [
720720
"anstyle-svg",
721721
"build_helper",
722+
"camino",
722723
"colored",
723724
"diff",
724725
"getopts",
@@ -4671,6 +4672,7 @@ name = "rustdoc-gui-test"
46714672
version = "0.1.0"
46724673
dependencies = [
46734674
"build_helper",
4675+
"camino",
46744676
"compiletest",
46754677
"getopts",
46764678
"walkdir",

‎src/tools/compiletest/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ doctest = false
1010
# tidy-alphabetical-start
1111
anstyle-svg = "0.1.3"
1212
build_helper = { path = "../../build_helper" }
13+
camino = "1"
1314
colored = "2"
1415
diff = "0.1.10"
1516
getopts = "0.2"

‎src/tools/compiletest/src/common.rs

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
use std::collections::{BTreeSet, HashMap, HashSet};
2-
use std::ffi::OsString;
3-
use std::path::{Path, PathBuf};
42
use std::process::Command;
53
use std::str::FromStr;
64
use std::sync::OnceLock;
75
use std::{fmt, iter};
86

97
use build_helper::git::GitConfig;
8+
use camino::{Utf8Path, Utf8PathBuf};
109
use semver::Version;
1110
use serde::de::{Deserialize, Deserializer, Error as _};
1211

1312
pub use self::Mode::*;
1413
use crate::executor::{ColorConfig, OutputFormat};
15-
use crate::util::{PathBufExt, add_dylib_path};
14+
use crate::util::{Utf8PathBufExt, add_dylib_path};
1615

1716
macro_rules! string_enum {
1817
($(#[$meta:meta])* $vis:vis enum $name:ident { $($variant:ident => $repr:expr,)* }) => {
@@ -183,25 +182,25 @@ pub struct Config {
183182
pub fail_fast: bool,
184183

185184
/// The library paths required for running the compiler.
186-
pub compile_lib_path: PathBuf,
185+
pub compile_lib_path: Utf8PathBuf,
187186

188187
/// The library paths required for running compiled programs.
189-
pub run_lib_path: PathBuf,
188+
pub run_lib_path: Utf8PathBuf,
190189

191190
/// The rustc executable.
192-
pub rustc_path: PathBuf,
191+
pub rustc_path: Utf8PathBuf,
193192

194193
/// The cargo executable.
195-
pub cargo_path: Option<PathBuf>,
194+
pub cargo_path: Option<Utf8PathBuf>,
196195

197196
/// Rustc executable used to compile run-make recipes.
198-
pub stage0_rustc_path: Option<PathBuf>,
197+
pub stage0_rustc_path: Option<Utf8PathBuf>,
199198

200199
/// The rustdoc executable.
201-
pub rustdoc_path: Option<PathBuf>,
200+
pub rustdoc_path: Option<Utf8PathBuf>,
202201

203202
/// The coverage-dump executable.
204-
pub coverage_dump_path: Option<PathBuf>,
203+
pub coverage_dump_path: Option<Utf8PathBuf>,
205204

206205
/// The Python executable to use for LLDB and htmldocck.
207206
pub python: String,
@@ -213,27 +212,27 @@ pub struct Config {
213212
pub jsondoclint_path: Option<String>,
214213

215214
/// The LLVM `FileCheck` binary path.
216-
pub llvm_filecheck: Option<PathBuf>,
215+
pub llvm_filecheck: Option<Utf8PathBuf>,
217216

218217
/// Path to LLVM's bin directory.
219-
pub llvm_bin_dir: Option<PathBuf>,
218+
pub llvm_bin_dir: Option<Utf8PathBuf>,
220219

221220
/// The path to the Clang executable to run Clang-based tests with. If
222221
/// `None` then these tests will be ignored.
223222
pub run_clang_based_tests_with: Option<String>,
224223

225224
/// The directory containing the sources.
226-
pub src_root: PathBuf,
225+
pub src_root: Utf8PathBuf,
227226
/// The directory containing the test suite sources. Must be a subdirectory of `src_root`.
228-
pub src_test_suite_root: PathBuf,
227+
pub src_test_suite_root: Utf8PathBuf,
229228

230229
/// Root build directory (e.g. `build/`).
231-
pub build_root: PathBuf,
230+
pub build_root: Utf8PathBuf,
232231
/// Test suite specific build directory (e.g. `build/host/test/ui/`).
233-
pub build_test_suite_root: PathBuf,
232+
pub build_test_suite_root: Utf8PathBuf,
234233

235234
/// The directory containing the compiler sysroot
236-
pub sysroot_base: PathBuf,
235+
pub sysroot_base: Utf8PathBuf,
237236

238237
/// The number of the stage under test.
239238
pub stage: u32,
@@ -301,7 +300,7 @@ pub struct Config {
301300
pub host: String,
302301

303302
/// Path to / name of the Microsoft Console Debugger (CDB) executable
304-
pub cdb: Option<OsString>,
303+
pub cdb: Option<Utf8PathBuf>,
305304

306305
/// Version of CDB
307306
pub cdb_version: Option<[u16; 4]>,
@@ -322,7 +321,7 @@ pub struct Config {
322321
pub system_llvm: bool,
323322

324323
/// Path to the android tools
325-
pub android_cross_path: PathBuf,
324+
pub android_cross_path: Utf8PathBuf,
326325

327326
/// Extra parameter to run adb on arm-linux-androideabi
328327
pub adb_path: String,
@@ -346,7 +345,7 @@ pub struct Config {
346345
pub color: ColorConfig,
347346

348347
/// where to find the remote test client process, if we're using it
349-
pub remote_test_client: Option<PathBuf>,
348+
pub remote_test_client: Option<Utf8PathBuf>,
350349

351350
/// mode describing what file the actual ui output will be compared to
352351
pub compare_mode: Option<CompareMode>,
@@ -414,7 +413,7 @@ pub struct Config {
414413
/// Path to minicore aux library, used for `no_core` tests that need `core` stubs in
415414
/// cross-compilation scenarios that do not otherwise want/need to `-Zbuild-std`. Used in e.g.
416415
/// ABI tests.
417-
pub minicore_path: PathBuf,
416+
pub minicore_path: Utf8PathBuf,
418417
}
419418

420419
impl Config {
@@ -804,8 +803,8 @@ fn serde_parse_u32<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u32, D:
804803

805804
#[derive(Debug, Clone)]
806805
pub struct TestPaths {
807-
pub file: PathBuf, // e.g., compile-test/foo/bar/baz.rs
808-
pub relative_dir: PathBuf, // e.g., foo/bar
806+
pub file: Utf8PathBuf, // e.g., compile-test/foo/bar/baz.rs
807+
pub relative_dir: Utf8PathBuf, // e.g., foo/bar
809808
}
810809

811810
/// Used by `ui` tests to generate things like `foo.stderr` from `foo.rs`.
@@ -814,7 +813,7 @@ pub fn expected_output_path(
814813
revision: Option<&str>,
815814
compare_mode: &Option<CompareMode>,
816815
kind: &str,
817-
) -> PathBuf {
816+
) -> Utf8PathBuf {
818817
assert!(UI_EXTENSIONS.contains(&kind));
819818
let mut parts = Vec::new();
820819

@@ -865,7 +864,7 @@ pub const UI_COVERAGE_MAP: &str = "cov-map";
865864
/// ```
866865
///
867866
/// This is created early when tests are collected to avoid race conditions.
868-
pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf {
867+
pub fn output_relative_path(config: &Config, relative_dir: &Utf8Path) -> Utf8PathBuf {
869868
config.build_test_suite_root.join(relative_dir)
870869
}
871870

@@ -874,10 +873,10 @@ pub fn output_testname_unique(
874873
config: &Config,
875874
testpaths: &TestPaths,
876875
revision: Option<&str>,
877-
) -> PathBuf {
876+
) -> Utf8PathBuf {
878877
let mode = config.compare_mode.as_ref().map_or("", |m| m.to_str());
879878
let debugger = config.debugger.as_ref().map_or("", |m| m.to_str());
880-
PathBuf::from(&testpaths.file.file_stem().unwrap())
879+
Utf8PathBuf::from(&testpaths.file.file_stem().unwrap())
881880
.with_extra_extension(config.mode.output_dir_disambiguator())
882881
.with_extra_extension(revision.unwrap_or(""))
883882
.with_extra_extension(mode)
@@ -887,20 +886,32 @@ pub fn output_testname_unique(
887886
/// Absolute path to the directory where all output for the given
888887
/// test/revision should reside. Example:
889888
/// /path/to/build/host-tuple/test/ui/relative/testname.revision.mode/
890-
pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
889+
pub fn output_base_dir(
890+
config: &Config,
891+
testpaths: &TestPaths,
892+
revision: Option<&str>,
893+
) -> Utf8PathBuf {
891894
output_relative_path(config, &testpaths.relative_dir)
892895
.join(output_testname_unique(config, testpaths, revision))
893896
}
894897

895898
/// Absolute path to the base filename used as output for the given
896899
/// test/revision. Example:
897900
/// /path/to/build/host-tuple/test/ui/relative/testname.revision.mode/testname
898-
pub fn output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
901+
pub fn output_base_name(
902+
config: &Config,
903+
testpaths: &TestPaths,
904+
revision: Option<&str>,
905+
) -> Utf8PathBuf {
899906
output_base_dir(config, testpaths, revision).join(testpaths.file.file_stem().unwrap())
900907
}
901908

902909
/// Absolute path to the directory to use for incremental compilation. Example:
903910
/// /path/to/build/host-tuple/test/ui/relative/testname.mode/testname.inc
904-
pub fn incremental_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
911+
pub fn incremental_dir(
912+
config: &Config,
913+
testpaths: &TestPaths,
914+
revision: Option<&str>,
915+
) -> Utf8PathBuf {
905916
output_base_name(config, testpaths, revision).with_extension("inc")
906917
}

‎src/tools/compiletest/src/compute_diff.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::VecDeque;
22
use std::fs::{File, FileType};
3-
use std::path::Path;
3+
4+
use camino::Utf8Path;
45

56
#[derive(Debug, PartialEq)]
67
pub enum DiffLine {
@@ -112,8 +113,8 @@ pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> S
112113
/// Returns whether any data was actually written.
113114
pub(crate) fn write_filtered_diff<Filter>(
114115
diff_filename: &str,
115-
out_dir: &Path,
116-
compare_dir: &Path,
116+
out_dir: &Utf8Path,
117+
compare_dir: &Utf8Path,
117118
verbose: bool,
118119
filter: Filter,
119120
) -> bool
@@ -123,19 +124,21 @@ where
123124
use std::io::{Read, Write};
124125
let mut diff_output = File::create(diff_filename).unwrap();
125126
let mut wrote_data = false;
126-
for entry in walkdir::WalkDir::new(out_dir) {
127+
for entry in walkdir::WalkDir::new(out_dir.as_std_path()) {
127128
let entry = entry.expect("failed to read file");
128129
let extension = entry.path().extension().and_then(|p| p.to_str());
129130
if filter(entry.file_type(), extension) {
130-
let expected_path = compare_dir.join(entry.path().strip_prefix(&out_dir).unwrap());
131+
let expected_path = compare_dir
132+
.as_std_path()
133+
.join(entry.path().strip_prefix(&out_dir.as_std_path()).unwrap());
131134
let expected = if let Ok(s) = std::fs::read(&expected_path) { s } else { continue };
132135
let actual_path = entry.path();
133136
let actual = std::fs::read(&actual_path).unwrap();
134137
let diff = unified_diff::diff(
135138
&expected,
136-
&expected_path.to_string_lossy(),
139+
&expected_path.to_str().unwrap(),
137140
&actual,
138-
&actual_path.to_string_lossy(),
141+
&actual_path.to_str().unwrap(),
139142
3,
140143
);
141144
wrote_data |= !diff.is_empty();

‎src/tools/compiletest/src/debuggers.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::env;
2-
use std::ffi::OsString;
3-
use std::path::{Path, PathBuf};
42
use std::process::Command;
53
use std::sync::Arc;
64

5+
use camino::{Utf8Path, Utf8PathBuf};
6+
77
use crate::common::{Config, Debugger};
88

99
pub(crate) fn configure_cdb(config: &Config) -> Option<Arc<Config>> {
@@ -78,12 +78,15 @@ fn is_pc_windows_msvc_target(target: &str) -> bool {
7878
target.ends_with("-pc-windows-msvc")
7979
}
8080

81-
fn find_cdb(target: &str) -> Option<OsString> {
81+
fn find_cdb(target: &str) -> Option<Utf8PathBuf> {
8282
if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
8383
return None;
8484
}
8585

86-
let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
86+
let pf86 = Utf8PathBuf::from_path_buf(
87+
env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?.into(),
88+
)
89+
.unwrap();
8790
let cdb_arch = if cfg!(target_arch = "x86") {
8891
"x86"
8992
} else if cfg!(target_arch = "x86_64") {
@@ -96,8 +99,7 @@ fn find_cdb(target: &str) -> Option<OsString> {
9699
return None; // No compatible CDB.exe in the Windows 10 SDK
97100
};
98101

99-
let mut path = PathBuf::new();
100-
path.push(pf86);
102+
let mut path = pf86;
101103
path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
102104
path.push(cdb_arch);
103105
path.push(r"cdb.exe");
@@ -106,15 +108,15 @@ fn find_cdb(target: &str) -> Option<OsString> {
106108
return None;
107109
}
108110

109-
Some(path.into_os_string())
111+
Some(path)
110112
}
111113

112114
/// Returns Path to CDB
113115
pub(crate) fn analyze_cdb(
114116
cdb: Option<String>,
115117
target: &str,
116-
) -> (Option<OsString>, Option<[u16; 4]>) {
117-
let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
118+
) -> (Option<Utf8PathBuf>, Option<[u16; 4]>) {
119+
let cdb = cdb.map(Utf8PathBuf::from).or_else(|| find_cdb(target));
118120

119121
let mut version = None;
120122
if let Some(cdb) = cdb.as_ref() {
@@ -143,7 +145,7 @@ pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
143145
pub(crate) fn analyze_gdb(
144146
gdb: Option<String>,
145147
target: &str,
146-
android_cross_path: &Path,
148+
android_cross_path: &Utf8Path,
147149
) -> (Option<String>, Option<u32>) {
148150
#[cfg(not(windows))]
149151
const GDB_FALLBACK: &str = "gdb";
@@ -152,10 +154,7 @@ pub(crate) fn analyze_gdb(
152154

153155
let fallback_gdb = || {
154156
if is_android_gdb_target(target) {
155-
let mut gdb_path = match android_cross_path.to_str() {
156-
Some(x) => x.to_owned(),
157-
None => panic!("cannot find android cross path"),
158-
};
157+
let mut gdb_path = android_cross_path.to_string();
159158
gdb_path.push_str("/bin/gdb");
160159
gdb_path
161160
} else {

‎src/tools/compiletest/src/errors.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use std::fmt;
22
use std::fs::File;
33
use std::io::BufReader;
44
use std::io::prelude::*;
5-
use std::path::Path;
65
use std::sync::OnceLock;
76

7+
use camino::Utf8Path;
88
use regex::Regex;
99
use tracing::*;
1010

@@ -102,8 +102,8 @@ impl Error {
102102
///
103103
/// If revision is not None, then we look
104104
/// for `//[X]~` instead, where `X` is the current revision.
105-
pub fn load_errors(testfile: &Path, revision: Option<&str>) -> Vec<Error> {
106-
let rdr = BufReader::new(File::open(testfile).unwrap());
105+
pub fn load_errors(testfile: &Utf8Path, revision: Option<&str>) -> Vec<Error> {
106+
let rdr = BufReader::new(File::open(testfile.as_std_path()).unwrap());
107107

108108
// `last_nonfollow_error` tracks the most recently seen
109109
// line with an error template that did not use the

‎src/tools/compiletest/src/header.rs

Lines changed: 49 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use std::env;
33
use std::fs::File;
44
use std::io::BufReader;
55
use std::io::prelude::*;
6-
use std::path::{Path, PathBuf};
76
use std::process::Command;
87

8+
use camino::{Utf8Path, Utf8PathBuf};
99
use semver::Version;
1010
use tracing::*;
1111

@@ -45,12 +45,12 @@ pub struct EarlyProps {
4545
}
4646

4747
impl EarlyProps {
48-
pub fn from_file(config: &Config, testfile: &Path) -> Self {
49-
let file = File::open(testfile).expect("open test file to parse earlyprops");
48+
pub fn from_file(config: &Config, testfile: &Utf8Path) -> Self {
49+
let file = File::open(testfile.as_std_path()).expect("open test file to parse earlyprops");
5050
Self::from_reader(config, testfile, file)
5151
}
5252

53-
pub fn from_reader<R: Read>(config: &Config, testfile: &Path, rdr: R) -> Self {
53+
pub fn from_reader<R: Read>(config: &Config, testfile: &Utf8Path, rdr: R) -> Self {
5454
let mut props = EarlyProps::default();
5555
let mut poisoned = false;
5656
iter_header(
@@ -66,7 +66,7 @@ impl EarlyProps {
6666
);
6767

6868
if poisoned {
69-
eprintln!("errors encountered during EarlyProps parsing: {}", testfile.display());
69+
eprintln!("errors encountered during EarlyProps parsing: {}", testfile);
7070
panic!("errors encountered during EarlyProps parsing");
7171
}
7272

@@ -88,7 +88,7 @@ pub struct TestProps {
8888
pub doc_flags: Vec<String>,
8989
// If present, the name of a file that this test should match when
9090
// pretty-printed
91-
pub pp_exact: Option<PathBuf>,
91+
pub pp_exact: Option<Utf8PathBuf>,
9292
/// Auxiliary crates that should be built and made available to this test.
9393
pub(crate) aux: AuxProps,
9494
// Environment settings to use for compiling
@@ -134,7 +134,7 @@ pub struct TestProps {
134134
// not set by end-users; rather it is set by the incremental
135135
// testing harness and used when generating compilation
136136
// arguments. (In particular, it propagates to the aux-builds.)
137-
pub incremental_dir: Option<PathBuf>,
137+
pub incremental_dir: Option<Utf8PathBuf>,
138138
// If `true`, this test will use incremental compilation.
139139
//
140140
// This can be set manually with the `incremental` header, or implicitly
@@ -305,7 +305,12 @@ impl TestProps {
305305
}
306306
}
307307

308-
pub fn from_aux_file(&self, testfile: &Path, revision: Option<&str>, config: &Config) -> Self {
308+
pub fn from_aux_file(
309+
&self,
310+
testfile: &Utf8Path,
311+
revision: Option<&str>,
312+
config: &Config,
313+
) -> Self {
309314
let mut props = TestProps::new();
310315

311316
// copy over select properties to the aux build:
@@ -316,10 +321,10 @@ impl TestProps {
316321
props
317322
}
318323

319-
pub fn from_file(testfile: &Path, revision: Option<&str>, config: &Config) -> Self {
324+
pub fn from_file(testfile: &Utf8Path, revision: Option<&str>, config: &Config) -> Self {
320325
let mut props = TestProps::new();
321326
props.load_from(testfile, revision, config);
322-
props.exec_env.push(("RUSTC".to_string(), config.rustc_path.display().to_string()));
327+
props.exec_env.push(("RUSTC".to_string(), config.rustc_path.to_string()));
323328

324329
match (props.pass_mode, props.fail_mode) {
325330
(None, None) if config.mode == Mode::Ui => props.fail_mode = Some(FailMode::Check),
@@ -334,10 +339,10 @@ impl TestProps {
334339
/// tied to a particular revision `foo` (indicated by writing
335340
/// `//@[foo]`), then the property is ignored unless `test_revision` is
336341
/// `Some("foo")`.
337-
fn load_from(&mut self, testfile: &Path, test_revision: Option<&str>, config: &Config) {
342+
fn load_from(&mut self, testfile: &Utf8Path, test_revision: Option<&str>, config: &Config) {
338343
let mut has_edition = false;
339344
if !testfile.is_dir() {
340-
let file = File::open(testfile).unwrap();
345+
let file = File::open(testfile.as_std_path()).unwrap();
341346

342347
let mut poisoned = false;
343348

@@ -594,7 +599,7 @@ impl TestProps {
594599
);
595600

596601
if poisoned {
597-
eprintln!("errors encountered during TestProps parsing: {}", testfile.display());
602+
eprintln!("errors encountered during TestProps parsing: {}", testfile);
598603
panic!("errors encountered during TestProps parsing");
599604
}
600605
}
@@ -865,7 +870,7 @@ fn iter_header(
865870
mode: Mode,
866871
_suite: &str,
867872
poisoned: &mut bool,
868-
testfile: &Path,
873+
testfile: &Utf8Path,
869874
rdr: impl Read,
870875
it: &mut dyn FnMut(DirectiveLine<'_>),
871876
) {
@@ -917,9 +922,7 @@ fn iter_header(
917922

918923
eprintln!(
919924
"error: detected unknown compiletest test directive `{}` in {}:{}",
920-
directive_line.raw_directive,
921-
testfile.display(),
922-
line_number,
925+
directive_line.raw_directive, testfile, line_number,
923926
);
924927

925928
return;
@@ -931,10 +934,7 @@ fn iter_header(
931934
eprintln!(
932935
"error: detected trailing compiletest test directive `{}` in {}:{}\n \
933936
help: put the trailing directive in it's own line: `//@ {}`",
934-
trailing_directive,
935-
testfile.display(),
936-
line_number,
937-
trailing_directive,
937+
trailing_directive, testfile, line_number, trailing_directive,
938938
);
939939

940940
return;
@@ -946,7 +946,12 @@ fn iter_header(
946946
}
947947

948948
impl Config {
949-
fn parse_and_update_revisions(&self, testfile: &Path, line: &str, existing: &mut Vec<String>) {
949+
fn parse_and_update_revisions(
950+
&self,
951+
testfile: &Utf8Path,
952+
line: &str,
953+
existing: &mut Vec<String>,
954+
) {
950955
const FORBIDDEN_REVISION_NAMES: [&str; 2] = [
951956
// `//@ revisions: true false` Implying `--cfg=true` and `--cfg=false` makes it very
952957
// weird for the test, since if the test writer wants a cfg of the same revision name
@@ -959,26 +964,19 @@ impl Config {
959964

960965
if let Some(raw) = self.parse_name_value_directive(line, "revisions") {
961966
if self.mode == Mode::RunMake {
962-
panic!("`run-make` tests do not support revisions: {}", testfile.display());
967+
panic!("`run-make` tests do not support revisions: {}", testfile);
963968
}
964969

965970
let mut duplicates: HashSet<_> = existing.iter().cloned().collect();
966971
for revision in raw.split_whitespace() {
967972
if !duplicates.insert(revision.to_string()) {
968-
panic!(
969-
"duplicate revision: `{}` in line `{}`: {}",
970-
revision,
971-
raw,
972-
testfile.display()
973-
);
973+
panic!("duplicate revision: `{}` in line `{}`: {}", revision, raw, testfile);
974974
}
975975

976976
if FORBIDDEN_REVISION_NAMES.contains(&revision) {
977977
panic!(
978978
"revision name `{revision}` is not permitted: `{}` in line `{}`: {}",
979-
revision,
980-
raw,
981-
testfile.display()
979+
revision, raw, testfile
982980
);
983981
}
984982

@@ -989,8 +987,7 @@ impl Config {
989987
"revision name `{revision}` is not permitted in a test suite that uses \
990988
`FileCheck` annotations as it is confusing when used as custom `FileCheck` \
991989
prefix: `{revision}` in line `{}`: {}",
992-
raw,
993-
testfile.display()
990+
raw, testfile
994991
);
995992
}
996993

@@ -1010,11 +1007,11 @@ impl Config {
10101007
(name.to_owned(), value.to_owned())
10111008
}
10121009

1013-
fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option<PathBuf> {
1010+
fn parse_pp_exact(&self, line: &str, testfile: &Utf8Path) -> Option<Utf8PathBuf> {
10141011
if let Some(s) = self.parse_name_value_directive(line, "pp-exact") {
1015-
Some(PathBuf::from(&s))
1012+
Some(Utf8PathBuf::from(&s))
10161013
} else if self.parse_name_directive(line, "pp-exact") {
1017-
testfile.file_name().map(PathBuf::from)
1014+
testfile.file_name().map(Utf8PathBuf::from)
10181015
} else {
10191016
None
10201017
}
@@ -1120,20 +1117,19 @@ fn expand_variables(mut value: String, config: &Config) -> String {
11201117

11211118
if value.contains(CWD) {
11221119
let cwd = env::current_dir().unwrap();
1123-
value = value.replace(CWD, &cwd.to_string_lossy());
1120+
value = value.replace(CWD, &cwd.to_str().unwrap());
11241121
}
11251122

11261123
if value.contains(SRC_BASE) {
1127-
value = value.replace(SRC_BASE, &config.src_test_suite_root.to_str().unwrap());
1124+
value = value.replace(SRC_BASE, &config.src_test_suite_root.as_str());
11281125
}
11291126

11301127
if value.contains(TEST_SUITE_BUILD_BASE) {
1131-
value =
1132-
value.replace(TEST_SUITE_BUILD_BASE, &config.build_test_suite_root.to_str().unwrap());
1128+
value = value.replace(TEST_SUITE_BUILD_BASE, &config.build_test_suite_root.as_str());
11331129
}
11341130

11351131
if value.contains(SYSROOT_BASE) {
1136-
value = value.replace(SYSROOT_BASE, &config.sysroot_base.to_str().unwrap());
1132+
value = value.replace(SYSROOT_BASE, &config.sysroot_base.as_str());
11371133
}
11381134

11391135
if value.contains(TARGET_LINKER) {
@@ -1146,9 +1142,9 @@ fn expand_variables(mut value: String, config: &Config) -> String {
11461142

11471143
if value.contains(RUST_SRC_BASE) {
11481144
let src_base = config.sysroot_base.join("lib/rustlib/src/rust");
1149-
src_base.try_exists().expect(&*format!("{} should exists", src_base.display()));
1150-
let src_base = src_base.read_link().unwrap_or(src_base);
1151-
value = value.replace(RUST_SRC_BASE, &src_base.to_string_lossy());
1145+
src_base.try_exists().expect(&*format!("{} should exists", src_base));
1146+
let src_base = src_base.read_link_utf8().unwrap_or(src_base);
1147+
value = value.replace(RUST_SRC_BASE, &src_base.as_str());
11521148
}
11531149

11541150
value
@@ -1251,14 +1247,14 @@ pub fn llvm_has_libzstd(config: &Config) -> bool {
12511247
// contains a path to that static lib, and that it exists.
12521248
//
12531249
// See compiler/rustc_llvm/build.rs for more details and similar expectations.
1254-
fn is_zstd_in_config(llvm_bin_dir: &Path) -> Option<()> {
1250+
fn is_zstd_in_config(llvm_bin_dir: &Utf8Path) -> Option<()> {
12551251
let llvm_config_path = llvm_bin_dir.join("llvm-config");
12561252
let output = Command::new(llvm_config_path).arg("--system-libs").output().ok()?;
12571253
assert!(output.status.success(), "running llvm-config --system-libs failed");
12581254

12591255
let libs = String::from_utf8(output.stdout).ok()?;
12601256
for lib in libs.split_whitespace() {
1261-
if lib.ends_with("libzstd.a") && Path::new(lib).exists() {
1257+
if lib.ends_with("libzstd.a") && Utf8Path::new(lib).exists() {
12621258
return Some(());
12631259
}
12641260
}
@@ -1276,7 +1272,7 @@ pub fn llvm_has_libzstd(config: &Config) -> bool {
12761272
// `lld` supports it. If not, an error will be emitted: "LLVM was not built with
12771273
// LLVM_ENABLE_ZSTD or did not find zstd at build time".
12781274
#[cfg(unix)]
1279-
fn is_lld_built_with_zstd(llvm_bin_dir: &Path) -> Option<()> {
1275+
fn is_lld_built_with_zstd(llvm_bin_dir: &Utf8Path) -> Option<()> {
12801276
let lld_path = llvm_bin_dir.join("lld");
12811277
if lld_path.exists() {
12821278
// We can't call `lld` as-is, it expects to be invoked by a compiler driver using a
@@ -1379,7 +1375,7 @@ pub(crate) fn make_test_description<R: Read>(
13791375
config: &Config,
13801376
cache: &HeadersCache,
13811377
name: String,
1382-
path: &Path,
1378+
path: &Utf8Path,
13831379
src: R,
13841380
test_revision: Option<&str>,
13851381
poisoned: &mut bool,
@@ -1410,7 +1406,7 @@ pub(crate) fn make_test_description<R: Read>(
14101406
ignore_message = Some(reason.into());
14111407
}
14121408
IgnoreDecision::Error { message } => {
1413-
eprintln!("error: {}:{line_number}: {message}", path.display());
1409+
eprintln!("error: {}:{line_number}: {message}", path);
14141410
*poisoned = true;
14151411
return;
14161412
}
@@ -1440,7 +1436,7 @@ pub(crate) fn make_test_description<R: Read>(
14401436
);
14411437

14421438
if local_poisoned {
1443-
eprintln!("errors encountered when trying to make test description: {}", path.display());
1439+
eprintln!("errors encountered when trying to make test description: {}", path);
14441440
panic!("errors encountered when trying to make test description");
14451441
}
14461442

@@ -1549,7 +1545,7 @@ fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision {
15491545
IgnoreDecision::Continue
15501546
}
15511547

1552-
fn ignore_llvm(config: &Config, path: &Path, line: &str) -> IgnoreDecision {
1548+
fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision {
15531549
if let Some(needed_components) =
15541550
config.parse_name_value_directive(line, "needs-llvm-components")
15551551
{
@@ -1561,8 +1557,7 @@ fn ignore_llvm(config: &Config, path: &Path, line: &str) -> IgnoreDecision {
15611557
if env::var_os("COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS").is_some() {
15621558
panic!(
15631559
"missing LLVM component {}, and COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS is set: {}",
1564-
missing_component,
1565-
path.display()
1560+
missing_component, path
15661561
);
15671562
}
15681563
return IgnoreDecision::Ignore {

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::io::Read;
2-
use std::path::Path;
32

3+
use camino::Utf8Path;
44
use semver::Version;
55

66
use super::{
@@ -13,7 +13,7 @@ use crate::executor::{CollectedTestDesc, ShouldPanic};
1313
fn make_test_description<R: Read>(
1414
config: &Config,
1515
name: String,
16-
path: &Path,
16+
path: &Utf8Path,
1717
src: R,
1818
revision: Option<&str>,
1919
) -> CollectedTestDesc {
@@ -230,12 +230,12 @@ fn cfg() -> ConfigBuilder {
230230

231231
fn parse_rs(config: &Config, contents: &str) -> EarlyProps {
232232
let bytes = contents.as_bytes();
233-
EarlyProps::from_reader(config, Path::new("a.rs"), bytes)
233+
EarlyProps::from_reader(config, Utf8Path::new("a.rs"), bytes)
234234
}
235235

236236
fn check_ignore(config: &Config, contents: &str) -> bool {
237237
let tn = String::new();
238-
let p = Path::new("a.rs");
238+
let p = Utf8Path::new("a.rs");
239239
let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None);
240240
d.ignore
241241
}
@@ -244,7 +244,7 @@ fn check_ignore(config: &Config, contents: &str) -> bool {
244244
fn should_fail() {
245245
let config: Config = cfg().build();
246246
let tn = String::new();
247-
let p = Path::new("a.rs");
247+
let p = Utf8Path::new("a.rs");
248248

249249
let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None);
250250
assert_eq!(d.should_panic, ShouldPanic::No);
@@ -784,7 +784,7 @@ fn threads_support() {
784784
}
785785
}
786786

787-
fn run_path(poisoned: &mut bool, path: &Path, buf: &[u8]) {
787+
fn run_path(poisoned: &mut bool, path: &Utf8Path, buf: &[u8]) {
788788
let rdr = std::io::Cursor::new(&buf);
789789
iter_header(Mode::Ui, "ui", poisoned, path, rdr, &mut |_| {});
790790
}
@@ -794,7 +794,7 @@ fn test_unknown_directive_check() {
794794
let mut poisoned = false;
795795
run_path(
796796
&mut poisoned,
797-
Path::new("a.rs"),
797+
Utf8Path::new("a.rs"),
798798
include_bytes!("./test-auxillary/unknown_directive.rs"),
799799
);
800800
assert!(poisoned);
@@ -805,7 +805,7 @@ fn test_known_directive_check_no_error() {
805805
let mut poisoned = false;
806806
run_path(
807807
&mut poisoned,
808-
Path::new("a.rs"),
808+
Utf8Path::new("a.rs"),
809809
include_bytes!("./test-auxillary/known_directive.rs"),
810810
);
811811
assert!(!poisoned);
@@ -816,7 +816,7 @@ fn test_error_annotation_no_error() {
816816
let mut poisoned = false;
817817
run_path(
818818
&mut poisoned,
819-
Path::new("a.rs"),
819+
Utf8Path::new("a.rs"),
820820
include_bytes!("./test-auxillary/error_annotation.rs"),
821821
);
822822
assert!(!poisoned);
@@ -827,7 +827,7 @@ fn test_non_rs_unknown_directive_not_checked() {
827827
let mut poisoned = false;
828828
run_path(
829829
&mut poisoned,
830-
Path::new("a.Makefile"),
830+
Utf8Path::new("a.Makefile"),
831831
include_bytes!("./test-auxillary/not_rs.Makefile"),
832832
);
833833
assert!(!poisoned);
@@ -836,21 +836,21 @@ fn test_non_rs_unknown_directive_not_checked() {
836836
#[test]
837837
fn test_trailing_directive() {
838838
let mut poisoned = false;
839-
run_path(&mut poisoned, Path::new("a.rs"), b"//@ only-x86 only-arm");
839+
run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm");
840840
assert!(poisoned);
841841
}
842842

843843
#[test]
844844
fn test_trailing_directive_with_comment() {
845845
let mut poisoned = false;
846-
run_path(&mut poisoned, Path::new("a.rs"), b"//@ only-x86 only-arm with comment");
846+
run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ only-x86 only-arm with comment");
847847
assert!(poisoned);
848848
}
849849

850850
#[test]
851851
fn test_not_trailing_directive() {
852852
let mut poisoned = false;
853-
run_path(&mut poisoned, Path::new("a.rs"), b"//@ revisions: incremental");
853+
run_path(&mut poisoned, Utf8Path::new("a.rs"), b"//@ revisions: incremental");
854854
assert!(!poisoned);
855855
}
856856

‎src/tools/compiletest/src/lib.rs

Lines changed: 76 additions & 73 deletions
Large diffs are not rendered by default.

‎src/tools/compiletest/src/runtest.rs

Lines changed: 83 additions & 105 deletions
Large diffs are not rendered by default.

‎src/tools/compiletest/src/runtest/assembly.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::path::PathBuf;
1+
use camino::Utf8PathBuf;
22

33
use super::{AllowUnused, Emit, LinkToAux, ProcRes, TargetLocation, TestCx};
44

@@ -19,7 +19,7 @@ impl TestCx<'_> {
1919
}
2020
}
2121

22-
fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) {
22+
fn compile_test_and_save_assembly(&self) -> (ProcRes, Utf8PathBuf) {
2323
// This works with both `--emit asm` (as default output name for the assembly)
2424
// and `ptx-linker` because the latter can write output at requested location.
2525
let output_path = self.output_base_name().with_extension("s");

‎src/tools/compiletest/src/runtest/codegen_units.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ impl TestCx<'_> {
2626
.stdout
2727
.lines()
2828
.filter(|line| line.starts_with(PREFIX))
29-
.map(|line| {
30-
line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string()
31-
})
29+
.map(|line| line.replace(&self.testpaths.file.as_str(), "TEST_PATH").to_string())
3230
.map(|line| str_to_mono_item(&line, true))
3331
.collect();
3432

‎src/tools/compiletest/src/runtest/coverage.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
//! Code specific to the coverage test suites.
22
33
use std::ffi::OsStr;
4-
use std::path::{Path, PathBuf};
54
use std::process::Command;
65

6+
use camino::{Utf8Path, Utf8PathBuf};
77
use glob::glob;
88

99
use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP};
1010
use crate::runtest::{Emit, ProcRes, TestCx, WillExecute};
1111
use crate::util::static_regex;
1212

1313
impl<'test> TestCx<'test> {
14-
fn coverage_dump_path(&self) -> &Path {
14+
fn coverage_dump_path(&self) -> &Utf8Path {
1515
self.config
1616
.coverage_dump_path
1717
.as_deref()
@@ -79,10 +79,8 @@ impl<'test> TestCx<'test> {
7979
std::fs::remove_file(&profdata_path).unwrap();
8080
}
8181

82-
let proc_res = self.exec_compiled_test_general(
83-
&[("LLVM_PROFILE_FILE", &profraw_path.to_str().unwrap())],
84-
false,
85-
);
82+
let proc_res =
83+
self.exec_compiled_test_general(&[("LLVM_PROFILE_FILE", profraw_path.as_str())], false);
8684
if self.props.failure_status.is_some() {
8785
self.check_correct_failure_status(&proc_res);
8886
} else if !proc_res.status.success() {
@@ -158,8 +156,8 @@ impl<'test> TestCx<'test> {
158156
/// `.profraw` files and doctest executables to the given vectors.
159157
fn run_doctests_for_coverage(
160158
&self,
161-
profraw_paths: &mut Vec<PathBuf>,
162-
bin_paths: &mut Vec<PathBuf>,
159+
profraw_paths: &mut Vec<Utf8PathBuf>,
160+
bin_paths: &mut Vec<Utf8PathBuf>,
163161
) {
164162
// Put .profraw files and doctest executables in dedicated directories,
165163
// to make it easier to glob them all later.
@@ -204,10 +202,9 @@ impl<'test> TestCx<'test> {
204202
self.fatal_proc_rec("rustdoc --test failed!", &proc_res)
205203
}
206204

207-
fn glob_iter(path: impl AsRef<Path>) -> impl Iterator<Item = PathBuf> {
208-
let path_str = path.as_ref().to_str().unwrap();
209-
let iter = glob(path_str).unwrap();
210-
iter.map(Result::unwrap)
205+
fn glob_iter(path: impl AsRef<Utf8Path>) -> impl Iterator<Item = Utf8PathBuf> {
206+
let iter = glob(path.as_ref().as_str()).unwrap();
207+
iter.map(Result::unwrap).map(Utf8PathBuf::try_from).map(Result::unwrap)
211208
}
212209

213210
// Find all profraw files in the profraw directory.

‎src/tools/compiletest/src/runtest/debugger.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::fmt::Write;
22
use std::fs::File;
33
use std::io::{BufRead, BufReader};
4-
use std::path::{Path, PathBuf};
4+
5+
use camino::{Utf8Path, Utf8PathBuf};
56

67
use crate::common::Config;
78
use crate::runtest::ProcRes;
@@ -15,19 +16,23 @@ pub(super) struct DebuggerCommands {
1516
/// Contains the source line number to check and the line itself
1617
check_lines: Vec<(usize, String)>,
1718
/// Source file name
18-
file: PathBuf,
19+
file: Utf8PathBuf,
1920
}
2021

2122
impl DebuggerCommands {
22-
pub fn parse_from(file: &Path, config: &Config, debugger_prefix: &str) -> Result<Self, String> {
23+
pub fn parse_from(
24+
file: &Utf8Path,
25+
config: &Config,
26+
debugger_prefix: &str,
27+
) -> Result<Self, String> {
2328
let command_directive = format!("{debugger_prefix}-command");
2429
let check_directive = format!("{debugger_prefix}-check");
2530

2631
let mut breakpoint_lines = vec![];
2732
let mut commands = vec![];
2833
let mut check_lines = vec![];
2934
let mut counter = 0;
30-
let reader = BufReader::new(File::open(file).unwrap());
35+
let reader = BufReader::new(File::open(file.as_std_path()).unwrap());
3136
for (line_no, line) in reader.lines().enumerate() {
3237
counter += 1;
3338
let line = line.map_err(|e| format!("Error while parsing debugger commands: {}", e))?;
@@ -50,7 +55,7 @@ impl DebuggerCommands {
5055
}
5156
}
5257

53-
Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_owned() })
58+
Ok(Self { commands, breakpoint_lines, check_lines, file: file.to_path_buf() })
5459
}
5560

5661
/// Given debugger output and lines to check, ensure that every line is
@@ -81,10 +86,10 @@ impl DebuggerCommands {
8186
if missing.is_empty() {
8287
Ok(())
8388
} else {
84-
let fname = self.file.file_name().unwrap().to_string_lossy();
89+
let fname = self.file.file_name().unwrap();
8590
let mut msg = format!(
8691
"check directive(s) from `{}` not found in debugger output. errors:",
87-
self.file.display()
92+
self.file
8893
);
8994

9095
for (src_lineno, err_line) in missing {

‎src/tools/compiletest/src/runtest/debuginfo.rs

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::ffi::{OsStr, OsString};
22
use std::fs::File;
33
use std::io::{BufRead, BufReader, Read};
4-
use std::path::Path;
54
use std::process::{Command, Output, Stdio};
65

6+
use camino::Utf8Path;
77
use tracing::debug;
88

99
use super::debugger::DebuggerCommands;
@@ -73,11 +73,11 @@ impl TestCx<'_> {
7373
let mut js_extension = self.testpaths.file.clone();
7474
js_extension.set_extension("cdb.js");
7575
if js_extension.exists() {
76-
script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension.to_string_lossy()));
76+
script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension));
7777
}
7878

7979
// Set breakpoints on every line that contains the string "#break"
80-
let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
80+
let source_file_name = self.testpaths.file.file_name().unwrap();
8181
for line in &dbg_cmds.breakpoint_lines {
8282
script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line));
8383
}
@@ -151,16 +151,11 @@ impl TestCx<'_> {
151151
if is_android_gdb_target(&self.config.target) {
152152
cmds = cmds.replace("run", "continue");
153153

154-
let tool_path = match self.config.android_cross_path.to_str() {
155-
Some(x) => x.to_owned(),
156-
None => self.fatal("cannot find android cross path"),
157-
};
158-
159154
// write debugger script
160155
let mut script_str = String::with_capacity(2048);
161156
script_str.push_str(&format!("set charset {}\n", Self::charset()));
162-
script_str.push_str(&format!("set sysroot {}\n", tool_path));
163-
script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
157+
script_str.push_str(&format!("set sysroot {}\n", &self.config.android_cross_path));
158+
script_str.push_str(&format!("file {}\n", exe_file));
164159
script_str.push_str("target remote :5039\n");
165160
script_str.push_str(&format!(
166161
"set solib-search-path \
@@ -169,12 +164,8 @@ impl TestCx<'_> {
169164
));
170165
for line in &dbg_cmds.breakpoint_lines {
171166
script_str.push_str(
172-
format!(
173-
"break {:?}:{}\n",
174-
self.testpaths.file.file_name().unwrap().to_string_lossy(),
175-
*line
176-
)
177-
.as_str(),
167+
format!("break {}:{}\n", self.testpaths.file.file_name().unwrap(), *line)
168+
.as_str(),
178169
);
179170
}
180171
script_str.push_str(&cmds);
@@ -203,7 +194,7 @@ impl TestCx<'_> {
203194
self.config.adb_test_dir.clone(),
204195
if self.config.target.contains("aarch64") { "64" } else { "" },
205196
self.config.adb_test_dir.clone(),
206-
exe_file.file_name().unwrap().to_str().unwrap()
197+
exe_file.file_name().unwrap()
207198
);
208199

209200
debug!("adb arg: {}", adb_arg);
@@ -242,7 +233,7 @@ impl TestCx<'_> {
242233
let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
243234
gdb.args(debugger_opts);
244235
// FIXME(jieyouxu): don't pass an empty Path
245-
let cmdline = self.make_cmdline(&gdb, Path::new(""));
236+
let cmdline = self.make_cmdline(&gdb, Utf8Path::new(""));
246237
logv(self.config, format!("executing {}", cmdline));
247238
cmdline
248239
};
@@ -259,7 +250,6 @@ impl TestCx<'_> {
259250
}
260251
} else {
261252
let rust_pp_module_abs_path = self.config.src_root.join("src").join("etc");
262-
let rust_pp_module_abs_path = rust_pp_module_abs_path.to_str().unwrap();
263253
// write debugger script
264254
let mut script_str = String::with_capacity(2048);
265255
script_str.push_str(&format!("set charset {}\n", Self::charset()));
@@ -274,17 +264,15 @@ impl TestCx<'_> {
274264
// GDB's script auto loading safe path
275265
script_str.push_str(&format!(
276266
"add-auto-load-safe-path {}\n",
277-
rust_pp_module_abs_path.replace(r"\", r"\\")
267+
rust_pp_module_abs_path.as_str().replace(r"\", r"\\")
278268
));
279269

280-
let output_base_dir = self.output_base_dir().to_str().unwrap().to_owned();
281-
282270
// Add the directory containing the output binary to
283271
// include embedded pretty printers to GDB's script
284272
// auto loading safe path
285273
script_str.push_str(&format!(
286274
"add-auto-load-safe-path {}\n",
287-
output_base_dir.replace(r"\", r"\\")
275+
self.output_base_dir().as_str().replace(r"\", r"\\")
288276
));
289277
}
290278
}
@@ -301,12 +289,13 @@ impl TestCx<'_> {
301289
script_str.push_str("set print pretty off\n");
302290

303291
// Add the pretty printer directory to GDB's source-file search path
304-
script_str
305-
.push_str(&format!("directory {}\n", rust_pp_module_abs_path.replace(r"\", r"\\")));
292+
script_str.push_str(&format!(
293+
"directory {}\n",
294+
rust_pp_module_abs_path.as_str().replace(r"\", r"\\")
295+
));
306296

307297
// Load the target executable
308-
script_str
309-
.push_str(&format!("file {}\n", exe_file.to_str().unwrap().replace(r"\", r"\\")));
298+
script_str.push_str(&format!("file {}\n", exe_file.as_str().replace(r"\", r"\\")));
310299

311300
// Force GDB to print values in the Rust format.
312301
script_str.push_str("set language rust\n");
@@ -315,7 +304,7 @@ impl TestCx<'_> {
315304
for line in &dbg_cmds.breakpoint_lines {
316305
script_str.push_str(&format!(
317306
"break '{}':{}\n",
318-
self.testpaths.file.file_name().unwrap().to_string_lossy(),
307+
self.testpaths.file.file_name().unwrap(),
319308
*line
320309
));
321310
}
@@ -410,14 +399,14 @@ impl TestCx<'_> {
410399

411400
script_str.push_str(&format!(
412401
"command script import {}/lldb_lookup.py\n",
413-
rust_pp_module_abs_path.to_str().unwrap()
402+
rust_pp_module_abs_path
414403
));
415404
File::open(rust_pp_module_abs_path.join("lldb_commands"))
416405
.and_then(|mut file| file.read_to_string(&mut script_str))
417406
.expect("Failed to read lldb_commands");
418407

419408
// Set breakpoints on every line that contains the string "#break"
420-
let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
409+
let source_file_name = self.testpaths.file.file_name().unwrap();
421410
for line in &dbg_cmds.breakpoint_lines {
422411
script_str.push_str(&format!(
423412
"breakpoint set --file '{}' --line {}\n",
@@ -451,7 +440,7 @@ impl TestCx<'_> {
451440
}
452441
}
453442

454-
fn run_lldb(&self, test_executable: &Path, debugger_script: &Path) -> ProcRes {
443+
fn run_lldb(&self, test_executable: &Utf8Path, debugger_script: &Utf8Path) -> ProcRes {
455444
// Prepare the lldb_batchmode which executes the debugger script
456445
let lldb_script_path = self.config.src_root.join("src/etc/lldb_batchmode.py");
457446
let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {

‎src/tools/compiletest/src/runtest/js_doc.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ impl TestCx<'_> {
99

1010
self.document(&out_dir, &self.testpaths);
1111

12-
let file_stem =
13-
self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem");
12+
let file_stem = self.testpaths.file.file_stem().expect("no file stem");
1413
let res = self.run_command_to_procres(
1514
Command::new(&nodejs)
1615
.arg(self.config.src_root.join("src/tools/rustdoc-js/tester.js"))

‎src/tools/compiletest/src/runtest/mir_opt.rs

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fs;
2-
use std::path::{Path, PathBuf};
32

3+
use camino::{Utf8Path, Utf8PathBuf};
44
use glob::glob;
55
use miropt_test_tools::{MiroptTest, MiroptTestFile, files_for_miropt_test};
66
use tracing::debug;
@@ -14,7 +14,7 @@ impl TestCx<'_> {
1414
let should_run = self.should_run(pm);
1515

1616
let mut test_info = files_for_miropt_test(
17-
&self.testpaths.file,
17+
&self.testpaths.file.as_std_path(),
1818
self.config.get_pointer_width(),
1919
self.config.target_cfg().panic.for_miropt_test_tools(),
2020
);
@@ -38,20 +38,15 @@ impl TestCx<'_> {
3838

3939
fn check_mir_dump(&self, test_info: MiroptTest) {
4040
let test_dir = self.testpaths.file.parent().unwrap();
41-
let test_crate =
42-
self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_");
41+
let test_crate = self.testpaths.file.file_stem().unwrap().replace('-', "_");
4342

4443
let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info;
4544

4645
if self.config.bless {
47-
for e in
48-
glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, suffix)).unwrap()
49-
{
46+
for e in glob(&format!("{}/{}.*{}.mir", test_dir, test_crate, suffix)).unwrap() {
5047
fs::remove_file(e.unwrap()).unwrap();
5148
}
52-
for e in
53-
glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, suffix)).unwrap()
54-
{
49+
for e in glob(&format!("{}/{}.*{}.diff", test_dir, test_crate, suffix)).unwrap() {
5550
fs::remove_file(e.unwrap()).unwrap();
5651
}
5752
}
@@ -60,19 +55,15 @@ impl TestCx<'_> {
6055
let dumped_string = if let Some(after) = to_file {
6156
self.diff_mir_files(from_file.into(), after.into())
6257
} else {
63-
let mut output_file = PathBuf::new();
58+
let mut output_file = Utf8PathBuf::new();
6459
output_file.push(self.get_mir_dump_dir());
6560
output_file.push(&from_file);
66-
debug!(
67-
"comparing the contents of: {} with {}",
68-
output_file.display(),
69-
expected_file.display()
70-
);
61+
debug!("comparing the contents of: {} with {:?}", output_file, expected_file);
7162
if !output_file.exists() {
7263
panic!(
7364
"Output file `{}` from test does not exist, available files are in `{}`",
74-
output_file.display(),
75-
output_file.parent().unwrap().display()
65+
output_file,
66+
output_file.parent().unwrap()
7667
);
7768
}
7869
self.check_mir_test_timestamp(&from_file, &output_file);
@@ -107,21 +98,20 @@ impl TestCx<'_> {
10798
}
10899
}
109100

110-
fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String {
111-
let to_full_path = |path: PathBuf| {
101+
fn diff_mir_files(&self, before: Utf8PathBuf, after: Utf8PathBuf) -> String {
102+
let to_full_path = |path: Utf8PathBuf| {
112103
let full = self.get_mir_dump_dir().join(&path);
113104
if !full.exists() {
114105
panic!(
115106
"the mir dump file for {} does not exist (requested in {})",
116-
path.display(),
117-
self.testpaths.file.display(),
107+
path, self.testpaths.file,
118108
);
119109
}
120110
full
121111
};
122112
let before = to_full_path(before);
123113
let after = to_full_path(after);
124-
debug!("comparing the contents of: {} with {}", before.display(), after.display());
114+
debug!("comparing the contents of: {} with {}", before, after);
125115
let before = fs::read_to_string(before).unwrap();
126116
let after = fs::read_to_string(after).unwrap();
127117
let before = self.normalize_output(&before, &[]);
@@ -138,17 +128,16 @@ impl TestCx<'_> {
138128
dumped_string
139129
}
140130

141-
fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) {
142-
let t = |file| fs::metadata(file).unwrap().modified().unwrap();
131+
fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Utf8Path) {
132+
let t = |file: &Utf8Path| fs::metadata(file.as_std_path()).unwrap().modified().unwrap();
143133
let source_file = &self.testpaths.file;
144134
let output_time = t(output_file);
145135
let source_time = t(source_file);
146136
if source_time > output_time {
147137
debug!("source file time: {:?} output file time: {:?}", source_time, output_time);
148138
panic!(
149139
"test source file `{}` is newer than potentially stale output file `{}`.",
150-
source_file.display(),
151-
test_name
140+
source_file, test_name
152141
);
153142
}
154143
}

‎src/tools/compiletest/src/runtest/run_make.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use std::path::Path;
21
use std::process::{Command, Output, Stdio};
32
use std::{env, fs};
43

54
use build_helper::fs::{ignore_not_found, recursive_remove};
5+
use camino::{Utf8Path, Utf8PathBuf};
66

77
use super::{ProcRes, TestCx, disable_error_reporting};
88
use crate::util::{copy_dir_all, dylib_env_var};
@@ -39,14 +39,16 @@ impl TestCx<'_> {
3939
// Copy all input files (apart from rmake.rs) to the temporary directory,
4040
// so that the input directory structure from `tests/run-make/<test>` is mirrored
4141
// to the `rmake_out` directory.
42-
for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) {
43-
let path = path.unwrap().path().to_path_buf();
42+
for entry in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) {
43+
let entry = entry.unwrap();
44+
let path = entry.path();
45+
let path = <&Utf8Path>::try_from(path).unwrap();
4446
if path.file_name().is_some_and(|s| s != "rmake.rs") {
4547
let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap());
4648
if path.is_dir() {
47-
copy_dir_all(&path, target).unwrap();
49+
copy_dir_all(&path, &target).unwrap();
4850
} else {
49-
fs::copy(&path, target).unwrap();
51+
fs::copy(path.as_std_path(), target).unwrap();
5052
}
5153
}
5254
}
@@ -83,8 +85,10 @@ impl TestCx<'_> {
8385
// on some linux distros.
8486
// 2. Specific library paths in `self.config.compile_lib_path` needed for running rustc.
8587

86-
let base_dylib_search_paths =
87-
Vec::from_iter(env::split_paths(&env::var(dylib_env_var()).unwrap()));
88+
let base_dylib_search_paths = Vec::from_iter(
89+
env::split_paths(&env::var(dylib_env_var()).unwrap())
90+
.map(|p| Utf8PathBuf::try_from(p).expect("dylib env var contains non-UTF8 paths")),
91+
);
8892

8993
// Calculate the paths of the recipe binary. As previously discussed, this is placed at
9094
// `<base_dir>/<bin_name>` with `bin_name` being `rmake` or `rmake.exe` depending on
@@ -113,13 +117,13 @@ impl TestCx<'_> {
113117
.arg("-o")
114118
.arg(&recipe_bin)
115119
// Specify library search paths for `run_make_support`.
116-
.arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap().to_string_lossy()))
117-
.arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy()))
118-
.arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy()))
120+
.arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap()))
121+
.arg(format!("-Ldependency={}", &support_lib_deps))
122+
.arg(format!("-Ldependency={}", &support_lib_deps_deps))
119123
// Provide `run_make_support` as extern prelude, so test writers don't need to write
120124
// `extern run_make_support;`.
121125
.arg("--extern")
122-
.arg(format!("run_make_support={}", &support_lib_path.to_string_lossy()))
126+
.arg(format!("run_make_support={}", &support_lib_path))
123127
.arg("--edition=2021")
124128
.arg(&self.testpaths.file.join("rmake.rs"))
125129
.arg("-Cprefer-dynamic");
@@ -240,7 +244,7 @@ impl TestCx<'_> {
240244
if self.config.target.contains("msvc") && !self.config.cc.is_empty() {
241245
// We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
242246
// and that `lib.exe` lives next to it.
243-
let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
247+
let lib = Utf8Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
244248

245249
// MSYS doesn't like passing flags of the form `/foo` as it thinks it's
246250
// a path and instead passes `C:\msys64\foo`, so convert all
@@ -262,8 +266,8 @@ impl TestCx<'_> {
262266

263267
cmd.env("IS_MSVC", "1")
264268
.env("IS_WINDOWS", "1")
265-
.env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
266-
.env("MSVC_LIB_PATH", format!("{}", lib.display()))
269+
.env("MSVC_LIB", format!("'{}' -nologo", lib))
270+
.env("MSVC_LIB_PATH", &lib)
267271
// Note: we diverge from legacy run_make and don't lump `CC` the compiler and
268272
// default flags together.
269273
.env("CC_DEFAULT_FLAGS", &cflags)

‎src/tools/compiletest/src/runtest/ui.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,16 @@ impl TestCx<'_> {
6868
{
6969
let mut coverage_file_path = self.config.build_test_suite_root.clone();
7070
coverage_file_path.push("rustfix_missing_coverage.txt");
71-
debug!("coverage_file_path: {}", coverage_file_path.display());
71+
debug!("coverage_file_path: {}", coverage_file_path);
7272

7373
let mut file = OpenOptions::new()
7474
.create(true)
7575
.append(true)
7676
.open(coverage_file_path.as_path())
7777
.expect("could not create or open file");
7878

79-
if let Err(e) = writeln!(file, "{}", self.testpaths.file.display()) {
80-
panic!("couldn't write to {}: {e:?}", coverage_file_path.display());
79+
if let Err(e) = writeln!(file, "{}", self.testpaths.file) {
80+
panic!("couldn't write to {}: {e:?}", coverage_file_path);
8181
}
8282
}
8383
} else if self.props.run_rustfix {
@@ -119,7 +119,7 @@ impl TestCx<'_> {
119119
self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
120120
println!(
121121
"To only update this specific test, also pass `--test-args {}`",
122-
relative_path_to_file.display(),
122+
relative_path_to_file,
123123
);
124124
self.fatal_proc_rec(
125125
&format!("{} errors occurred comparing output.", errors),
@@ -211,8 +211,6 @@ impl TestCx<'_> {
211211
let crate_name =
212212
self.testpaths.file.file_stem().expect("test must have a file stem");
213213
// crate name must be alphanumeric or `_`.
214-
let crate_name =
215-
crate_name.to_str().expect("crate name implies file name must be valid UTF-8");
216214
// replace `a.foo` -> `a__foo` for crate name purposes.
217215
// replace `revision-name-with-dashes` -> `revision_name_with_underscore`
218216
let crate_name = crate_name.replace('.', "__");

‎src/tools/compiletest/src/tests.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::ffi::OsString;
2-
31
use crate::debuggers::{extract_gdb_version, extract_lldb_version};
42
use crate::is_test;
53

@@ -60,11 +58,11 @@ fn test_extract_lldb_version() {
6058

6159
#[test]
6260
fn is_test_test() {
63-
assert!(is_test(&OsString::from("a_test.rs")));
64-
assert!(!is_test(&OsString::from(".a_test.rs")));
65-
assert!(!is_test(&OsString::from("a_cat.gif")));
66-
assert!(!is_test(&OsString::from("#a_dog_gif")));
67-
assert!(!is_test(&OsString::from("~a_temp_file")));
61+
assert!(is_test("a_test.rs"));
62+
assert!(!is_test(".a_test.rs"));
63+
assert!(!is_test("a_cat.gif"));
64+
assert!(!is_test("#a_dog_gif"));
65+
assert!(!is_test("~a_temp_file"));
6866
}
6967

7068
#[test]

‎src/tools/compiletest/src/util.rs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use std::env;
2-
use std::ffi::OsStr;
3-
use std::path::{Path, PathBuf};
42
use std::process::Command;
53

4+
use camino::{Utf8Path, Utf8PathBuf};
65
use tracing::*;
76

87
use crate::common::Config;
@@ -34,21 +33,21 @@ pub fn logv(config: &Config, s: String) {
3433
}
3534
}
3635

37-
pub trait PathBufExt {
36+
pub trait Utf8PathBufExt {
3837
/// Append an extension to the path, even if it already has one.
39-
fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf;
38+
fn with_extra_extension(&self, extension: &str) -> Utf8PathBuf;
4039
}
4140

42-
impl PathBufExt for PathBuf {
43-
fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
44-
if extension.as_ref().is_empty() {
41+
impl Utf8PathBufExt for Utf8PathBuf {
42+
fn with_extra_extension(&self, extension: &str) -> Utf8PathBuf {
43+
if extension.is_empty() {
4544
self.clone()
4645
} else {
47-
let mut fname = self.file_name().unwrap().to_os_string();
48-
if !extension.as_ref().to_str().unwrap().starts_with('.') {
49-
fname.push(".");
46+
let mut fname = self.file_name().unwrap().to_string();
47+
if !extension.starts_with('.') {
48+
fname.push_str(".");
5049
}
51-
fname.push(extension);
50+
fname.push_str(extension);
5251
self.with_file_name(fname)
5352
}
5453
}
@@ -71,22 +70,27 @@ pub fn dylib_env_var() -> &'static str {
7170

7271
/// Adds a list of lookup paths to `cmd`'s dynamic library lookup path.
7372
/// If the dylib_path_var is already set for this cmd, the old value will be overwritten!
74-
pub fn add_dylib_path(cmd: &mut Command, paths: impl Iterator<Item = impl Into<PathBuf>>) {
73+
pub fn add_dylib_path(
74+
cmd: &mut Command,
75+
paths: impl Iterator<Item = impl Into<std::path::PathBuf>>,
76+
) {
7577
let path_env = env::var_os(dylib_env_var());
7678
let old_paths = path_env.as_ref().map(env::split_paths);
7779
let new_paths = paths.map(Into::into).chain(old_paths.into_iter().flatten());
7880
cmd.env(dylib_env_var(), env::join_paths(new_paths).unwrap());
7981
}
8082

81-
pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
82-
std::fs::create_dir_all(&dst)?;
83-
for entry in std::fs::read_dir(src)? {
83+
pub fn copy_dir_all(src: &Utf8Path, dst: &Utf8Path) -> std::io::Result<()> {
84+
std::fs::create_dir_all(dst.as_std_path())?;
85+
for entry in std::fs::read_dir(src.as_std_path())? {
8486
let entry = entry?;
87+
let path = Utf8PathBuf::try_from(entry.path()).unwrap();
88+
let file_name = path.file_name().unwrap();
8589
let ty = entry.file_type()?;
8690
if ty.is_dir() {
87-
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
91+
copy_dir_all(&path, &dst.join(file_name))?;
8892
} else {
89-
std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
93+
std::fs::copy(path.as_std_path(), dst.join(file_name).as_std_path())?;
9094
}
9195
}
9296
Ok(())

‎src/tools/compiletest/src/util/tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ use super::*;
33
#[test]
44
fn path_buf_with_extra_extension_test() {
55
assert_eq!(
6-
PathBuf::from("foo.rs.stderr"),
7-
PathBuf::from("foo.rs").with_extra_extension("stderr")
6+
Utf8PathBuf::from("foo.rs.stderr"),
7+
Utf8PathBuf::from("foo.rs").with_extra_extension("stderr")
88
);
99
assert_eq!(
10-
PathBuf::from("foo.rs.stderr"),
11-
PathBuf::from("foo.rs").with_extra_extension(".stderr")
10+
Utf8PathBuf::from("foo.rs.stderr"),
11+
Utf8PathBuf::from("foo.rs").with_extra_extension(".stderr")
1212
);
13-
assert_eq!(PathBuf::from("foo.rs"), PathBuf::from("foo.rs").with_extra_extension(""));
13+
assert_eq!(Utf8PathBuf::from("foo.rs"), Utf8PathBuf::from("foo.rs").with_extra_extension(""));
1414
}

‎src/tools/rustdoc-gui-test/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
build_helper = { path = "../../build_helper" }
8+
camino = "1"
89
compiletest = { path = "../compiletest" }
910
getopts = "0.2"
1011
walkdir = "2"

‎src/tools/rustdoc-gui-test/src/main.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,11 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse
118118
..Default::default()
119119
};
120120

121-
let test_props = TestProps::from_file(&librs, None, &compiletest_c);
121+
let test_props = TestProps::from_file(
122+
&camino::Utf8PathBuf::try_from(librs).unwrap(),
123+
None,
124+
&compiletest_c,
125+
);
122126

123127
if !test_props.compile_flags.is_empty() {
124128
cargo.env("RUSTDOCFLAGS", test_props.compile_flags.join(" "));

0 commit comments

Comments
 (0)
Please sign in to comment.