Change:
return None; } <del> 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<getopts::OptGroup> = 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 </del> <ins> 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<String>) -> getopts::Matches { let all_groups : Vec<getopts::OptGroup> = 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 </ins> } else { <del> early_error(&f_stable_attempt.to_string()); </del> <ins> &opt.opt_group.short_name }; if m.opt_present(opt_name) { early_error(&format!("use of unstable option '{}' requires  -Z unstable-options", opt_name)); </ins> } } <del> 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()); } </del> } <ins> m </ins> } <del> }; </del> <ins> Err(f) => early_error(&f.to_string()) } } </ins> <del> let r = matches.opt_strs("Z"); let include_unstable_options = r.iter().any(|x| *x == "unstable-options"); </del> <ins> // 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) } }; </ins> if matches.opt_present("h") || matches.opt_present("help") { <del> usage(matches.opt_present("verbose"), include_unstable_options); </del> <ins> usage(matches.opt_present("verbose"), allows_unstable_options(&matches)); </ins> return None; }

Discussion:
cc
Oh dear, sorry about that!
Just hit this myself - it would seem that the pretty option has disappeared too, making it very difficult to debug compiler plugins.
I believe that this specific error has been fixed, only to be replaced by this: Note that the unspecified options is now instead of .
And it also happens if you simply do .
If I understand correctly, the problem is that is missing from the here in : It's missing due to the way we parse unstable options in : we try to parse command-line arguments using an that only defines the stable options. If we succeed, then we return a that only records the results for the stable options. ( is an unstable option, so it is omitted in this case.) we failed, then maybe it was because the command-line had an unstable option on it. We parse arguments again, this time using a value with both stable and unstable options. If this succeeds, and if we found in the result, then we return a recording results for all options. One obvious fix is to ensure that records the unstable options whenever is set. If the first parse succeeds, but that flag is set, then reparse. I think I have a fix; I'll test it.
Is it worth considering adding one or more tests covering these use-cases for invoking itself? It sounds like having such a test would have prevented the prior implementation (I did not want to block on such a requirement, but it might be a nice followup task.)
I think so. I'll look into it.
actually, doing this nicely might be harder than I initially thought, since in all the cases listed above, the invocation is expected to return a non-zero error code, right? ... so the test is going to need to grep through the output ... it does not sound like a very fun test to maintain ... hmm. Update: I guess we could at least ensure that we do not emit ; that is probably good enough.
It's supposed to succeed quietly in the case where is passed. At one point, I had written a test to verify this, but I nixed it because running the test seemed wasteful. We could test this by adding to any existing test, but that conflates things, I think. AFAIK, we don't have a test verifying that unstable options require to work.

I guess I was solely looking at the original description ... running on a newly created shouldn't quietly succeed. But clearly you have a different test in mind, so okay.
Oh, right, the original test should still have failed for lack of a function.