Skip to content
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
92 changes: 76 additions & 16 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,10 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))

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

let Some(matches) = handle_options(&default_early_dcx, &args) else {
return;
let (matches, help_only) = match handle_options(&default_early_dcx, &args) {
HandledOptions::None => return,
HandledOptions::Normal(matches) => (matches, false),
HandledOptions::HelpOnly(matches) => (matches, true),
};

let sopts = config::build_session_options(&mut default_early_dcx, &matches);
Expand Down Expand Up @@ -291,6 +293,11 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))
return early_exit();
}

// We have now handled all help options, exit
if help_only {
return early_exit();
}

if print_crate_info(codegen_backend, sess, has_input) == Compilation::Stop {
return early_exit();
}
Expand Down Expand Up @@ -1092,7 +1099,7 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) ->
// Don't handle -W help here, because we might first load additional lints.
let debug_flags = matches.opt_strs("Z");
if debug_flags.iter().any(|x| *x == "help") {
describe_debug_flags();
describe_unstable_flags();
return true;
}

Expand Down Expand Up @@ -1131,8 +1138,8 @@ fn get_backend_from_raw_matches(
get_codegen_backend(early_dcx, &sysroot, backend_name, &target)
}

fn describe_debug_flags() {
safe_println!("\nAvailable options:\n");
fn describe_unstable_flags() {
safe_println!("\nAvailable unstable options:\n");
print_flag_list("-Z", config::Z_OPTIONS);
}

Expand All @@ -1156,6 +1163,16 @@ fn print_flag_list<T>(cmdline_opt: &str, flag_list: &[OptionDesc<T>]) {
}
}

pub enum HandledOptions {
/// Parsing failed, or we parsed a flag causing an early exit
None,
/// Successful parsing
Normal(getopts::Matches),
/// Parsing succeeded, but we received one or more 'help' flags
/// The compiler should proceed only until a possible `-W help` flag has been processed
HelpOnly(getopts::Matches),
}

/// Process command line options. Emits messages as appropriate. If compilation
/// should continue, returns a getopts::Matches object parsed from args,
/// otherwise returns `None`.
Expand Down Expand Up @@ -1183,7 +1200,7 @@ fn print_flag_list<T>(cmdline_opt: &str, flag_list: &[OptionDesc<T>]) {
/// This does not need to be `pub` for rustc itself, but @chaosite needs it to
/// be public when using rustc as a library, see
/// <https://github.com/rust-lang/rust/commit/2b4c33817a5aaecabf4c6598d41e190080ec119e>
pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<getopts::Matches> {
pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> HandledOptions {
// Parse with *all* options defined in the compiler, we don't worry about
// option stability here we just want to parse as much as possible.
let mut options = getopts::Options::new();
Expand Down Expand Up @@ -1229,26 +1246,69 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
// (unstable option being used on stable)
nightly_options::check_nightly_options(early_dcx, &matches, &config::rustc_optgroups());

if args.is_empty() || matches.opt_present("h") || matches.opt_present("help") {
// Only show unstable options in --help if we accept unstable options.
let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
let nightly_build = nightly_options::match_is_nightly_build(&matches);
usage(matches.opt_present("verbose"), unstable_enabled, nightly_build);
return None;
// Handle the special case of -Wall.
let wall = matches.opt_strs("W");
if wall.iter().any(|x| *x == "all") {
print_wall_help();
return HandledOptions::None;
}

if describe_flag_categories(early_dcx, &matches) {
return None;
if handle_help(&matches, args) {
return HandledOptions::HelpOnly(matches);
}

if matches.opt_strs("C").iter().any(|x| x == "passes=list") {
get_backend_from_raw_matches(early_dcx, &matches).print_passes();
return HandledOptions::None;
}

if matches.opt_present("version") {
version!(early_dcx, "rustc", &matches);
return None;
return HandledOptions::None;
}

warn_on_confusing_output_filename_flag(early_dcx, &matches, args);

Some(matches)
HandledOptions::Normal(matches)
}

/// Handle help options in the order they are provided, ignoring other flags. Returns if any options were handled
/// Handled options:
/// - `-h`/`--help`/empty arguments
/// - `-Z help`
/// - `-C help`
/// NOTE: `-W help` is NOT handled here, as additional lints may be loaded.
pub fn handle_help(matches: &getopts::Matches, args: &[String]) -> bool {
let opt_pos = |opt| matches.opt_positions(opt).first().copied();
let opt_help_pos = |opt| {
matches
.opt_strs_pos(opt)
.iter()
.filter_map(|(pos, oval)| if oval == "help" { Some(*pos) } else { None })
.next()
};
let help_pos = if args.is_empty() { Some(0) } else { opt_pos("h").or_else(|| opt_pos("help")) };
let zhelp_pos = opt_help_pos("Z");
let chelp_pos = opt_help_pos("C");
let print_help = || {
// Only show unstable options in --help if we accept unstable options.
let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
let nightly_build = nightly_options::match_is_nightly_build(&matches);
usage(matches.opt_present("verbose"), unstable_enabled, nightly_build);
};

let mut helps = [
(help_pos, &print_help as &dyn Fn()),
(zhelp_pos, &describe_unstable_flags),
(chelp_pos, &describe_codegen_flags),
];
helps.sort_by_key(|(pos, _)| pos.clone());
let mut printed_any = false;
for printer in helps.iter().filter_map(|(pos, func)| pos.is_some().then_some(func)) {
printer();
printed_any = true;
}
printed_any
}

/// Warn if `-o` is used without a space between the flag name and the value
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2125,6 +2125,7 @@ options! {
#[rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field")]
force_unwind_tables: Option<bool> = (None, parse_opt_bool, [TRACKED],
"force use of unwind tables"),
help: bool = (false, parse_no_value, [UNTRACKED], "Print codegen options"),
incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
"enable incremental compilation"),
#[rustc_lint_opt_deny_field_access("documented to do nothing")]
Expand Down Expand Up @@ -2395,6 +2396,7 @@ options! {
environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED],
"explicitly enable the `cfg(target_thread_local)` directive"),
help: bool = (false, parse_no_value, [UNTRACKED], "Print unstable compiler options"),
higher_ranked_assumptions: bool = (false, parse_bool, [TRACKED],
"allow deducing higher-ranked outlives assumptions from coroutines when proving auto traits"),
hint_mostly_unused: bool = (false, parse_bool, [TRACKED],
Expand Down
30 changes: 30 additions & 0 deletions tests/run-make/rustc-help/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,34 @@ fn main() {
// Check the diff between `rustc --help` and `rustc --help -v`.
let help_v_diff = similar::TextDiff::from_lines(&help, &help_v).unified_diff().to_string();
diff().expected_file("help-v.diff").actual_text("actual", &help_v_diff).run();

// Check that all help options can be invoked at once
let codegen_help = bare_rustc().arg("-Chelp").run().stdout_utf8();
let unstable_help = bare_rustc().arg("-Zhelp").run().stdout_utf8();
let lints_help = bare_rustc().arg("-Whelp").run().stdout_utf8();
let expected_all = format!("{help}{codegen_help}{unstable_help}{lints_help}");
let all_help = bare_rustc().args(["--help", "-Chelp", "-Zhelp", "-Whelp"]).run().stdout_utf8();
diff()
.expected_text(
"(rustc --help && rustc -Chelp && rustc -Zhelp && rustc -Whelp)",
&expected_all,
)
.actual_text("(rustc --help -Chelp -Zhelp -Whelp)", &all_help)
.run();

// Check that the ordering of help options is respected
// Note that this is except for `-Whelp`, which always comes last
let expected_ordered_help = format!("{unstable_help}{codegen_help}{help}{lints_help}");
let ordered_help =
bare_rustc().args(["-Whelp", "-Zhelp", "-Chelp", "--help"]).run().stdout_utf8();
diff()
.expected_text(
"(rustc -Whelp && rustc -Zhelp && rustc -Chelp && rustc --help)",
&expected_ordered_help,
)
.actual_text("(rustc -Whelp -Zhelp -Chelp --help)", &ordered_help)
.run();

// Test that `rustc --help` does not suppress invalid flag errors
let help = bare_rustc().arg("--help --invalid-flag").run_fail().stdout_utf8();
}
Loading