Skip to content

Commit

Permalink
Rollup merge of #121194 - beetrees:rustc-raw-args, r=petrochenkov
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
matthiaskrgr authored Mar 8, 2024
2 parents 075f1c3 + 2d7d0bd commit a8e3543
Show file tree
Hide file tree
Showing 44 changed files with 303 additions and 79 deletions.
55 changes: 41 additions & 14 deletions compiler/rustc_driver_impl/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use std::error;
use std::fmt;
use std::fs;
use std::io;
use std::{env, error, fmt, fs, io};

use rustc_session::EarlyDiagCtxt;
use rustc_span::ErrorGuaranteed;

/// Expands argfiles in command line arguments.
#[derive(Default)]
Expand Down Expand Up @@ -86,42 +84,71 @@ impl Expander {
fn read_file(path: &str) -> Result<String, Error> {
fs::read_to_string(path).map_err(|e| {
if e.kind() == io::ErrorKind::InvalidData {
Error::Utf8Error(Some(path.to_string()))
Error::Utf8Error(path.to_string())
} else {
Error::IOError(path.to_string(), e)
}
})
}
}

/// Replaces any `@file` arguments with the contents of `file`, with each line of `file` as a
/// separate argument.
///
/// **Note:** This function doesn't interpret argument 0 in any special way.
/// If this function is intended to be used with command line arguments,
/// `argv[0]` must be removed prior to calling it manually.
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> {
pub fn arg_expand_all(
early_dcx: &EarlyDiagCtxt,
at_args: &[String],
) -> Result<Vec<String>, ErrorGuaranteed> {
let mut expander = Expander::default();
let mut result = Ok(());
for arg in at_args {
if let Err(err) = expander.arg(arg) {
early_dcx.early_fatal(format!("Failed to load argument file: {err}"));
result = Err(early_dcx.early_err(format!("failed to load argument file: {err}")));
}
}
expander.finish()
result.map(|()| expander.finish())
}

/// Gets the raw unprocessed command-line arguments as Unicode strings, without doing any further
/// processing (e.g., without `@file` expansion).
///
/// This function is identical to [`env::args()`] except that it emits an error when it encounters
/// non-Unicode arguments instead of panicking.
pub fn raw_args(early_dcx: &EarlyDiagCtxt) -> Result<Vec<String>, ErrorGuaranteed> {
let mut res = Ok(Vec::new());
for (i, arg) in env::args_os().enumerate() {
match arg.into_string() {
Ok(arg) => {
if let Ok(args) = &mut res {
args.push(arg);
}
}
Err(arg) => {
res =
Err(early_dcx.early_err(format!("argument {i} is not valid Unicode: {arg:?}")))
}
}
}
res
}

#[derive(Debug)]
pub enum Error {
Utf8Error(Option<String>),
enum Error {
Utf8Error(String),
IOError(String, io::Error),
ShellParseError(String),
}

impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Utf8Error(None) => write!(fmt, "Utf8 error"),
Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {path}"),
Error::IOError(path, err) => write!(fmt, "IO Error: {path}: {err}"),
Error::ShellParseError(path) => write!(fmt, "Invalid shell-style arguments in {path}"),
Error::Utf8Error(path) => write!(fmt, "UTF-8 error in {path}"),
Error::IOError(path, err) => write!(fmt, "IO error: {path}: {err}"),
Error::ShellParseError(path) => write!(fmt, "invalid shell-style arguments in {path}"),
}
}
}
Expand Down
12 changes: 2 additions & 10 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ fn run_compiler(
// the compiler with @empty_file as argv[0] and no more arguments.
let at_args = at_args.get(1..).unwrap_or_default();

let args = args::arg_expand_all(&default_early_dcx, at_args);
let args = args::arg_expand_all(&default_early_dcx, at_args)?;

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

Expand Down Expand Up @@ -1515,15 +1515,7 @@ pub fn main() -> ! {
let mut callbacks = TimePassesCallbacks::default();
let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
let exit_code = catch_with_exit_code(|| {
let args = env::args_os()
.enumerate()
.map(|(i, arg)| {
arg.into_string().unwrap_or_else(|arg| {
early_dcx.early_fatal(format!("argument {i} is not valid Unicode: {arg:?}"))
})
})
.collect::<Vec<_>>();
RunCompiler::new(&args, &mut callbacks)
RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks)
.set_using_internal_features(using_internal_features)
.run()
});
Expand Down
15 changes: 4 additions & 11 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,21 +179,14 @@ pub fn main() {
rustc_driver::init_logger(&early_dcx, rustc_log::LoggerConfig::from_env("RUSTDOC_LOG"));

let exit_code = rustc_driver::catch_with_exit_code(|| {
let args = env::args_os()
.enumerate()
.map(|(i, arg)| {
arg.into_string().unwrap_or_else(|arg| {
early_dcx.early_fatal(format!("argument {i} is not valid Unicode: {arg:?}"))
})
})
.collect::<Vec<_>>();
main_args(&mut early_dcx, &args, using_internal_features)
let at_args = rustc_driver::args::raw_args(&early_dcx)?;
main_args(&mut early_dcx, &at_args, using_internal_features)
});
process::exit(exit_code);
}

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

let args = rustc_driver::args::arg_expand_all(early_dcx, at_args);
let args = rustc_driver::args::arg_expand_all(early_dcx, at_args)?;

let mut options = getopts::Options::new();
for option in opts() {
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ pub fn main() {
});

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

let has_sysroot_arg = |args: &mut [String]| -> bool {
if arg_value(args, "--sysroot", |_| true).is_some() {
Expand Down
6 changes: 4 additions & 2 deletions src/tools/miri/src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ fn main() {
// (`install_ice_hook` might change `RUST_BACKTRACE`.)
let env_snapshot = env::vars_os().collect::<Vec<_>>();

let args = rustc_driver::args::raw_args(&early_dcx).unwrap_or_else(|_| std::process::exit(rustc_driver::EXIT_FAILURE));

// If the environment asks us to actually be rustc, then do that.
if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {
// Earliest rustc setup.
Expand All @@ -359,7 +361,7 @@ fn main() {

// We cannot use `rustc_driver::main` as we need to adjust the CLI arguments.
run_compiler(
env::args().collect(),
args,
target_crate,
&mut MiriBeRustCompilerCalls { target_crate },
using_internal_features,
Expand All @@ -382,7 +384,7 @@ fn main() {

// If user has explicitly enabled/disabled isolation
let mut isolation_enabled: Option<bool> = None;
for arg in env::args() {
for arg in args {
if rustc_args.is_empty() {
// Very first arg: binary name.
rustc_args.push(arg);
Expand Down
6 changes: 3 additions & 3 deletions src/tools/tidy/src/ui_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const ENTRY_LIMIT: usize = 900;
// FIXME: The following limits should be reduced eventually.

const ISSUES_ENTRY_LIMIT: usize = 1750;
const ROOT_ENTRY_LIMIT: usize = 872;
const ROOT_ENTRY_LIMIT: usize = 866;

const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files
Expand All @@ -34,8 +34,8 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[
"tests/ui/asm/named-asm-labels.s", // loading an external asm file to test named labels lint
"tests/ui/codegen/mismatched-data-layout.json", // testing mismatched data layout w/ custom targets
"tests/ui/check-cfg/my-awesome-platform.json", // testing custom targets with cfgs
"tests/ui/commandline-argfile-badutf8.args", // passing args via a file
"tests/ui/commandline-argfile.args", // passing args via a file
"tests/ui/argfile/commandline-argfile-badutf8.args", // passing args via a file
"tests/ui/argfile/commandline-argfile.args", // passing args via a file
"tests/ui/crate-loading/auxiliary/libfoo.rlib", // testing loading a manually created rlib
"tests/ui/include-macros/data.bin", // testing including data with the include macros
"tests/ui/include-macros/file.txt", // testing including data with the include macros
Expand Down
17 changes: 17 additions & 0 deletions tests/rustdoc-ui/argfile/commandline-argfile-badutf8-windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Check to see if we can get parameters from an @argsfile file
//
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
// separators. This test uses backslash as the path separator for the command
// line arguments and is only run on windows.
//
//@ only-windows
//@ compile-flags: --cfg cmdline_set @{{src-base}}\argfile\commandline-argfile-badutf8.args

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

#[cfg(not(unbroken))]
compile_error!("unbroken not set");

fn main() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
error: failed to load argument file: UTF-8 error in $DIR/commandline-argfile-badutf8.args

18 changes: 18 additions & 0 deletions tests/rustdoc-ui/argfile/commandline-argfile-badutf8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Check to see if we can get parameters from an @argsfile file
//
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
// separators. We have a duplicated version of this test that uses backslash as
// the path separator for the command line arguments that is only run on
// windows.
//
//@ ignore-windows
//@ compile-flags: --cfg cmdline_set @{{src-base}}/argfile/commandline-argfile-badutf8.args

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

#[cfg(not(unbroken))]
compile_error!("unbroken not set");

fn main() {
}
2 changes: 2 additions & 0 deletions tests/rustdoc-ui/argfile/commandline-argfile-badutf8.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
error: failed to load argument file: UTF-8 error in $DIR/commandline-argfile-badutf8.args

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

#[cfg(not(cmdline_set))]
compile_error!("cmdline_set not set");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing.args: $FILE_MISSING (os error $ERR)

20 changes: 20 additions & 0 deletions tests/rustdoc-ui/argfile/commandline-argfile-missing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Check to see if we can get parameters from an @argsfile file
//
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
// separators. We have a duplicated version of this test that uses backslash as
// the path separator for the command line arguments that is only run on
// windows.
//
//@ ignore-windows
//@ normalize-stderr-test: "os error \d+" -> "os error $$ERR"
//@ normalize-stderr-test: "commandline-argfile-missing.args:[^(]*" -> "commandline-argfile-missing.args: $$FILE_MISSING "
//@ compile-flags: --cfg cmdline_set @{{src-base}}/argfile/commandline-argfile-missing.args

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

#[cfg(not(unbroken))]
compile_error!("unbroken not set");

fn main() {
}
2 changes: 2 additions & 0 deletions tests/rustdoc-ui/argfile/commandline-argfile-missing.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing.args: $FILE_MISSING (os error $ERR)

20 changes: 20 additions & 0 deletions tests/rustdoc-ui/argfile/commandline-argfile-multiple-windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Check to see if we can get parameters from an @argsfile file
//
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
// separators. This test uses backslash as the path separator for the command
// line arguments and is only run on windows.
//
//@ only-windows
//@ normalize-stderr-test: "os error \d+" -> "os error $$ERR"
//@ normalize-stderr-test: "commandline-argfile-missing.args:[^(]*" -> "commandline-argfile-missing.args: $$FILE_MISSING "
//@ normalize-stderr-test: "commandline-argfile-missing2.args:[^(]*" -> "commandline-argfile-missing2.args: $$FILE_MISSING "
//@ 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

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

#[cfg(not(unbroken))]
compile_error!("unbroken not set");

fn main() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing.args: $FILE_MISSING (os error $ERR)

error: failed to load argument file: UTF-8 error in $DIR/commandline-argfile-badutf8.args

error: failed to load argument file: IO error: $DIR/commandline-argfile-missing2.args: $FILE_MISSING (os error $ERR)

21 changes: 21 additions & 0 deletions tests/rustdoc-ui/argfile/commandline-argfile-multiple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Check to see if we can get parameters from an @argsfile file
//
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
// separators. We have a duplicated version of this test that uses backslash as
// the path separator for the command line arguments that is only run on
// windows.
//
//@ ignore-windows
//@ normalize-stderr-test: "os error \d+" -> "os error $$ERR"
//@ normalize-stderr-test: "commandline-argfile-missing.args:[^(]*" -> "commandline-argfile-missing.args: $$FILE_MISSING "
//@ normalize-stderr-test: "commandline-argfile-missing2.args:[^(]*" -> "commandline-argfile-missing2.args: $$FILE_MISSING "
//@ 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

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

#[cfg(not(unbroken))]
compile_error!("unbroken not set");

fn main() {
}
6 changes: 6 additions & 0 deletions tests/rustdoc-ui/argfile/commandline-argfile-multiple.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
error: failed to load argument file: IO error: $DIR/commandline-argfile-missing.args: $FILE_MISSING (os error $ERR)

error: failed to load argument file: UTF-8 error in $DIR/commandline-argfile-badutf8.args

error: failed to load argument file: IO error: $DIR/commandline-argfile-missing2.args: $FILE_MISSING (os error $ERR)

File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Check to see if we can get parameters from an @argsfile file
//
//@ check-pass
//@ compile-flags: --cfg cmdline_set @{{src-base}}/commandline-argfile.args
//@ compile-flags: --cfg cmdline_set @{{src-base}}/argfile/commandline-argfile.args

#[cfg(not(cmdline_set))]
compile_error!("cmdline_set not set");
Expand Down
12 changes: 0 additions & 12 deletions tests/rustdoc-ui/commandline-argfile-badutf8.rs

This file was deleted.

2 changes: 0 additions & 2 deletions tests/rustdoc-ui/commandline-argfile-badutf8.stderr

This file was deleted.

2 changes: 0 additions & 2 deletions tests/rustdoc-ui/commandline-argfile-missing.stderr

This file was deleted.

17 changes: 17 additions & 0 deletions tests/ui/argfile/commandline-argfile-badutf8-windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Check to see if we can get parameters from an @argsfile file
//
// Path replacement in .stderr files (i.e. `$DIR`) doesn't handle mixed path
// separators. This test uses backslash as the path separator for the command
// line arguments and is only run on windows.
//
//@ only-windows
//@ compile-flags: --cfg cmdline_set @{{src-base}}\argfile\commandline-argfile-badutf8.args

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

#[cfg(not(unbroken))]
compile_error!("unbroken not set");

fn main() {
}
Loading

0 comments on commit a8e3543

Please sign in to comment.