diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index f835613cfcbb9..ea08bf021fbba 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -749,24 +749,20 @@ pub fn build_target_config(opts: &Options, sp: &Handler) -> Config { } } -/// Returns the "short" subset of the stable rustc command line options. -pub fn short_optgroups() -> Vec { - rustc_short_optgroups().into_iter() - .filter(|g|g.is_stable()) - .map(|g|g.opt_group) - .collect() -} - -/// Returns all of the stable rustc command line options. -pub fn optgroups() -> Vec { - rustc_optgroups().into_iter() - .filter(|g|g.is_stable()) - .map(|g|g.opt_group) - .collect() -} - #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum OptionStability { Stable, Unstable } +pub enum OptionStability { + Stable, + + // FIXME: historically there were some options which were either `-Z` or + // required the `-Z unstable-options` flag, which were all intended + // to be unstable. Unfortunately we didn't actually gate usage of + // these options on the stable compiler, so we still allow them there + // today. There are some warnings printed out about this in the + // driver. + UnstableButNotReally, + + Unstable, +} #[derive(Clone, PartialEq, Eq)] pub struct RustcOptGroup { @@ -783,9 +779,17 @@ impl RustcOptGroup { RustcOptGroup { opt_group: g, stability: OptionStability::Stable } } + #[allow(dead_code)] // currently we have no "truly unstable" options fn unstable(g: getopts::OptGroup) -> RustcOptGroup { RustcOptGroup { opt_group: g, stability: OptionStability::Unstable } } + + fn unstable_bnr(g: getopts::OptGroup) -> RustcOptGroup { + RustcOptGroup { + opt_group: g, + stability: OptionStability::UnstableButNotReally, + } + } } // The `opt` local module holds wrappers around the `getopts` API that @@ -807,24 +811,57 @@ mod opt { fn stable(g: getopts::OptGroup) -> R { RustcOptGroup::stable(g) } fn unstable(g: getopts::OptGroup) -> R { RustcOptGroup::unstable(g) } + fn unstable_bnr(g: getopts::OptGroup) -> R { RustcOptGroup::unstable_bnr(g) } - // FIXME (pnkfelix): We default to stable since the current set of - // options is defacto stable. However, it would be good to revise the - // code so that a stable option is the thing that takes extra effort - // to encode. - - pub fn opt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optopt(a, b, c, d)) } - pub fn multi(a: S, b: S, c: S, d: S) -> R { stable(getopts::optmulti(a, b, c, d)) } - pub fn flag(a: S, b: S, c: S) -> R { stable(getopts::optflag(a, b, c)) } - pub fn flagopt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optflagopt(a, b, c, d)) } - pub fn flagmulti(a: S, b: S, c: S) -> R { stable(getopts::optflagmulti(a, b, c)) } + pub fn opt_s(a: S, b: S, c: S, d: S) -> R { + stable(getopts::optopt(a, b, c, d)) + } + pub fn multi_s(a: S, b: S, c: S, d: S) -> R { + stable(getopts::optmulti(a, b, c, d)) + } + pub fn flag_s(a: S, b: S, c: S) -> R { + stable(getopts::optflag(a, b, c)) + } + pub fn flagopt_s(a: S, b: S, c: S, d: S) -> R { + stable(getopts::optflagopt(a, b, c, d)) + } + pub fn flagmulti_s(a: S, b: S, c: S) -> R { + stable(getopts::optflagmulti(a, b, c)) + } + pub fn opt(a: S, b: S, c: S, d: S) -> R { + unstable(getopts::optopt(a, b, c, d)) + } + pub fn multi(a: S, b: S, c: S, d: S) -> R { + unstable(getopts::optmulti(a, b, c, d)) + } + pub fn flag(a: S, b: S, c: S) -> R { + unstable(getopts::optflag(a, b, c)) + } + pub fn flagopt(a: S, b: S, c: S, d: S) -> R { + unstable(getopts::optflagopt(a, b, c, d)) + } + pub fn flagmulti(a: S, b: S, c: S) -> R { + unstable(getopts::optflagmulti(a, b, c)) + } - pub fn opt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optopt(a, b, c, d)) } - pub fn multi_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optmulti(a, b, c, d)) } - pub fn flag_u(a: S, b: S, c: S) -> R { unstable(getopts::optflag(a, b, c)) } - pub fn flagopt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optflagopt(a, b, c, d)) } - pub fn flagmulti_u(a: S, b: S, c: S) -> R { unstable(getopts::optflagmulti(a, b, c)) } + // Do not use these functions for any new options added to the compiler, all + // new options should use the `*_u` variants above to be truly unstable. + pub fn opt_ubnr(a: S, b: S, c: S, d: S) -> R { + unstable_bnr(getopts::optopt(a, b, c, d)) + } + pub fn multi_ubnr(a: S, b: S, c: S, d: S) -> R { + unstable_bnr(getopts::optmulti(a, b, c, d)) + } + pub fn flag_ubnr(a: S, b: S, c: S) -> R { + unstable_bnr(getopts::optflag(a, b, c)) + } + pub fn flagopt_ubnr(a: S, b: S, c: S, d: S) -> R { + unstable_bnr(getopts::optflagopt(a, b, c, d)) + } + pub fn flagmulti_ubnr(a: S, b: S, c: S) -> R { + unstable_bnr(getopts::optflagmulti(a, b, c)) + } } /// Returns the "short" subset of the rustc command line options, @@ -832,44 +869,44 @@ mod opt { /// part of the stable long-term interface for rustc. pub fn rustc_short_optgroups() -> Vec { vec![ - opt::flag("h", "help", "Display this message"), - opt::multi("", "cfg", "Configure the compilation environment", "SPEC"), - opt::multi("L", "", "Add a directory to the library search path", + opt::flag_s("h", "help", "Display this message"), + opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"), + opt::multi_s("L", "", "Add a directory to the library search path", "[KIND=]PATH"), - opt::multi("l", "", "Link the generated crate(s) to the specified native + opt::multi_s("l", "", "Link the generated crate(s) to the specified native library NAME. The optional KIND can be one of, static, dylib, or framework. If omitted, dylib is assumed.", "[KIND=]NAME"), - opt::multi("", "crate-type", "Comma separated list of types of crates + opt::multi_s("", "crate-type", "Comma separated list of types of crates for the compiler to emit", "[bin|lib|rlib|dylib|staticlib]"), - opt::opt("", "crate-name", "Specify the name of the crate being built", + opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"), - opt::multi("", "emit", "Comma separated list of types of output for \ + opt::multi_s("", "emit", "Comma separated list of types of output for \ the compiler to emit", "[asm|llvm-bc|llvm-ir|obj|link|dep-info]"), - opt::multi("", "print", "Comma separated list of compiler information to \ + opt::multi_s("", "print", "Comma separated list of compiler information to \ print on stdout", "[crate-name|file-names|sysroot|target-list]"), - opt::flagmulti("g", "", "Equivalent to -C debuginfo=2"), - opt::flagmulti("O", "", "Equivalent to -C opt-level=2"), - opt::opt("o", "", "Write output to ", "FILENAME"), - opt::opt("", "out-dir", "Write output to compiler-chosen filename \ + opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"), + opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"), + opt::opt_s("o", "", "Write output to ", "FILENAME"), + opt::opt_s("", "out-dir", "Write output to compiler-chosen filename \ in ", "DIR"), - opt::opt("", "explain", "Provide a detailed explanation of an error \ + opt::opt_s("", "explain", "Provide a detailed explanation of an error \ message", "OPT"), - opt::flag("", "test", "Build a test harness"), - opt::opt("", "target", "Target triple for which the code is compiled", "TARGET"), - opt::multi("W", "warn", "Set lint warnings", "OPT"), - opt::multi("A", "allow", "Set lint allowed", "OPT"), - opt::multi("D", "deny", "Set lint denied", "OPT"), - opt::multi("F", "forbid", "Set lint forbidden", "OPT"), - opt::multi("", "cap-lints", "Set the most restrictive lint level. \ + opt::flag_s("", "test", "Build a test harness"), + opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"), + opt::multi_s("W", "warn", "Set lint warnings", "OPT"), + opt::multi_s("A", "allow", "Set lint allowed", "OPT"), + opt::multi_s("D", "deny", "Set lint denied", "OPT"), + opt::multi_s("F", "forbid", "Set lint forbidden", "OPT"), + opt::multi_s("", "cap-lints", "Set the most restrictive lint level. \ More restrictive lints are capped at this \ level", "LEVEL"), - opt::multi("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), - opt::flag("V", "version", "Print version info and exit"), - opt::flag("v", "verbose", "Use verbose output"), + opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"), + opt::flag_s("V", "version", "Print version info and exit"), + opt::flag_s("v", "verbose", "Use verbose output"), ] } @@ -879,24 +916,26 @@ pub fn rustc_short_optgroups() -> Vec { pub fn rustc_optgroups() -> Vec { let mut opts = rustc_short_optgroups(); opts.extend_from_slice(&[ - opt::multi("", "extern", "Specify where an external rust library is \ + opt::multi_s("", "extern", "Specify where an external rust library is \ located", "NAME=PATH"), - opt::opt("", "sysroot", "Override the system root", "PATH"), - opt::multi("Z", "", "Set internal debugging options", "FLAG"), - opt::opt_u("", "error-format", "How errors and other messages are produced", "human|json"), - opt::opt("", "color", "Configure coloring of output: + opt::opt_s("", "sysroot", "Override the system root", "PATH"), + opt::multi_ubnr("Z", "", "Set internal debugging options", "FLAG"), + opt::opt_ubnr("", "error-format", + "How errors and other messages are produced", + "human|json"), + opt::opt_s("", "color", "Configure coloring of output: auto = colorize, if output goes to a tty (default); always = always colorize output; never = never colorize output", "auto|always|never"), - opt::flagopt_u("", "pretty", + opt::flagopt_ubnr("", "pretty", "Pretty-print the input instead of compiling; valid types are: `normal` (un-annotated source), `expanded` (crates expanded), or `expanded,identified` (fully parenthesized, AST nodes with IDs).", "TYPE"), - opt::flagopt_u("", "unpretty", + opt::flagopt_ubnr("", "unpretty", "Present the input source, unstable (and less-pretty) variants; valid types are any of the types for `--pretty`, as well as: `flowgraph=` (graphviz formatted flowgraph for node), @@ -904,6 +943,14 @@ pub fn rustc_optgroups() -> Vec { `hir` (the HIR), `hir,identified`, or `hir,typed` (HIR with types for each node).", "TYPE"), + + // new options here should **not** use the `_ubnr` functions, all new + // unstable options should use the short variants to indicate that they + // are truly unstable. All `_ubnr` flags are just that way because they + // were so historically. + // + // You may also wish to keep this comment at the bottom of this list to + // ensure that others see it. ]); opts } @@ -1242,15 +1289,21 @@ impl fmt::Display for CrateType { #[cfg(test)] mod tests { use middle::cstore::DummyCrateStore; - use session::config::{build_configuration, optgroups, build_session_options}; + use session::config::{build_configuration, build_session_options}; use session::build_session; use std::rc::Rc; - use getopts::getopts; + use getopts::{getopts, OptGroup}; use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::diagnostics; + fn optgroups() -> Vec { + super::rustc_optgroups().into_iter() + .map(|a| a.opt_group) + .collect() + } + // When the user supplies --test we should implicitly supply --cfg test #[test] fn test_switch_implies_cfg_test() { diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index e77663091106b..d0f86cfcb46ba 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -65,6 +65,7 @@ use rustc_trans::back::link; use rustc_trans::save; use rustc::session::{config, Session, build_session, CompileResult}; use rustc::session::config::{Input, PrintRequest, OutputType, ErrorOutputType}; +use rustc::session::config::{get_unstable_features_setting, OptionStability}; use rustc::middle::cstore::CrateStore; use rustc::lint::Lint; use rustc::lint; @@ -85,7 +86,7 @@ use std::str; use std::sync::{Arc, Mutex}; use std::thread; -use rustc::session::early_error; +use rustc::session::{early_error, early_warn}; use syntax::ast; use syntax::parse; @@ -93,6 +94,7 @@ use syntax::errors; use syntax::errors::emitter::Emitter; use syntax::diagnostics; use syntax::parse::token; +use syntax::feature_gate::UnstableFeatures; #[cfg(test)] pub mod test; @@ -819,8 +821,31 @@ fn print_flag_list(cmdline_opt: &str, } /// Process command line options. Emits messages as appropriate. If compilation -/// should continue, returns a getopts::Matches object parsed from args, otherwise -/// returns None. +/// should continue, returns a getopts::Matches object parsed from args, +/// otherwise returns None. +/// +/// The compiler's handling of options is a little complication as it ties into +/// our stability story, and it's even *more* complicated by historical +/// accidents. The current intention of each compiler option is to have one of +/// three modes: +/// +/// 1. An option is stable and can be used everywhere. +/// 2. An option is unstable, but was historically allowed on the stable +/// channel. +/// 3. An option is unstable, and can only be used on nightly. +/// +/// Like unstable library and language features, however, unstable options have +/// always required a form of "opt in" to indicate that you're using them. This +/// provides the easy ability to scan a code base to check to see if anything +/// unstable is being used. Currently, this "opt in" is the `-Z` "zed" flag. +/// +/// All options behind `-Z` are considered unstable by default. Other top-level +/// options can also be considered unstable, and they were unlocked through the +/// `-Z unstable-options` flag. Note that `-Z` remains to be the root of +/// instability in both cases, though. +/// +/// So with all that in mind, the comments below have some more detail about the +/// contortions done here to get things to work out correctly. pub fn handle_options(mut args: Vec) -> Option { // Throw away the first argument, the name of the binary let _binary = args.remove(0); @@ -832,62 +857,83 @@ pub fn handle_options(mut args: Vec) -> Option { return None; } - fn allows_unstable_options(matches: &getopts::Matches) -> bool { - let r = matches.opt_strs("Z"); - r.iter().any(|x| *x == "unstable-options") - } + // 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 all_groups: Vec = config::rustc_optgroups() + .into_iter() + .map(|x| x.opt_group) + .collect(); + let matches = match getopts::getopts(&args[..], &all_groups) { + Ok(m) => m, + Err(f) => early_error(ErrorOutputType::default(), &f.to_string()), + }; - 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 { - &opt.opt_group.short_name - }; - if m.opt_present(opt_name) { - early_error(ErrorOutputType::default(), - &format!("use of unstable option '{}' requires -Z \ - unstable-options", - opt_name)); - } - } - } - m - } - Err(f) => early_error(ErrorOutputType::default(), &f.to_string()), + // For all options we just parsed, we check a few aspects: + // + // * If the option is stable, we're all good + // * If the option wasn't passed, we're all good + // * If `-Z unstable-options` wasn't passed (and we're not a -Z option + // ourselves), then we require the `-Z unstable-options` flag to unlock + // this option that was passed. + // * If we're a nightly compiler, then unstable options are now unlocked, so + // we're good to go. + // * Otherwise, if we're a truly unstable option then we generate an error + // (unstable option being used on stable) + // * If we're a historically stable-but-should-be-unstable option then we + // emit a warning that we're going to turn this into an error soon. + let has_z_unstable_options = matches.opt_strs("Z") + .iter() + .any(|x| *x == "unstable-options"); + let really_allows_unstable_options = match get_unstable_features_setting() { + UnstableFeatures::Disallow => false, + _ => true, + }; + for opt in config::rustc_optgroups() { + if opt.stability == OptionStability::Stable { + continue } - } - - // 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) + let opt_name = if !opt.opt_group.long_name.is_empty() { + &opt.opt_group.long_name + } else { + &opt.opt_group.short_name + }; + if !matches.opt_present(opt_name) { + continue } - 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 opt_name != "Z" && !has_z_unstable_options { + let msg = format!("the `-Z unstable-options` flag must also be \ + passed to enable the flag `{}`", opt_name); + early_error(ErrorOutputType::default(), &msg); } - }; + if really_allows_unstable_options { + continue + } + match opt.stability { + OptionStability::Unstable => { + let msg = format!("the option `{}` is only accepted on the \ + nightly compiler", opt_name); + early_error(ErrorOutputType::default(), &msg); + } + OptionStability::UnstableButNotReally => { + let msg = format!("the option `{}` is is unstable and should \ + only be used on the nightly compiler, but \ + it is currently accepted for backwards \ + compatibility; this will soon change, \ + see issue #31847 for more details", + opt_name); + early_warn(ErrorOutputType::default(), &msg); + } + OptionStability::Stable => {} + } + } if matches.opt_present("h") || matches.opt_present("help") { + // Only show unstable options in --help if we *really* accept unstable + // options, which catches the case where we got `-Z unstable-options` on + // the stable channel of Rust which was accidentally allowed + // historically. usage(matches.opt_present("verbose"), - allows_unstable_options(&matches)); + has_z_unstable_options && really_allows_unstable_options); return None; }