Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor pre-getopts command line argument handling #121194

Merged
merged 4 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)

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
Loading