about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/session/config.rs181
-rw-r--r--src/librustc_driver/lib.rs150
2 files changed, 215 insertions, 116 deletions
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index f835613cfcb..ea08bf021fb 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<getopts::OptGroup> {
-    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<getopts::OptGroup> {
-    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<RustcOptGroup> {
     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>", "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>", "FILENAME"),
+        opt::opt_s("",  "out-dir", "Write output to compiler-chosen filename \
                                 in <dir>", "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<RustcOptGroup> {
 pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
     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=<nodeid>` (graphviz formatted flowgraph for node),
@@ -904,6 +943,14 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
                       `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<OptGroup> {
+        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 e7766309110..d0f86cfcb46 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<T>(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<String>) -> Option<getopts::Matches> {
     // 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<String>) -> Option<getopts::Matches> {
         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<getopts::OptGroup> = 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<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
-                        } 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;
     }