From 61004f88d1a250d2086c0ad28f005380a6a64f9f Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Wed, 11 Mar 2015 01:42:00 -0700 Subject: [PATCH] Improve -Z unstable-options diagnostics and avoid an ICE * Consumers of handle_options assume the unstable options are defined in the getopts::Matches value if -Z unstable-options is set, but that's not the case if there weren't any actual unstable options. Fix this by always reparsing options when -Z unstable-options is set. * If both argument parsing attempts fail, print the error from the second attempt rather than the first. The error from the first is very poor whenever unstable options are present. e.g.: $ rustc hello.rs -Z unstable-options --show-span error: Unrecognized option: 'show-span'. $ rustc hello.rs -Z unstable-options --pretty --pretty error: Unrecognized option: 'pretty'. $ rustc hello.rs -Z unstable-options --pretty --bad-option error: Unrecognized option: 'pretty'. * On the second parse, add a separate pass to reject unstable options if -Z unstable-options wasn't specified. Fixes #21715. r? @pnkfelix --- src/librustc_driver/lib.rs | 68 ++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index c09b018ab63..0af5de06852 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -688,39 +688,57 @@ pub fn handle_options(mut args: Vec) -> Option { return None; } - let matches = - match getopts::getopts(&args[..], &config::optgroups()) { - Ok(m) => m, - Err(f_stable_attempt) => { - // redo option parsing, including unstable options this time, - // in anticipation that the mishandled option was one of the - // unstable ones. - let all_groups : Vec - = config::rustc_optgroups().into_iter().map(|x|x.opt_group).collect(); - match getopts::getopts(&args, &all_groups) { - Ok(m_unstable) => { - let r = m_unstable.opt_strs("Z"); - let include_unstable_options = r.iter().any(|x| *x == "unstable-options"); - if include_unstable_options { - m_unstable + fn allows_unstable_options(matches: &getopts::Matches) -> bool { + let r = matches.opt_strs("Z"); + r.iter().any(|x| *x == "unstable-options") + } + + fn parse_all_options(args: &Vec) -> getopts::Matches { + let all_groups : Vec + = config::rustc_optgroups().into_iter().map(|x|x.opt_group).collect(); + match getopts::getopts(&args[..], &all_groups) { + Ok(m) => { + if !allows_unstable_options(&m) { + // If -Z unstable-options was not specified, verify that + // no unstable options were present. + for opt in config::rustc_optgroups().into_iter().filter(|x| !x.is_stable()) { + let opt_name = if !opt.opt_group.long_name.is_empty() { + &opt.opt_group.long_name } else { - early_error(&f_stable_attempt.to_string()); + &opt.opt_group.short_name + }; + if m.opt_present(opt_name) { + early_error(&format!("use of unstable option '{}' requires \ + -Z unstable-options", opt_name)); } } - Err(_) => { - // ignore the error from the unstable attempt; just - // pass the error we got from the first try. - early_error(&f_stable_attempt.to_string()); - } } + m } - }; + Err(f) => early_error(&f.to_string()) + } + } - let r = matches.opt_strs("Z"); - let include_unstable_options = r.iter().any(|x| *x == "unstable-options"); + // As a speed optimization, first try to parse the command-line using just + // the stable options. + let matches = match getopts::getopts(&args[..], &config::optgroups()) { + Ok(ref m) if allows_unstable_options(m) => { + // If -Z unstable-options was specified, redo parsing with the + // unstable options to ensure that unstable options are defined + // in the returned getopts::Matches. + parse_all_options(&args) + } + Ok(m) => m, + Err(_) => { + // redo option parsing, including unstable options this time, + // in anticipation that the mishandled option was one of the + // unstable ones. + parse_all_options(&args) + } + }; if matches.opt_present("h") || matches.opt_present("help") { - usage(matches.opt_present("verbose"), include_unstable_options); + usage(matches.opt_present("verbose"), allows_unstable_options(&matches)); return None; }