Skip to content

Commit a8e3543

Browse files
authored
Rollup merge of #121194 - beetrees:rustc-raw-args, r=petrochenkov
Refactor pre-getopts command line argument handling Rebased version of #111658. I've also fixed the Windows CI failure (although I don't have access to Windows to test it myself).
2 parents 075f1c3 + 2d7d0bd commit a8e3543

File tree

44 files changed

+303
-79
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+303
-79
lines changed

compiler/rustc_driver_impl/src/args.rs

+41-14
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
use std::error;
2-
use std::fmt;
3-
use std::fs;
4-
use std::io;
1+
use std::{env, error, fmt, fs, io};
52

63
use rustc_session::EarlyDiagCtxt;
4+
use rustc_span::ErrorGuaranteed;
75

86
/// Expands argfiles in command line arguments.
97
#[derive(Default)]
@@ -86,42 +84,71 @@ impl Expander {
8684
fn read_file(path: &str) -> Result<String, Error> {
8785
fs::read_to_string(path).map_err(|e| {
8886
if e.kind() == io::ErrorKind::InvalidData {
89-
Error::Utf8Error(Some(path.to_string()))
87+
Error::Utf8Error(path.to_string())
9088
} else {
9189
Error::IOError(path.to_string(), e)
9290
}
9391
})
9492
}
9593
}
9694

95+
/// Replaces any `@file` arguments with the contents of `file`, with each line of `file` as a
96+
/// separate argument.
97+
///
9798
/// **Note:** This function doesn't interpret argument 0 in any special way.
9899
/// If this function is intended to be used with command line arguments,
99100
/// `argv[0]` must be removed prior to calling it manually.
100101
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
101-
pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> {
102+
pub fn arg_expand_all(
103+
early_dcx: &EarlyDiagCtxt,
104+
at_args: &[String],
105+
) -> Result<Vec<String>, ErrorGuaranteed> {
102106
let mut expander = Expander::default();
107+
let mut result = Ok(());
103108
for arg in at_args {
104109
if let Err(err) = expander.arg(arg) {
105-
early_dcx.early_fatal(format!("Failed to load argument file: {err}"));
110+
result = Err(early_dcx.early_err(format!("failed to load argument file: {err}")));
106111
}
107112
}
108-
expander.finish()
113+
result.map(|()| expander.finish())
114+
}
115+
116+
/// Gets the raw unprocessed command-line arguments as Unicode strings, without doing any further
117+
/// processing (e.g., without `@file` expansion).
118+
///
119+
/// This function is identical to [`env::args()`] except that it emits an error when it encounters
120+
/// non-Unicode arguments instead of panicking.
121+
pub fn raw_args(early_dcx: &EarlyDiagCtxt) -> Result<Vec<String>, ErrorGuaranteed> {
122+
let mut res = Ok(Vec::new());
123+
for (i, arg) in env::args_os().enumerate() {
124+
match arg.into_string() {
125+
Ok(arg) => {
126+
if let Ok(args) = &mut res {
127+
args.push(arg);
128+
}
129+
}
130+
Err(arg) => {
131+
res =
132+
Err(early_dcx.early_err(format!("argument {i} is not valid Unicode: {arg:?}")))
133+
}
134+
}
135+
}
136+
res
109137
}
110138

111139
#[derive(Debug)]
112-
pub enum Error {
113-
Utf8Error(Option<String>),
140+
enum Error {
141+
Utf8Error(String),
114142
IOError(String, io::Error),
115143
ShellParseError(String),
116144
}
117145

118146
impl fmt::Display for Error {
119147
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
120148
match self {
121-
Error::Utf8Error(None) => write!(fmt, "Utf8 error"),
122-
Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {path}"),
123-
Error::IOError(path, err) => write!(fmt, "IO Error: {path}: {err}"),
124-
Error::ShellParseError(path) => write!(fmt, "Invalid shell-style arguments in {path}"),
149+
Error::Utf8Error(path) => write!(fmt, "UTF-8 error in {path}"),
150+
Error::IOError(path, err) => write!(fmt, "IO error: {path}: {err}"),
151+
Error::ShellParseError(path) => write!(fmt, "invalid shell-style arguments in {path}"),
125152
}
126153
}
127154
}

compiler/rustc_driver_impl/src/lib.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ fn run_compiler(
292292
// the compiler with @empty_file as argv[0] and no more arguments.
293293
let at_args = at_args.get(1..).unwrap_or_default();
294294

295-
let args = args::arg_expand_all(&default_early_dcx, at_args);
295+
let args = args::arg_expand_all(&default_early_dcx, at_args)?;
296296

297297
let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) };
298298

@@ -1515,15 +1515,7 @@ pub fn main() -> ! {
15151515
let mut callbacks = TimePassesCallbacks::default();
15161516
let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
15171517
let exit_code = catch_with_exit_code(|| {
1518-
let args = env::args_os()
1519-
.enumerate()
1520-
.map(|(i, arg)| {
1521-
arg.into_string().unwrap_or_else(|arg| {
1522-
early_dcx.early_fatal(format!("argument {i} is not valid Unicode: {arg:?}"))
1523-
})
1524-
})
1525-
.collect::<Vec<_>>();
1526-
RunCompiler::new(&args, &mut callbacks)
1518+
RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks)
15271519
.set_using_internal_features(using_internal_features)
15281520
.run()
15291521
});

src/librustdoc/lib.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -179,21 +179,14 @@ pub fn main() {
179179
rustc_driver::init_logger(&early_dcx, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG"));
180180

181181
let exit_code = rustc_driver::catch_with_exit_code(|| {
182-
let args = env::args_os()
183-
.enumerate()
184-
.map(|(i, arg)| {
185-
arg.into_string().unwrap_or_else(|arg| {
186-
early_dcx.early_fatal(format!("argument {i} is not valid Unicode: {arg:?}"))
187-
})
188-
})
189-
.collect::<Vec<_>>();
190-
main_args(&mut early_dcx, &args, using_internal_features)
182+
let at_args = rustc_driver::args::raw_args(&early_dcx)?;
183+
main_args(&mut early_dcx, &at_args, using_internal_features)
191184
});
192185
process::exit(exit_code);
193186
}
194187

195188
fn init_logging(early_dcx: &EarlyDiagCtxt) {
196-
let color_logs = match std::env::var("RUSTDOC_LOG_COLOR").as_deref() {
189+
let color_logs = match env::var("RUSTDOC_LOG_COLOR").as_deref() {
197190
Ok("always") => true,
198191
Ok("never") => false,
199192
Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(),
@@ -705,7 +698,7 @@ fn main_args(
705698
// the compiler with @empty_file as argv[0] and no more arguments.
706699
let at_args = at_args.get(1..).unwrap_or_default();
707700

708-
let args = rustc_driver::args::arg_expand_all(early_dcx, at_args);
701+
let args = rustc_driver::args::arg_expand_all(early_dcx, at_args)?;
709702

710703
let mut options = getopts::Options::new();
711704
for option in opts() {

src/tools/clippy/src/driver.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ pub fn main() {
190190
});
191191

192192
exit(rustc_driver::catch_with_exit_code(move || {
193-
let mut orig_args: Vec<String> = env::args().collect();
193+
let mut orig_args = rustc_driver::args::raw_args(&early_dcx)?;
194194

195195
let has_sysroot_arg = |args: &mut [String]| -> bool {
196196
if arg_value(args, "--sysroot", |_| true).is_some() {

src/tools/miri/src/bin/miri.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ fn main() {
342342
// (`install_ice_hook` might change `RUST_BACKTRACE`.)
343343
let env_snapshot = env::vars_os().collect::<Vec<_>>();
344344

345+
let args = rustc_driver::args::raw_args(&early_dcx).unwrap_or_else(|_| std::process::exit(rustc_driver::EXIT_FAILURE));
346+
345347
// If the environment asks us to actually be rustc, then do that.
346348
if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {
347349
// Earliest rustc setup.
@@ -359,7 +361,7 @@ fn main() {
359361

360362
// We cannot use `rustc_driver::main` as we need to adjust the CLI arguments.
361363
run_compiler(
362-
env::args().collect(),
364+
args,
363365
target_crate,
364366
&mut MiriBeRustCompilerCalls { target_crate },
365367
using_internal_features,
@@ -382,7 +384,7 @@ fn main() {
382384

383385
// If user has explicitly enabled/disabled isolation
384386
let mut isolation_enabled: Option<bool> = None;
385-
for arg in env::args() {
387+
for arg in args {
386388
if rustc_args.is_empty() {
387389
// Very first arg: binary name.
388390
rustc_args.push(arg);

src/tools/tidy/src/ui_tests.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const ENTRY_LIMIT: usize = 900;
1818
// FIXME: The following limits should be reduced eventually.
1919

2020
const ISSUES_ENTRY_LIMIT: usize = 1750;
21-
const ROOT_ENTRY_LIMIT: usize = 872;
21+
const ROOT_ENTRY_LIMIT: usize = 866;
2222

2323
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
2424
"rs", // test source files
@@ -34,8 +34,8 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[
3434
"tests/ui/asm/named-asm-labels.s", // loading an external asm file to test named labels lint
3535
"tests/ui/codegen/mismatched-data-layout.json", // testing mismatched data layout w/ custom targets
3636
"tests/ui/check-cfg/my-awesome-platform.json", // testing custom targets with cfgs
37-
"tests/ui/commandline-argfile-badutf8.args", // passing args via a file
38-
"tests/ui/commandline-argfile.args", // passing args via a file
37+
"tests/ui/argfile/commandline-argfile-badutf8.args", // passing args via a file
38+
"tests/ui/argfile/commandline-argfile.args", // passing args via a file
3939
"tests/ui/crate-loading/auxiliary/libfoo.rlib", // testing loading a manually created rlib
4040
"tests/ui/include-macros/data.bin", // testing including data with the include macros
4141
"tests/ui/include-macros/file.txt", // testing including data with the include macros
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Check to see if we can get parameters from an @argsfile file
2+
//
3+
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
4+
// separators. This test uses backslash as the path separator for the command
5+
// line arguments and is only run on windows.
6+
//
7+
//@ only-windows
8+
//@ compile-flags: --cfg cmdline_set @{{src-base}}\argfile\commandline-argfile-badutf8.args
9+
10+
#[cfg(not(cmdline_set))]
11+
compile_error!("cmdline_set not set");
12+
13+
#[cfg(not(unbroken))]
14+
compile_error!("unbroken not set");
15+
16+
fn main() {
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
error: failed to load argument file: UTF-8 error in $DIR/commandline-argfile-badutf8.args
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Check to see if we can get parameters from an @argsfile file
2+
//
3+
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
4+
// separators. We have a duplicated version of this test that uses backslash as
5+
// the path separator for the command line arguments that is only run on
6+
// windows.
7+
//
8+
//@ ignore-windows
9+
//@ compile-flags: --cfg cmdline_set @{{src-base}}/argfile/commandline-argfile-badutf8.args
10+
11+
#[cfg(not(cmdline_set))]
12+
compile_error!("cmdline_set not set");
13+
14+
#[cfg(not(unbroken))]
15+
compile_error!("unbroken not set");
16+
17+
fn main() {
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
error: failed to load argument file: UTF-8 error in $DIR/commandline-argfile-badutf8.args
2+

tests/ui/commandline-argfile-missing.rs tests/rustdoc-ui/argfile/commandline-argfile-missing-windows.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
// Check to see if we can get parameters from an @argsfile file
22
//
3+
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
4+
// separators. This test uses backslash as the path separator for the command
5+
// line arguments and is only run on windows.
6+
//
7+
//@ only-windows
38
//@ normalize-stderr-test: "os error \d+" -> "os error $$ERR"
49
//@ normalize-stderr-test: "commandline-argfile-missing.args:[^(]*" -> "commandline-argfile-missing.args: $$FILE_MISSING "
5-
//@ compile-flags: --cfg cmdline_set @{{src-base}}/commandline-argfile-missing.args
10+
//@ compile-flags: --cfg cmdline_set @{{src-base}}\argfile\commandline-argfile-missing.args
611

712
#[cfg(not(cmdline_set))]
813
compile_error!("cmdline_set not set");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing.args: $FILE_MISSING (os error $ERR)
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Check to see if we can get parameters from an @argsfile file
2+
//
3+
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
4+
// separators. We have a duplicated version of this test that uses backslash as
5+
// the path separator for the command line arguments that is only run on
6+
// windows.
7+
//
8+
//@ ignore-windows
9+
//@ normalize-stderr-test: "os error \d+" -> "os error $$ERR"
10+
//@ normalize-stderr-test: "commandline-argfile-missing.args:[^(]*" -> "commandline-argfile-missing.args: $$FILE_MISSING "
11+
//@ compile-flags: --cfg cmdline_set @{{src-base}}/argfile/commandline-argfile-missing.args
12+
13+
#[cfg(not(cmdline_set))]
14+
compile_error!("cmdline_set not set");
15+
16+
#[cfg(not(unbroken))]
17+
compile_error!("unbroken not set");
18+
19+
fn main() {
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing.args: $FILE_MISSING (os error $ERR)
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Check to see if we can get parameters from an @argsfile file
2+
//
3+
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
4+
// separators. This test uses backslash as the path separator for the command
5+
// line arguments and is only run on windows.
6+
//
7+
//@ only-windows
8+
//@ normalize-stderr-test: "os error \d+" -> "os error $$ERR"
9+
//@ normalize-stderr-test: "commandline-argfile-missing.args:[^(]*" -> "commandline-argfile-missing.args: $$FILE_MISSING "
10+
//@ normalize-stderr-test: "commandline-argfile-missing2.args:[^(]*" -> "commandline-argfile-missing2.args: $$FILE_MISSING "
11+
//@ compile-flags: --cfg cmdline_set @{{src-base}}\argfile\commandline-argfile-missing.args @{{src-base}}\argfile\commandline-argfile-badutf8.args @{{src-base}}\argfile\commandline-argfile-missing2.args
12+
13+
#[cfg(not(cmdline_set))]
14+
compile_error!("cmdline_set not set");
15+
16+
#[cfg(not(unbroken))]
17+
compile_error!("unbroken not set");
18+
19+
fn main() {
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing.args: $FILE_MISSING (os error $ERR)
2+
3+
error: failed to load argument file: UTF-8 error in $DIR/commandline-argfile-badutf8.args
4+
5+
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing2.args: $FILE_MISSING (os error $ERR)
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Check to see if we can get parameters from an @argsfile file
2+
//
3+
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
4+
// separators. We have a duplicated version of this test that uses backslash as
5+
// the path separator for the command line arguments that is only run on
6+
// windows.
7+
//
8+
//@ ignore-windows
9+
//@ normalize-stderr-test: "os error \d+" -> "os error $$ERR"
10+
//@ normalize-stderr-test: "commandline-argfile-missing.args:[^(]*" -> "commandline-argfile-missing.args: $$FILE_MISSING "
11+
//@ normalize-stderr-test: "commandline-argfile-missing2.args:[^(]*" -> "commandline-argfile-missing2.args: $$FILE_MISSING "
12+
//@ compile-flags: --cfg cmdline_set @{{src-base}}/argfile/commandline-argfile-missing.args @{{src-base}}/argfile/commandline-argfile-badutf8.args @{{src-base}}/argfile/commandline-argfile-missing2.args
13+
14+
#[cfg(not(cmdline_set))]
15+
compile_error!("cmdline_set not set");
16+
17+
#[cfg(not(unbroken))]
18+
compile_error!("unbroken not set");
19+
20+
fn main() {
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing.args: $FILE_MISSING (os error $ERR)
2+
3+
error: failed to load argument file: UTF-8 error in $DIR/commandline-argfile-badutf8.args
4+
5+
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing2.args: $FILE_MISSING (os error $ERR)
6+

tests/rustdoc-ui/commandline-argfile.rs tests/rustdoc-ui/argfile/commandline-argfile.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Check to see if we can get parameters from an @argsfile file
22
//
33
//@ check-pass
4-
//@ compile-flags: --cfg cmdline_set @{{src-base}}/commandline-argfile.args
4+
//@ compile-flags: --cfg cmdline_set @{{src-base}}/argfile/commandline-argfile.args
55

66
#[cfg(not(cmdline_set))]
77
compile_error!("cmdline_set not set");

tests/rustdoc-ui/commandline-argfile-badutf8.rs

-12
This file was deleted.

tests/rustdoc-ui/commandline-argfile-badutf8.stderr

-2
This file was deleted.

tests/rustdoc-ui/commandline-argfile-missing.stderr

-2
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Check to see if we can get parameters from an @argsfile file
2+
//
3+
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
4+
// separators. This test uses backslash as the path separator for the command
5+
// line arguments and is only run on windows.
6+
//
7+
//@ only-windows
8+
//@ compile-flags: --cfg cmdline_set @{{src-base}}\argfile\commandline-argfile-badutf8.args
9+
10+
#[cfg(not(cmdline_set))]
11+
compile_error!("cmdline_set not set");
12+
13+
#[cfg(not(unbroken))]
14+
compile_error!("unbroken not set");
15+
16+
fn main() {
17+
}

0 commit comments

Comments
 (0)