Skip to content

Commit 05d494f

Browse files
Rollup merge of #150767 - all-help, r=jieyouxu
Allow invoking all help options at once Implements #150442 Help messages are printed in the order they are invoked, but only once (e.g. `--help -C help --help` prints regular help then codegen help). Lint groups (`-Whelp`) are always printed last due to necessarily coming later in the compiler pipeline. Adds `help` flags to `-C` and `-Z` to avoid an error in `build_session_options`. cc @bjorn3 [#t-compiler/major changes > Allow combining `--help -C help -Z help -… compiler-team#954 @ 💬](https://rust-lang.zulipchat.com/#narrow/channel/233931-t-compiler.2Fmajor-changes/topic/Allow.20combining.20.60--help.20-C.20help.20-Z.20help.20-.E2.80.A6.20compiler-team.23954/near/564358190) - this currently maintains the behaviour of unrecognized options always failing-fast, as this happens within `getopt`s parsing, before we can even check if help options are present.
2 parents ba2a7d3 + e834e60 commit 05d494f

3 files changed

Lines changed: 108 additions & 16 deletions

File tree

compiler/rustc_driver_impl/src/lib.rs

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,10 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send))
228228

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

231-
let Some(matches) = handle_options(&default_early_dcx, &args) else {
232-
return;
231+
let (matches, help_only) = match handle_options(&default_early_dcx, &args) {
232+
HandledOptions::None => return,
233+
HandledOptions::Normal(matches) => (matches, false),
234+
HandledOptions::HelpOnly(matches) => (matches, true),
233235
};
234236

235237
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))
291293
return early_exit();
292294
}
293295

296+
// We have now handled all help options, exit
297+
if help_only {
298+
return early_exit();
299+
}
300+
294301
if print_crate_info(codegen_backend, sess, has_input) == Compilation::Stop {
295302
return early_exit();
296303
}
@@ -1097,7 +1104,7 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) ->
10971104
// Don't handle -W help here, because we might first load additional lints.
10981105
let debug_flags = matches.opt_strs("Z");
10991106
if debug_flags.iter().any(|x| *x == "help") {
1100-
describe_debug_flags();
1107+
describe_unstable_flags();
11011108
return true;
11021109
}
11031110

@@ -1137,8 +1144,8 @@ fn get_backend_from_raw_matches(
11371144
get_codegen_backend(early_dcx, &sysroot, backend_name, &target)
11381145
}
11391146

1140-
fn describe_debug_flags() {
1141-
safe_println!("\nAvailable options:\n");
1147+
fn describe_unstable_flags() {
1148+
safe_println!("\nAvailable unstable options:\n");
11421149
print_flag_list("-Z", config::Z_OPTIONS);
11431150
}
11441151

@@ -1162,6 +1169,16 @@ fn print_flag_list<T>(cmdline_opt: &str, flag_list: &[OptionDesc<T>]) {
11621169
}
11631170
}
11641171

1172+
pub enum HandledOptions {
1173+
/// Parsing failed, or we parsed a flag causing an early exit
1174+
None,
1175+
/// Successful parsing
1176+
Normal(getopts::Matches),
1177+
/// Parsing succeeded, but we received one or more 'help' flags
1178+
/// The compiler should proceed only until a possible `-W help` flag has been processed
1179+
HelpOnly(getopts::Matches),
1180+
}
1181+
11651182
/// Process command line options. Emits messages as appropriate. If compilation
11661183
/// should continue, returns a getopts::Matches object parsed from args,
11671184
/// otherwise returns `None`.
@@ -1189,7 +1206,7 @@ fn print_flag_list<T>(cmdline_opt: &str, flag_list: &[OptionDesc<T>]) {
11891206
/// This does not need to be `pub` for rustc itself, but @chaosite needs it to
11901207
/// be public when using rustc as a library, see
11911208
/// <https://github.com/rust-lang/rust/commit/2b4c33817a5aaecabf4c6598d41e190080ec119e>
1192-
pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<getopts::Matches> {
1209+
pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> HandledOptions {
11931210
// Parse with *all* options defined in the compiler, we don't worry about
11941211
// option stability here we just want to parse as much as possible.
11951212
let mut options = getopts::Options::new();
@@ -1235,26 +1252,69 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto
12351252
// (unstable option being used on stable)
12361253
nightly_options::check_nightly_options(early_dcx, &matches, &config::rustc_optgroups());
12371254

1238-
if args.is_empty() || matches.opt_present("h") || matches.opt_present("help") {
1239-
// Only show unstable options in --help if we accept unstable options.
1240-
let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
1241-
let nightly_build = nightly_options::match_is_nightly_build(&matches);
1242-
usage(matches.opt_present("verbose"), unstable_enabled, nightly_build);
1243-
return None;
1255+
// Handle the special case of -Wall.
1256+
let wall = matches.opt_strs("W");
1257+
if wall.iter().any(|x| *x == "all") {
1258+
print_wall_help();
1259+
return HandledOptions::None;
12441260
}
12451261

1246-
if describe_flag_categories(early_dcx, &matches) {
1247-
return None;
1262+
if handle_help(&matches, args) {
1263+
return HandledOptions::HelpOnly(matches);
1264+
}
1265+
1266+
if matches.opt_strs("C").iter().any(|x| x == "passes=list") {
1267+
get_backend_from_raw_matches(early_dcx, &matches).print_passes();
1268+
return HandledOptions::None;
12481269
}
12491270

12501271
if matches.opt_present("version") {
12511272
version!(early_dcx, "rustc", &matches);
1252-
return None;
1273+
return HandledOptions::None;
12531274
}
12541275

12551276
warn_on_confusing_output_filename_flag(early_dcx, &matches, args);
12561277

1257-
Some(matches)
1278+
HandledOptions::Normal(matches)
1279+
}
1280+
1281+
/// Handle help options in the order they are provided, ignoring other flags. Returns if any options were handled
1282+
/// Handled options:
1283+
/// - `-h`/`--help`/empty arguments
1284+
/// - `-Z help`
1285+
/// - `-C help`
1286+
/// NOTE: `-W help` is NOT handled here, as additional lints may be loaded.
1287+
pub fn handle_help(matches: &getopts::Matches, args: &[String]) -> bool {
1288+
let opt_pos = |opt| matches.opt_positions(opt).first().copied();
1289+
let opt_help_pos = |opt| {
1290+
matches
1291+
.opt_strs_pos(opt)
1292+
.iter()
1293+
.filter_map(|(pos, oval)| if oval == "help" { Some(*pos) } else { None })
1294+
.next()
1295+
};
1296+
let help_pos = if args.is_empty() { Some(0) } else { opt_pos("h").or_else(|| opt_pos("help")) };
1297+
let zhelp_pos = opt_help_pos("Z");
1298+
let chelp_pos = opt_help_pos("C");
1299+
let print_help = || {
1300+
// Only show unstable options in --help if we accept unstable options.
1301+
let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
1302+
let nightly_build = nightly_options::match_is_nightly_build(&matches);
1303+
usage(matches.opt_present("verbose"), unstable_enabled, nightly_build);
1304+
};
1305+
1306+
let mut helps = [
1307+
(help_pos, &print_help as &dyn Fn()),
1308+
(zhelp_pos, &describe_unstable_flags),
1309+
(chelp_pos, &describe_codegen_flags),
1310+
];
1311+
helps.sort_by_key(|(pos, _)| pos.clone());
1312+
let mut printed_any = false;
1313+
for printer in helps.iter().filter_map(|(pos, func)| pos.is_some().then_some(func)) {
1314+
printer();
1315+
printed_any = true;
1316+
}
1317+
printed_any
12581318
}
12591319

12601320
/// Warn if `-o` is used without a space between the flag name and the value

compiler/rustc_session/src/options.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2126,6 +2126,7 @@ options! {
21262126
#[rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field")]
21272127
force_unwind_tables: Option<bool> = (None, parse_opt_bool, [TRACKED],
21282128
"force use of unwind tables"),
2129+
help: bool = (false, parse_no_value, [UNTRACKED], "Print codegen options"),
21292130
incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
21302131
"enable incremental compilation"),
21312132
#[rustc_lint_opt_deny_field_access("documented to do nothing")]
@@ -2398,6 +2399,7 @@ options! {
23982399
environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
23992400
has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED],
24002401
"explicitly enable the `cfg(target_thread_local)` directive"),
2402+
help: bool = (false, parse_no_value, [UNTRACKED], "Print unstable compiler options"),
24012403
higher_ranked_assumptions: bool = (false, parse_bool, [TRACKED],
24022404
"allow deducing higher-ranked outlives assumptions from coroutines when proving auto traits"),
24032405
hint_mostly_unused: bool = (false, parse_bool, [TRACKED],

tests/run-make/rustc-help/rmake.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,34 @@ fn main() {
1818
// Check the diff between `rustc --help` and `rustc --help -v`.
1919
let help_v_diff = similar::TextDiff::from_lines(&help, &help_v).unified_diff().to_string();
2020
diff().expected_file("help-v.diff").actual_text("actual", &help_v_diff).run();
21+
22+
// Check that all help options can be invoked at once
23+
let codegen_help = bare_rustc().arg("-Chelp").run().stdout_utf8();
24+
let unstable_help = bare_rustc().arg("-Zhelp").run().stdout_utf8();
25+
let lints_help = bare_rustc().arg("-Whelp").run().stdout_utf8();
26+
let expected_all = format!("{help}{codegen_help}{unstable_help}{lints_help}");
27+
let all_help = bare_rustc().args(["--help", "-Chelp", "-Zhelp", "-Whelp"]).run().stdout_utf8();
28+
diff()
29+
.expected_text(
30+
"(rustc --help && rustc -Chelp && rustc -Zhelp && rustc -Whelp)",
31+
&expected_all,
32+
)
33+
.actual_text("(rustc --help -Chelp -Zhelp -Whelp)", &all_help)
34+
.run();
35+
36+
// Check that the ordering of help options is respected
37+
// Note that this is except for `-Whelp`, which always comes last
38+
let expected_ordered_help = format!("{unstable_help}{codegen_help}{help}{lints_help}");
39+
let ordered_help =
40+
bare_rustc().args(["-Whelp", "-Zhelp", "-Chelp", "--help"]).run().stdout_utf8();
41+
diff()
42+
.expected_text(
43+
"(rustc -Whelp && rustc -Zhelp && rustc -Chelp && rustc --help)",
44+
&expected_ordered_help,
45+
)
46+
.actual_text("(rustc -Whelp -Zhelp -Chelp --help)", &ordered_help)
47+
.run();
48+
49+
// Test that `rustc --help` does not suppress invalid flag errors
50+
let help = bare_rustc().arg("--help --invalid-flag").run_fail().stdout_utf8();
2151
}

0 commit comments

Comments
 (0)