diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 7820198f2dcf2..81d25a307bddd 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -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); @@ -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(); } @@ -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; } @@ -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); } @@ -1156,6 +1163,16 @@ fn print_flag_list(cmdline_opt: &str, flag_list: &[OptionDesc]) { } } +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`. @@ -1183,7 +1200,7 @@ fn print_flag_list(cmdline_opt: &str, flag_list: &[OptionDesc]) { /// This does not need to be `pub` for rustc itself, but @chaosite needs it to /// be public when using rustc as a library, see /// -pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option { +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(); @@ -1229,26 +1246,69 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option 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 diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 21fa3321a30ad..0a29d45457d52 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -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 = (None, parse_opt_bool, [TRACKED], "force use of unwind tables"), + help: bool = (false, parse_no_value, [UNTRACKED], "Print codegen options"), incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), #[rustc_lint_opt_deny_field_access("documented to do nothing")] @@ -2395,6 +2396,7 @@ options! { environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), has_thread_local: Option = (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], diff --git a/tests/run-make/rustc-help/rmake.rs b/tests/run-make/rustc-help/rmake.rs index 85e90e6352d09..17811ef18449f 100644 --- a/tests/run-make/rustc-help/rmake.rs +++ b/tests/run-make/rustc-help/rmake.rs @@ -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(); }