about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiletest/header.rs13
-rw-r--r--src/compiletest/runtest.rs10
-rw-r--r--src/librustc/metadata/creader.rs30
-rw-r--r--src/librustc/middle/infer/region_inference/graphviz.rs3
-rw-r--r--src/librustc/plugin/load.rs91
-rw-r--r--src/librustc/session/config.rs317
-rw-r--r--src/librustc/session/mod.rs31
-rw-r--r--src/librustc_borrowck/borrowck/fragments.rs3
-rw-r--r--src/librustc_driver/driver.rs14
-rw-r--r--src/librustc_driver/lib.rs18
-rw-r--r--src/librustc_driver/pretty.rs17
-rw-r--r--src/librustc_driver/test.rs2
-rw-r--r--src/librustc_trans/back/link.rs2
-rw-r--r--src/librustc_trans/back/write.rs2
-rw-r--r--src/libsyntax/codemap.rs7
-rw-r--r--src/libsyntax/diagnostic.rs20
-rw-r--r--src/test/compile-fail/recursion.rs2
-rw-r--r--src/test/run-pass-fulldeps/lint-plugin-cmdline-allow.rs (renamed from src/test/run-pass-fulldeps/lint-plugin-cmdline.rs)0
-rw-r--r--src/test/run-pass-fulldeps/lint-plugin-cmdline-load.rs21
19 files changed, 329 insertions, 274 deletions
diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs
index 2413a001ee8..374fd2e3ef0 100644
--- a/src/compiletest/header.rs
+++ b/src/compiletest/header.rs
@@ -42,6 +42,8 @@ pub struct TestProps {
     pub pretty_compare_only: bool,
     // Patterns which must not appear in the output of a cfail test.
     pub forbid_output: Vec<String>,
+    // Ignore errors which originate from a command line span
+    pub ignore_command_line: bool,
 }
 
 // Load any test directives embedded in the file
@@ -60,6 +62,8 @@ pub fn load_props(testfile: &Path) -> TestProps {
     let mut pretty_mode = None;
     let mut pretty_compare_only = false;
     let mut forbid_output = Vec::new();
+    let mut ignore_command_line = false;
+
     iter_header(testfile, |ln| {
         match parse_error_pattern(ln) {
           Some(ep) => error_patterns.push(ep),
@@ -102,6 +106,10 @@ pub fn load_props(testfile: &Path) -> TestProps {
             pretty_compare_only = parse_pretty_compare_only(ln);
         }
 
+        if !ignore_command_line {
+            ignore_command_line = parse_ignore_command_line(ln);
+        }
+
         match parse_aux_build(ln) {
             Some(ab) => { aux_builds.push(ab); }
             None => {}
@@ -140,6 +148,7 @@ pub fn load_props(testfile: &Path) -> TestProps {
         pretty_mode: pretty_mode.unwrap_or("normal".to_string()),
         pretty_compare_only: pretty_compare_only,
         forbid_output: forbid_output,
+        ignore_command_line: ignore_command_line,
     }
 }
 
@@ -291,6 +300,10 @@ fn parse_pretty_compare_only(line: &str) -> bool {
     parse_name_directive(line, "pretty-compare-only")
 }
 
+fn parse_ignore_command_line(line: &str) -> bool {
+    parse_name_directive(line, "ignore-command-line")
+}
+
 fn parse_exec_env(line: &str) -> Option<(String, String)> {
     parse_name_value_directive(line, "exec-env").map(|nv| {
         // nv is either FOO or FOO=BAR
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index 5de93c52029..a97d4913440 100644
--- a/src/compiletest/runtest.rs
+++ b/src/compiletest/runtest.rs
@@ -104,7 +104,7 @@ fn run_cfail_test(config: &Config, props: &TestProps, testfile: &Path) {
         if !props.error_patterns.is_empty() {
             fatal("both error pattern and expected errors specified");
         }
-        check_expected_errors(expected_errors, testfile, &proc_res);
+        check_expected_errors(props, expected_errors, testfile, &proc_res);
     } else {
         check_error_patterns(props, testfile, output_to_check.as_slice(), &proc_res);
     }
@@ -941,7 +941,8 @@ fn check_forbid_output(props: &TestProps,
     }
 }
 
-fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> ,
+fn check_expected_errors(props: &TestProps,
+                         expected_errors: Vec<errors::ExpectedError> ,
                          testfile: &Path,
                          proc_res: &ProcRes) {
 
@@ -996,6 +997,11 @@ fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> ,
             was_expected = true;
         }
 
+        if line.starts_with("<command line option>") &&
+           props.ignore_command_line {
+            was_expected = true;
+        }
+
         if !was_expected && is_compiler_error_or_warning(line) {
             fatal_proc_rec(format!("unexpected compiler error or warning: '{}'",
                                   line).as_slice(),
diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs
index 310874c311b..4a2981b8cdf 100644
--- a/src/librustc/metadata/creader.rs
+++ b/src/librustc/metadata/creader.rs
@@ -26,7 +26,7 @@ use syntax::ast;
 use syntax::abi;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
-use syntax::codemap::{Span, mk_sp};
+use syntax::codemap::{COMMAND_LINE_SP, Span, mk_sp};
 use syntax::parse;
 use syntax::parse::token::InternedString;
 use syntax::parse::token;
@@ -445,8 +445,20 @@ impl<'a> CrateReader<'a> {
     }
 
     pub fn read_plugin_metadata<'b>(&'b mut self,
-                                    vi: &'b ast::ViewItem) -> PluginMetadata<'b> {
-        let info = self.extract_crate_info(vi).unwrap();
+                                    krate: CrateOrString<'b>) -> PluginMetadata<'b> {
+        let (info, span) = match krate {
+            CrateOrString::Krate(c) => {
+                (self.extract_crate_info(c).unwrap(), c.span)
+            }
+            CrateOrString::Str(s) => {
+                (CrateInfo {
+                     name: s.to_string(),
+                     ident: s.to_string(),
+                     id: ast::DUMMY_NODE_ID,
+                     should_link: true,
+                 }, COMMAND_LINE_SP)
+            }
+        };
         let target_triple = &self.sess.opts.target_triple[];
         let is_cross = target_triple != config::host_triple();
         let mut should_link = info.should_link && !is_cross;
@@ -455,7 +467,7 @@ impl<'a> CrateReader<'a> {
         let name = info.name.clone();
         let mut load_ctxt = loader::Context {
             sess: self.sess,
-            span: vi.span,
+            span: span,
             ident: &ident[],
             crate_name: &name[],
             hash: None,
@@ -486,7 +498,7 @@ impl<'a> CrateReader<'a> {
         let metadata = if register {
             // Register crate now to avoid double-reading metadata
             let (_, cmd, _) = self.register_crate(&None, &info.ident[],
-                                &info.name[], vi.span, library);
+                                &info.name[], span, library);
             PMDSource::Registered(cmd)
         } else {
             // Not registering the crate; just hold on to the metadata
@@ -498,12 +510,18 @@ impl<'a> CrateReader<'a> {
             metadata: metadata,
             dylib: dylib,
             info: info,
-            vi_span: vi.span,
+            vi_span: span,
             target_only: target_only,
         }
     }
 }
 
+#[derive(Copy)]
+pub enum CrateOrString<'a> {
+    Krate(&'a ast::ViewItem),
+    Str(&'a str)
+}
+
 impl<'a> PluginMetadata<'a> {
     /// Read exported macros
     pub fn exported_macros(&self) -> Vec<ast::MacroDef> {
diff --git a/src/librustc/middle/infer/region_inference/graphviz.rs b/src/librustc/middle/infer/region_inference/graphviz.rs
index 29feaf358e2..0d6ab9c273b 100644
--- a/src/librustc/middle/infer/region_inference/graphviz.rs
+++ b/src/librustc/middle/infer/region_inference/graphviz.rs
@@ -22,7 +22,6 @@ use middle::ty;
 use super::Constraint;
 use middle::infer::SubregionOrigin;
 use middle::infer::region_inference::RegionVarBindings;
-use session::config;
 use util::nodemap::{FnvHashMap, FnvHashSet};
 use util::ppaux::Repr;
 
@@ -55,7 +54,7 @@ pub fn maybe_print_constraints_for<'a, 'tcx>(region_vars: &RegionVarBindings<'a,
                                              subject_node: ast::NodeId) {
     let tcx = region_vars.tcx;
 
-    if !region_vars.tcx.sess.debugging_opt(config::PRINT_REGION_GRAPH) {
+    if !region_vars.tcx.sess.opts.debugging_opts.print_region_graph {
         return;
     }
 
diff --git a/src/librustc/plugin/load.rs b/src/librustc/plugin/load.rs
index 87f5ba0246f..ef8a89c40fb 100644
--- a/src/librustc/plugin/load.rs
+++ b/src/librustc/plugin/load.rs
@@ -11,7 +11,7 @@
 //! Used by `rustc` when loading a plugin, or a crate with exported macros.
 
 use session::Session;
-use metadata::creader::CrateReader;
+use metadata::creader::{CrateOrString, CrateReader};
 use plugin::registry::Registry;
 
 use std::mem;
@@ -44,11 +44,11 @@ pub struct Plugins {
     pub registrars: Vec<PluginRegistrar>,
 }
 
-struct PluginLoader<'a> {
+pub struct PluginLoader<'a> {
     sess: &'a Session,
     span_whitelist: HashSet<Span>,
     reader: CrateReader<'a>,
-    plugins: Plugins,
+    pub plugins: Plugins,
 }
 
 impl<'a> PluginLoader<'a> {
@@ -67,7 +67,7 @@ impl<'a> PluginLoader<'a> {
 
 /// Read plugin metadata and dynamically load registrar functions.
 pub fn load_plugins(sess: &Session, krate: &ast::Crate,
-                    addl_plugins: Option<Plugins>) -> Plugins {
+                    addl_plugins: Option<Vec<String>>) -> Plugins {
     let mut loader = PluginLoader::new(sess);
 
     // We need to error on `#[macro_use] extern crate` when it isn't at the
@@ -79,19 +79,14 @@ pub fn load_plugins(sess: &Session, krate: &ast::Crate,
 
     visit::walk_crate(&mut loader, krate);
 
-    let mut plugins = loader.plugins;
-
-    match addl_plugins {
-        Some(addl_plugins) => {
-            // Add in the additional plugins requested by the frontend
-            let Plugins { macros: addl_macros, registrars: addl_registrars } = addl_plugins;
-            plugins.macros.extend(addl_macros.into_iter());
-            plugins.registrars.extend(addl_registrars.into_iter());
+    if let Some(plugins) = addl_plugins {
+        for plugin in plugins.iter() {
+            loader.load_plugin(CrateOrString::Str(plugin.as_slice()),
+                                                  None, None, None)
         }
-        None => ()
     }
 
-    return plugins;
+    return loader.plugins;
 }
 
 // note that macros aren't expanded yet, and therefore macros can't add plugins.
@@ -160,22 +155,39 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
             }
         }
 
+        self.load_plugin(CrateOrString::Krate(vi), plugin_attr, macro_selection, Some(reexport))
+    }
+
+    fn visit_mac(&mut self, _: &ast::Mac) {
+        // bummer... can't see plugins inside macros.
+        // do nothing.
+    }
+}
+
+impl<'a> PluginLoader<'a> {
+    pub fn load_plugin<'b>(&mut self,
+                           c: CrateOrString<'b>,
+                           plugin_attr: Option<P<ast::MetaItem>>,
+                           macro_selection: Option<HashSet<token::InternedString>>,
+                           reexport: Option<HashSet<token::InternedString>>) {
         let mut macros = vec![];
         let mut registrar = None;
 
-        let load_macros = match macro_selection.as_ref() {
-            Some(sel) => sel.len() != 0 || reexport.len() != 0,
-            None => true,
+        let load_macros = match (macro_selection.as_ref(), reexport.as_ref()) {
+            (Some(sel), Some(re)) => sel.len() != 0 || re.len() != 0,
+            _ => true,
         };
         let load_registrar = plugin_attr.is_some();
 
-        if load_macros && !self.span_whitelist.contains(&vi.span) {
-            self.sess.span_err(vi.span, "an `extern crate` loading macros must be at \
-                                         the crate root");
-        }
+        if let CrateOrString::Krate(vi) = c {
+            if load_macros && !self.span_whitelist.contains(&vi.span) {
+                self.sess.span_err(vi.span, "an `extern crate` loading macros must be at \
+                                             the crate root");
+            }
+       }
 
         if load_macros || load_registrar {
-            let pmd = self.reader.read_plugin_metadata(vi);
+            let pmd = self.reader.read_plugin_metadata(c);
             if load_macros {
                 macros = pmd.exported_macros();
             }
@@ -190,12 +202,16 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
                 None => true,
                 Some(sel) => sel.contains(&name),
             };
-            def.export = reexport.contains(&name);
+            def.export = if let Some(ref re) = reexport {
+                re.contains(&name)
+            } else {
+                false // Don't reexport macros from crates loaded from the command line
+            };
             self.plugins.macros.push(def);
         }
 
         if let Some((lib, symbol)) = registrar {
-            let fun = self.dylink_registrar(vi, lib, symbol);
+            let fun = self.dylink_registrar(c, lib, symbol);
             self.plugins.registrars.push(PluginRegistrar {
                 fun: fun,
                 args: plugin_attr.unwrap(),
@@ -203,16 +219,9 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
         }
     }
 
-    fn visit_mac(&mut self, _: &ast::Mac) {
-        // bummer... can't see plugins inside macros.
-        // do nothing.
-    }
-}
-
-impl<'a> PluginLoader<'a> {
     // Dynamically link a registrar function into the compiler process.
-    fn dylink_registrar(&mut self,
-                        vi: &ast::ViewItem,
+    fn dylink_registrar<'b>(&mut self,
+                        c: CrateOrString<'b>,
                         path: Path,
                         symbol: String) -> PluginRegistrarFun {
         // Make sure the path contains a / or the linker will search for it.
@@ -223,7 +232,13 @@ impl<'a> PluginLoader<'a> {
             // this is fatal: there are almost certainly macros we need
             // inside this crate, so continue would spew "macro undefined"
             // errors
-            Err(err) => self.sess.span_fatal(vi.span, &err[])
+            Err(err) => {
+                if let CrateOrString::Krate(cr) = c {
+                    self.sess.span_fatal(cr.span, &err[])
+                } else {
+                    self.sess.fatal(&err[])
+                }
+            }
         };
 
         unsafe {
@@ -233,7 +248,13 @@ impl<'a> PluginLoader<'a> {
                         mem::transmute::<*mut u8,PluginRegistrarFun>(registrar)
                     }
                     // again fatal if we can't register macros
-                    Err(err) => self.sess.span_fatal(vi.span, &err[])
+                    Err(err) => {
+                        if let CrateOrString::Krate(cr) = c {
+                            self.sess.span_fatal(cr.span, &err[])
+                        } else {
+                            self.sess.fatal(&err[])
+                        }
+                    }
                 };
 
             // Intentionally leak the dynamic library. We can't ever unload it
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 72b1669843a..d9bb1d769bf 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -99,7 +99,7 @@ pub struct Options {
     pub parse_only: bool,
     pub no_trans: bool,
     pub no_analysis: bool,
-    pub debugging_opts: u64,
+    pub debugging_opts: DebuggingOptions,
     /// Whether to write dependency files. It's (enabled, optional filename).
     pub write_dependency_info: (bool, Option<Path>),
     pub prints: Vec<PrintRequest>,
@@ -224,7 +224,7 @@ pub fn basic_options() -> Options {
         parse_only: false,
         no_trans: false,
         no_analysis: false,
-        debugging_opts: 0,
+        debugging_opts: basic_debugging_options(),
         write_dependency_info: (false, None),
         prints: Vec::new(),
         cg: basic_codegen_options(),
@@ -257,103 +257,6 @@ pub enum CrateType {
     CrateTypeStaticlib,
 }
 
-macro_rules! debugging_opts {
-    ([ $opt:ident ] $cnt:expr ) => (
-        pub const $opt: u64 = 1 << $cnt;
-    );
-    ([ $opt:ident, $($rest:ident),* ] $cnt:expr ) => (
-        pub const $opt: u64 = 1 << $cnt;
-        debugging_opts! { [ $($rest),* ] $cnt + 1 }
-    )
-}
-
-debugging_opts! {
-    [
-        VERBOSE,
-        TIME_PASSES,
-        COUNT_LLVM_INSNS,
-        TIME_LLVM_PASSES,
-        TRANS_STATS,
-        ASM_COMMENTS,
-        NO_VERIFY,
-        BORROWCK_STATS,
-        NO_LANDING_PADS,
-        DEBUG_LLVM,
-        COUNT_TYPE_SIZES,
-        META_STATS,
-        GC,
-        PRINT_LINK_ARGS,
-        PRINT_LLVM_PASSES,
-        AST_JSON,
-        AST_JSON_NOEXPAND,
-        LS,
-        SAVE_ANALYSIS,
-        PRINT_MOVE_FRAGMENTS,
-        FLOWGRAPH_PRINT_LOANS,
-        FLOWGRAPH_PRINT_MOVES,
-        FLOWGRAPH_PRINT_ASSIGNS,
-        FLOWGRAPH_PRINT_ALL,
-        PRINT_REGION_GRAPH,
-        PARSE_ONLY,
-        NO_TRANS,
-        NO_ANALYSIS,
-        UNSTABLE_OPTIONS,
-        PRINT_ENUM_SIZES
-    ]
-    0
-}
-
-pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
-    vec![("verbose", "in general, enable more debug printouts", VERBOSE),
-     ("time-passes", "measure time of each rustc pass", TIME_PASSES),
-     ("count-llvm-insns", "count where LLVM \
-                           instrs originate", COUNT_LLVM_INSNS),
-     ("time-llvm-passes", "measure time of each LLVM pass",
-      TIME_LLVM_PASSES),
-     ("trans-stats", "gather trans statistics", TRANS_STATS),
-     ("asm-comments", "generate comments into the assembly (may change behavior)",
-      ASM_COMMENTS),
-     ("no-verify", "skip LLVM verification", NO_VERIFY),
-     ("borrowck-stats", "gather borrowck statistics",  BORROWCK_STATS),
-     ("no-landing-pads", "omit landing pads for unwinding",
-      NO_LANDING_PADS),
-     ("debug-llvm", "enable debug output from LLVM", DEBUG_LLVM),
-     ("count-type-sizes", "count the sizes of aggregate types",
-      COUNT_TYPE_SIZES),
-     ("meta-stats", "gather metadata statistics", META_STATS),
-     ("print-link-args", "Print the arguments passed to the linker",
-      PRINT_LINK_ARGS),
-     ("gc", "Garbage collect shared data (experimental)", GC),
-     ("print-llvm-passes",
-      "Prints the llvm optimization passes being run",
-      PRINT_LLVM_PASSES),
-     ("ast-json", "Print the AST as JSON and halt", AST_JSON),
-     ("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND),
-     ("ls", "List the symbols defined by a library crate", LS),
-     ("save-analysis", "Write syntax and type analysis information \
-                        in addition to normal output", SAVE_ANALYSIS),
-     ("print-move-fragments", "Print out move-fragment data for every fn",
-      PRINT_MOVE_FRAGMENTS),
-     ("flowgraph-print-loans", "Include loan analysis data in \
-                       --pretty flowgraph output", FLOWGRAPH_PRINT_LOANS),
-     ("flowgraph-print-moves", "Include move analysis data in \
-                       --pretty flowgraph output", FLOWGRAPH_PRINT_MOVES),
-     ("flowgraph-print-assigns", "Include assignment analysis data in \
-                       --pretty flowgraph output", FLOWGRAPH_PRINT_ASSIGNS),
-     ("flowgraph-print-all", "Include all dataflow analysis data in \
-                       --pretty flowgraph output", FLOWGRAPH_PRINT_ALL),
-     ("print-region-graph", "Prints region inference graph. \
-                             Use with RUST_REGION_GRAPH=help for more info",
-      PRINT_REGION_GRAPH),
-     ("parse-only", "Parse only; do not compile, assemble, or link", PARSE_ONLY),
-     ("no-trans", "Run all passes except translation; no output", NO_TRANS),
-     ("no-analysis", "Parse and expand the source, but run no analysis and",
-      NO_ANALYSIS),
-     ("unstable-options", "Adds unstable command line options to rustc interface",
-      UNSTABLE_OPTIONS),
-     ("print-enum-sizes", "Print the size of enums and their variants", PRINT_ENUM_SIZES),
-    ]
-}
 
 #[derive(Clone)]
 pub enum Passes {
@@ -370,7 +273,7 @@ impl Passes {
     }
 }
 
-/// Declare a macro that will define all CodegenOptions fields and parsers all
+/// Declare a macro that will define all CodegenOptions/DebuggingOptions fields and parsers all
 /// at once. The goal of this macro is to define an interface that can be
 /// programmatically used by the option parser in order to initialize the struct
 /// without hardcoding field names all over the place.
@@ -380,23 +283,70 @@ impl Passes {
 /// cgsetters module which is a bunch of generated code to parse an option into
 /// its respective field in the struct. There are a few hand-written parsers for
 /// parsing specific types of values in this module.
-macro_rules! cgoptions {
-    ($($opt:ident : $t:ty = ($init:expr, $parse:ident, $desc:expr)),* ,) =>
+macro_rules! options {
+    ($struct_name:ident, $setter_name:ident, $defaultfn:ident,
+     $buildfn:ident, $prefix:expr, $outputname:expr,
+     $stat:ident, $mod_desc:ident, $mod_set:ident,
+     $($opt:ident : $t:ty = ($init:expr, $parse:ident, $desc:expr)),* ,) =>
 (
     #[derive(Clone)]
-    pub struct CodegenOptions { $(pub $opt: $t),* }
+    #[allow(missing_copy_implementations)]
+    pub struct $struct_name { $(pub $opt: $t),* }
 
-    pub fn basic_codegen_options() -> CodegenOptions {
-        CodegenOptions { $($opt: $init),* }
+    pub fn $defaultfn() -> $struct_name {
+        $struct_name { $($opt: $init),* }
+    }
+
+    pub fn $buildfn(matches: &getopts::Matches) -> $struct_name
+    {
+        let mut op = $defaultfn();
+        for option in matches.opt_strs($prefix).into_iter() {
+            let mut iter = option.splitn(1, '=');
+            let key = iter.next().unwrap();
+            let value = iter.next();
+            let option_to_lookup = key.replace("-", "_");
+            let mut found = false;
+            for &(candidate, setter, opt_type_desc, _) in $stat.iter() {
+                if option_to_lookup != candidate { continue }
+                if !setter(&mut op, value) {
+                    match (value, opt_type_desc) {
+                        (Some(..), None) => {
+                            early_error(&format!("{} option `{}` takes no \
+                                                 value", $outputname, key)[])
+                        }
+                        (None, Some(type_desc)) => {
+                            early_error(&format!("{0} option `{1}` requires \
+                                                 {2} ({3} {1}=<value>)",
+                                                $outputname, key,
+                                                type_desc, $prefix)[])
+                        }
+                        (Some(value), Some(type_desc)) => {
+                            early_error(&format!("incorrect value `{}` for {} \
+                                                 option `{}` - {} was expected",
+                                                 value, $outputname,
+                                                 key, type_desc)[])
+                        }
+                        (None, None) => unreachable!()
+                    }
+                }
+                found = true;
+                break;
+            }
+            if !found {
+                early_error(&format!("unknown codegen option: `{}`",
+                                    key)[]);
+            }
+        }
+        return op;
     }
 
-    pub type CodegenSetter = fn(&mut CodegenOptions, v: Option<&str>) -> bool;
-    pub const CG_OPTIONS: &'static [(&'static str, CodegenSetter,
+    pub type $setter_name = fn(&mut $struct_name, v: Option<&str>) -> bool;
+    pub const $stat: &'static [(&'static str, $setter_name,
                                      Option<&'static str>, &'static str)] =
-        &[ $( (stringify!($opt), cgsetters::$opt, cg_type_descs::$parse, $desc) ),* ];
+        &[ $( (stringify!($opt), $mod_set::$opt, $mod_desc::$parse, $desc) ),* ];
 
-    #[allow(non_upper_case_globals)]
-    mod cg_type_descs {
+    #[allow(non_upper_case_globals, dead_code)]
+    mod $mod_desc {
         pub const parse_bool: Option<&'static str> = None;
         pub const parse_opt_bool: Option<&'static str> = None;
         pub const parse_string: Option<&'static str> = Some("a string");
@@ -410,11 +360,12 @@ macro_rules! cgoptions {
             Some("a number");
     }
 
-    mod cgsetters {
-        use super::{CodegenOptions, Passes, SomePasses, AllPasses};
+    #[allow(dead_code)]
+    mod $mod_set {
+        use super::{$struct_name, Passes, SomePasses, AllPasses};
 
         $(
-            pub fn $opt(cg: &mut CodegenOptions, v: Option<&str>) -> bool {
+            pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool {
                 $parse(&mut cg.$opt, v)
             }
         )*
@@ -506,7 +457,9 @@ macro_rules! cgoptions {
     }
 ) }
 
-cgoptions! {
+options! {CodegenOptions, CodegenSetter, basic_codegen_options,
+         build_codegen_options, "C", "codegen",
+         CG_OPTIONS, cg_type_desc, cgsetters,
     ar: Option<String> = (None, parse_opt_string,
         "tool to assemble archives with"),
     linker: Option<String> = (None, parse_opt_string,
@@ -562,45 +515,73 @@ cgoptions! {
         "Optimize with possible levels 0-3"),
 }
 
-pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions
-{
-    let mut cg = basic_codegen_options();
-    for option in matches.opt_strs("C").into_iter() {
-        let mut iter = option.splitn(1, '=');
-        let key = iter.next().unwrap();
-        let value = iter.next();
-        let option_to_lookup = key.replace("-", "_");
-        let mut found = false;
-        for &(candidate, setter, opt_type_desc, _) in CG_OPTIONS.iter() {
-            if option_to_lookup != candidate { continue }
-            if !setter(&mut cg, value) {
-                match (value, opt_type_desc) {
-                    (Some(..), None) => {
-                        early_error(&format!("codegen option `{}` takes no \
-                                             value", key)[])
-                    }
-                    (None, Some(type_desc)) => {
-                        early_error(&format!("codegen option `{0}` requires \
-                                             {1} (-C {0}=<value>)",
-                                            key, type_desc)[])
-                    }
-                    (Some(value), Some(type_desc)) => {
-                        early_error(&format!("incorrect value `{}` for codegen \
-                                             option `{}` - {} was expected",
-                                             value, key, type_desc)[])
-                    }
-                    (None, None) => unreachable!()
-                }
-            }
-            found = true;
-            break;
-        }
-        if !found {
-            early_error(&format!("unknown codegen option: `{}`",
-                                key)[]);
-        }
-    }
-    return cg;
+
+options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
+         build_debugging_options, "Z", "debugging",
+         DB_OPTIONS, db_type_desc, dbsetters,
+    verbose: bool = (false, parse_bool,
+        "in general, enable more debug printouts"),
+    time_passes: bool = (false, parse_bool,
+        "measure time of each rustc pass"),
+    count_llvm_insns: bool = (false, parse_bool,
+        "count where LLVM instrs originate"),
+    time_llvm_passes: bool = (false, parse_bool,
+        "measure time of each LLVM pass"),
+    trans_stats: bool = (false, parse_bool,
+        "gather trans statistics"),
+    asm_comments: bool = (false, parse_bool,
+        "generate comments into the assembly (may change behavior)"),
+    no_verify: bool = (false, parse_bool,
+        "skip LLVM verification"),
+    borrowck_stats: bool = (false, parse_bool,
+        "gather borrowck statistics"),
+    no_landing_pads: bool = (false, parse_bool,
+        "omit landing pads for unwinding"),
+    debug_llvm: bool = (false, parse_bool,
+        "enable debug output from LLVM"),
+    count_type_sizes: bool = (false, parse_bool,
+        "count the sizes of aggregate types"),
+    meta_stats: bool = (false, parse_bool,
+        "gather metadata statistics"),
+    print_link_args: bool = (false, parse_bool,
+        "Print the arguments passed to the linker"),
+    gc: bool = (false, parse_bool,
+        "Garbage collect shared data (experimental)"),
+    print_llvm_passes: bool = (false, parse_bool,
+        "Prints the llvm optimization passes being run"),
+    ast_json: bool = (false, parse_bool,
+        "Print the AST as JSON and halt"),
+    ast_json_noexpand: bool = (false, parse_bool,
+        "Print the pre-expansion AST as JSON and halt"),
+    ls: bool = (false, parse_bool,
+        "List the symbols defined by a library crate"),
+    save_analysis: bool = (false, parse_bool,
+        "Write syntax and type analysis information in addition to normal output"),
+    print_move_fragments: bool = (false, parse_bool,
+        "Print out move-fragment data for every fn"),
+    flowgraph_print_loans: bool = (false, parse_bool,
+        "Include loan analysis data in --pretty flowgraph output"),
+    flowgraph_print_moves: bool = (false, parse_bool,
+        "Include move analysis data in --pretty flowgraph output"),
+    flowgraph_print_assigns: bool = (false, parse_bool,
+        "Include assignment analysis data in --pretty flowgraph output"),
+    flowgraph_print_all: bool = (false, parse_bool,
+        "Include all dataflow analysis data in --pretty flowgraph output"),
+    print_region_graph: bool = (false, parse_bool,
+         "Prints region inference graph. \
+          Use with RUST_REGION_GRAPH=help for more info"),
+    parse_only: bool = (false, parse_bool,
+          "Parse only; do not compile, assemble, or link"),
+    no_trans: bool = (false, parse_bool,
+          "Run all passes except translation; no output"),
+    no_analysis: bool = (false, parse_bool,
+          "Parse and expand the source, but run no analysis"),
+    extra_plugins: Vec<String> = (Vec::new(), parse_list,
+        "load extra plugins"),
+    unstable_options: bool = (false, parse_bool,
+          "Adds unstable command line options to rustc interface"),
+    print_enum_sizes: bool = (false, parse_bool,
+          "Print the size of enums and their variants"),
 }
 
 pub fn default_lib_output() -> CrateType {
@@ -878,52 +859,36 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         }
     }
 
-    let mut debugging_opts = 0;
-    let debug_flags = matches.opt_strs("Z");
-    let debug_map = debugging_opts_map();
-    for debug_flag in debug_flags.iter() {
-        let mut this_bit = 0;
-        for &(name, _, bit) in debug_map.iter() {
-            if name == *debug_flag {
-                this_bit = bit;
-                break;
-            }
-        }
-        if this_bit == 0 {
-            early_error(&format!("unknown debug flag: {}",
-                                *debug_flag)[])
-        }
-        debugging_opts |= this_bit;
-    }
+    let debugging_opts = build_debugging_options(matches);
 
     let parse_only = if matches.opt_present("parse-only") {
         // FIXME(acrichto) remove this eventually
         early_warn("--parse-only is deprecated in favor of -Z parse-only");
         true
     } else {
-        debugging_opts & PARSE_ONLY != 0
+        debugging_opts.parse_only
     };
     let no_trans = if matches.opt_present("no-trans") {
         // FIXME(acrichto) remove this eventually
         early_warn("--no-trans is deprecated in favor of -Z no-trans");
         true
     } else {
-        debugging_opts & NO_TRANS != 0
+        debugging_opts.no_trans
     };
     let no_analysis = if matches.opt_present("no-analysis") {
         // FIXME(acrichto) remove this eventually
         early_warn("--no-analysis is deprecated in favor of -Z no-analysis");
         true
     } else {
-        debugging_opts & NO_ANALYSIS != 0
+        debugging_opts.no_analysis
     };
 
-    if debugging_opts & DEBUG_LLVM != 0 {
+    if debugging_opts.debug_llvm {
         unsafe { llvm::LLVMSetDebug(1); }
     }
 
     let mut output_types = Vec::new();
-    if !parse_only && !no_trans {
+    if !debugging_opts.parse_only && !no_trans {
         let unparsed_output_types = matches.opt_strs("emit");
         for unparsed_output_type in unparsed_output_types.iter() {
             for part in unparsed_output_type.split(',') {
@@ -993,7 +958,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             }
         }
     };
-    let gc = debugging_opts & GC != 0;
+    let gc = debugging_opts.gc;
     let debuginfo = if matches.opt_present("g") {
         if matches.opt_present("debuginfo") {
             early_error("-g and --debuginfo both provided");
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 65dac1a5fac..79e4d0f7aea 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -164,9 +164,6 @@ impl Session {
     pub fn diagnostic<'a>(&'a self) -> &'a diagnostic::SpanHandler {
         &self.parse_sess.span_diagnostic
     }
-    pub fn debugging_opt(&self, opt: u64) -> bool {
-        (self.opts.debugging_opts & opt) != 0
-    }
     pub fn codemap<'a>(&'a self) -> &'a codemap::CodeMap {
         &self.parse_sess.span_diagnostic.cm
     }
@@ -176,36 +173,36 @@ impl Session {
         self.span_bug(sp,
                       &format!("impossible case reached: {}", msg)[]);
     }
-    pub fn verbose(&self) -> bool { self.debugging_opt(config::VERBOSE) }
-    pub fn time_passes(&self) -> bool { self.debugging_opt(config::TIME_PASSES) }
+    pub fn verbose(&self) -> bool { self.opts.debugging_opts.verbose }
+    pub fn time_passes(&self) -> bool { self.opts.debugging_opts.time_passes }
     pub fn count_llvm_insns(&self) -> bool {
-        self.debugging_opt(config::COUNT_LLVM_INSNS)
+        self.opts.debugging_opts.count_llvm_insns
     }
     pub fn count_type_sizes(&self) -> bool {
-        self.debugging_opt(config::COUNT_TYPE_SIZES)
+        self.opts.debugging_opts.count_type_sizes
     }
     pub fn time_llvm_passes(&self) -> bool {
-        self.debugging_opt(config::TIME_LLVM_PASSES)
+        self.opts.debugging_opts.time_llvm_passes
     }
-    pub fn trans_stats(&self) -> bool { self.debugging_opt(config::TRANS_STATS) }
-    pub fn meta_stats(&self) -> bool { self.debugging_opt(config::META_STATS) }
-    pub fn asm_comments(&self) -> bool { self.debugging_opt(config::ASM_COMMENTS) }
-    pub fn no_verify(&self) -> bool { self.debugging_opt(config::NO_VERIFY) }
-    pub fn borrowck_stats(&self) -> bool { self.debugging_opt(config::BORROWCK_STATS) }
+    pub fn trans_stats(&self) -> bool { self.opts.debugging_opts.trans_stats }
+    pub fn meta_stats(&self) -> bool { self.opts.debugging_opts.meta_stats }
+    pub fn asm_comments(&self) -> bool { self.opts.debugging_opts.asm_comments }
+    pub fn no_verify(&self) -> bool { self.opts.debugging_opts.no_verify }
+    pub fn borrowck_stats(&self) -> bool { self.opts.debugging_opts.borrowck_stats }
     pub fn print_llvm_passes(&self) -> bool {
-        self.debugging_opt(config::PRINT_LLVM_PASSES)
+        self.opts.debugging_opts.print_llvm_passes
     }
     pub fn lto(&self) -> bool {
         self.opts.cg.lto
     }
     pub fn no_landing_pads(&self) -> bool {
-        self.debugging_opt(config::NO_LANDING_PADS)
+        self.opts.debugging_opts.no_landing_pads
     }
     pub fn unstable_options(&self) -> bool {
-        self.debugging_opt(config::UNSTABLE_OPTIONS)
+        self.opts.debugging_opts.unstable_options
     }
     pub fn print_enum_sizes(&self) -> bool {
-        self.debugging_opt(config::PRINT_ENUM_SIZES)
+        self.opts.debugging_opts.print_enum_sizes
     }
     pub fn sysroot<'a>(&'a self) -> &'a Path {
         match self.opts.maybe_sysroot {
diff --git a/src/librustc_borrowck/borrowck/fragments.rs b/src/librustc_borrowck/borrowck/fragments.rs
index 1b120208217..e2942719f2a 100644
--- a/src/librustc_borrowck/borrowck/fragments.rs
+++ b/src/librustc_borrowck/borrowck/fragments.rs
@@ -19,7 +19,6 @@ use borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend};
 use borrowck::LoanPathElem::{LpDeref, LpInterior};
 use borrowck::move_data::{InvalidMovePathIndex};
 use borrowck::move_data::{MoveData, MovePathIndex};
-use rustc::session::config;
 use rustc::middle::ty;
 use rustc::middle::mem_categorization as mc;
 use rustc::util::ppaux::{Repr, UserString};
@@ -133,7 +132,7 @@ pub fn instrument_move_fragments<'tcx>(this: &MoveData<'tcx>,
 
         let span_err =
             attrs.iter().any(|a| a.check_name("rustc_move_fragments"));
-        let print = tcx.sess.debugging_opt(config::PRINT_MOVE_FRAGMENTS);
+        let print = tcx.sess.opts.debugging_opts.print_move_fragments;
 
         (span_err, print)
     };
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 019691c1e10..c2af4315b06 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -47,7 +47,7 @@ pub fn compile_input(sess: Session,
                      input: &Input,
                      outdir: &Option<Path>,
                      output: &Option<Path>,
-                     addl_plugins: Option<Plugins>) {
+                     addl_plugins: Option<Vec<String>>) {
     // We need nested scopes here, because the intermediate results can keep
     // large chunks of memory alive and we want to free them as soon as
     // possible to keep the peak memory usage low
@@ -142,7 +142,7 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
         }
     });
 
-    if sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0 {
+    if sess.opts.debugging_opts.ast_json_noexpand {
         println!("{}", json::as_json(&krate));
     }
 
@@ -166,7 +166,7 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
 pub fn phase_2_configure_and_expand(sess: &Session,
                                     mut krate: ast::Crate,
                                     crate_name: &str,
-                                    addl_plugins: Option<Plugins>)
+                                    addl_plugins: Option<Vec<String>>)
                                     -> Option<ast::Crate> {
     let time_passes = sess.time_passes();
 
@@ -334,7 +334,7 @@ pub fn assign_node_ids_and_map<'ast>(sess: &Session,
     let map = time(sess.time_passes(), "assigning node ids and indexing ast", forest, |forest|
                    ast_map::map_crate(forest, NodeIdAssigner { sess: sess }));
 
-    if sess.opts.debugging_opts & config::AST_JSON != 0 {
+    if sess.opts.debugging_opts.ast_json {
         println!("{}", json::as_json(map.krate()));
     }
 
@@ -484,7 +484,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
 }
 
 fn save_analysis(sess: &Session) -> bool {
-    (sess.opts.debugging_opts & config::SAVE_ANALYSIS) != 0
+    sess.opts.debugging_opts.save_analysis
 }
 
 pub fn phase_save_analysis(sess: &Session,
@@ -575,7 +575,7 @@ pub fn stop_after_phase_1(sess: &Session) -> bool {
     if sess.opts.show_span.is_some() {
         return true;
     }
-    return sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0;
+    return sess.opts.debugging_opts.ast_json_noexpand;
 }
 
 pub fn stop_after_phase_2(sess: &Session) -> bool {
@@ -583,7 +583,7 @@ pub fn stop_after_phase_2(sess: &Session) -> bool {
         debug!("invoked with --no-analysis, returning early from compile_input");
         return true;
     }
-    return sess.opts.debugging_opts & config::AST_JSON != 0;
+    return sess.opts.debugging_opts.ast_json;
 }
 
 pub fn stop_after_phase_5(sess: &Session) -> bool {
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 071549a213c..9122a34a793 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -54,6 +54,7 @@ use rustc::session::config::{Input, PrintRequest, UnstableFeatures};
 use rustc::lint::Lint;
 use rustc::lint;
 use rustc::metadata;
+use rustc::metadata::creader::CrateOrString::Str;
 use rustc::DIAGNOSTICS;
 
 use std::cmp::Ordering::Equal;
@@ -186,7 +187,8 @@ fn run_compiler(args: &[String]) {
         return;
     }
 
-    driver::compile_input(sess, cfg, &input, &odir, &ofile, None);
+    let plugins = sess.opts.debugging_opts.extra_plugins.clone();
+    driver::compile_input(sess, cfg, &input, &odir, &ofile, Some(plugins));
 }
 
 pub fn get_unstable_features_setting() -> UnstableFeatures {
@@ -379,13 +381,13 @@ Available lint options:
 
 fn describe_debug_flags() {
     println!("\nAvailable debug options:\n");
-    let r = config::debugging_opts_map();
-    for tuple in r.iter() {
-        match *tuple {
-            (ref name, ref desc, _) => {
-                println!("    -Z {:>20} -- {}", *name, *desc);
-            }
-        }
+    for &(name, _, opt_type_desc, desc) in config::DB_OPTIONS.iter() {
+        let (width, extra) = match opt_type_desc {
+            Some(..) => (21, "=val"),
+            None => (25, "")
+        };
+        println!("    -Z {:>width$}{} -- {}", name.replace("_", "-"),
+                 extra, desc, width=width);
     }
 }
 
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index 7592fbc05b3..1765c80f943 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -23,7 +23,7 @@ use rustc::middle::ty;
 use rustc::middle::cfg;
 use rustc::middle::cfg::graphviz::LabelledCFG;
 use rustc::session::Session;
-use rustc::session::config::{self, Input};
+use rustc::session::config::Input;
 use rustc::util::ppaux;
 use rustc_borrowck as borrowck;
 use rustc_borrowck::graphviz as borrowck_dot;
@@ -305,19 +305,18 @@ impl<'tcx> pprust::PpAnn for TypedAnnotation<'tcx> {
 }
 
 fn gather_flowgraph_variants(sess: &Session) -> Vec<borrowck_dot::Variant> {
-    let print_loans   = config::FLOWGRAPH_PRINT_LOANS;
-    let print_moves   = config::FLOWGRAPH_PRINT_MOVES;
-    let print_assigns = config::FLOWGRAPH_PRINT_ASSIGNS;
-    let print_all     = config::FLOWGRAPH_PRINT_ALL;
-    let opt = |&: print_which| sess.debugging_opt(print_which);
+    let print_loans = sess.opts.debugging_opts.flowgraph_print_loans;
+    let print_moves = sess.opts.debugging_opts.flowgraph_print_moves;
+    let print_assigns = sess.opts.debugging_opts.flowgraph_print_assigns;
+    let print_all = sess.opts.debugging_opts.flowgraph_print_all;
     let mut variants = Vec::new();
-    if opt(print_all) || opt(print_loans) {
+    if print_all || print_loans {
         variants.push(borrowck_dot::Loans);
     }
-    if opt(print_all) || opt(print_moves) {
+    if print_all || print_moves {
         variants.push(borrowck_dot::Moves);
     }
-    if opt(print_all) || opt(print_assigns) {
+    if print_all || print_assigns {
         variants.push(borrowck_dot::Assigns);
     }
     variants
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index a798ec9aaf7..f68c76f4c44 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -99,7 +99,7 @@ fn test_env<F>(source_string: &str,
 {
     let mut options =
         config::basic_options();
-    options.debugging_opts |= config::VERBOSE;
+    options.debugging_opts.verbose = true;
     let codemap =
         CodeMap::new();
     let diagnostic_handler =
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 43f8c677e30..351be70cf52 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -778,7 +778,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
         cmd.arg("-lcompiler-rt");
     }
 
-    if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 {
+    if sess.opts.debugging_opts.print_link_args {
         println!("{}", &cmd);
     }
 
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index e0ba6d569cc..c818dda7581 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -715,7 +715,7 @@ pub fn run_passes(sess: &Session,
 
         cmd.args(&sess.target.target.options.post_link_args[]);
 
-        if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 {
+        if sess.opts.debugging_opts.print_link_args {
             println!("{}", &cmd);
         }
 
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index d1768867f0d..9a422e17bb4 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -105,6 +105,11 @@ pub struct Span {
 
 pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION };
 
+// Generic span to be used for code originating from the command line
+pub const COMMAND_LINE_SP: Span = Span { lo: BytePos(0),
+                                         hi: BytePos(0),
+                                         expn_id: COMMAND_LINE_EXPN };
+
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Show, Copy)]
 pub struct Spanned<T> {
     pub node: T,
@@ -235,6 +240,8 @@ pub struct ExpnInfo {
 pub struct ExpnId(u32);
 
 pub const NO_EXPANSION: ExpnId = ExpnId(-1);
+// For code appearing from the command line
+pub const COMMAND_LINE_EXPN: ExpnId = ExpnId(-2);
 
 impl ExpnId {
     pub fn from_llvm_cookie(cookie: c_uint) -> ExpnId {
diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs
index 7e57709f33d..64fdb61f2ec 100644
--- a/src/libsyntax/diagnostic.rs
+++ b/src/libsyntax/diagnostic.rs
@@ -13,7 +13,7 @@ pub use self::RenderSpan::*;
 pub use self::ColorConfig::*;
 use self::Destination::*;
 
-use codemap::{Pos, Span};
+use codemap::{COMMAND_LINE_SP, Pos, Span};
 use codemap;
 use diagnostics;
 
@@ -368,6 +368,9 @@ impl Emitter for EmitterWriter {
             cmsp: Option<(&codemap::CodeMap, Span)>,
             msg: &str, code: Option<&str>, lvl: Level) {
         let error = match cmsp {
+            Some((cm, COMMAND_LINE_SP)) => emit(self, cm,
+                                                FileLine(COMMAND_LINE_SP),
+                                                msg, code, lvl, false),
             Some((cm, sp)) => emit(self, cm, FullSpan(sp), msg, code, lvl, false),
             None => print_diagnostic(self, "", lvl, msg, code),
         };
@@ -390,8 +393,11 @@ impl Emitter for EmitterWriter {
 fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
         msg: &str, code: Option<&str>, lvl: Level, custom: bool) -> io::IoResult<()> {
     let sp = rsp.span();
-    let ss = cm.span_to_string(sp);
-    let lines = cm.span_to_lines(sp);
+    let ss = if sp == COMMAND_LINE_SP {
+        "<command line option>".to_string()
+    } else {
+        cm.span_to_string(sp)
+    };
     if custom {
         // we want to tell compiletest/runtest to look at the last line of the
         // span (since `custom_highlight_lines` displays an arrow to the end of
@@ -400,15 +406,17 @@ fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
         let ses = cm.span_to_string(span_end);
         try!(print_diagnostic(dst, &ses[], lvl, msg, code));
         if rsp.is_full_span() {
-            try!(custom_highlight_lines(dst, cm, sp, lvl, lines));
+            try!(custom_highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp)));
         }
     } else {
         try!(print_diagnostic(dst, &ss[], lvl, msg, code));
         if rsp.is_full_span() {
-            try!(highlight_lines(dst, cm, sp, lvl, lines));
+            try!(highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp)));
         }
     }
-    try!(print_macro_backtrace(dst, cm, sp));
+    if sp != COMMAND_LINE_SP {
+        try!(print_macro_backtrace(dst, cm, sp));
+    }
     match code {
         Some(code) =>
             match dst.registry.as_ref().and_then(|registry| registry.find_description(code)) {
diff --git a/src/test/compile-fail/recursion.rs b/src/test/compile-fail/recursion.rs
index ffc21a5ce61..f39c839a0e7 100644
--- a/src/test/compile-fail/recursion.rs
+++ b/src/test/compile-fail/recursion.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//~^^^^^^^^^^ ERROR overflow
+// ignore-command-line: See https://github.com/rust-lang/rust/issues/20747
 //
 // We also get a second error message at the top of file (dummy
 // span). This is not helpful, but also kind of annoying to prevent,
diff --git a/src/test/run-pass-fulldeps/lint-plugin-cmdline.rs b/src/test/run-pass-fulldeps/lint-plugin-cmdline-allow.rs
index 7144d2b0f1e..7144d2b0f1e 100644
--- a/src/test/run-pass-fulldeps/lint-plugin-cmdline.rs
+++ b/src/test/run-pass-fulldeps/lint-plugin-cmdline-allow.rs
diff --git a/src/test/run-pass-fulldeps/lint-plugin-cmdline-load.rs b/src/test/run-pass-fulldeps/lint-plugin-cmdline-load.rs
new file mode 100644
index 00000000000..1af26094f23
--- /dev/null
+++ b/src/test/run-pass-fulldeps/lint-plugin-cmdline-load.rs
@@ -0,0 +1,21 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:lint_plugin_test.rs
+// ignore-stage1
+// ignore-pretty: Random space appears with the pretty test
+// compile-flags: -Z extra-plugins=lint_plugin_test
+
+fn lintme() { } //~ WARNING item is named 'lintme'
+
+#[allow(test_lint)]
+pub fn main() {
+    fn lintme() { }
+}