From acfccc515bfbed098f46c6530d57aaef846b99ca Mon Sep 17 00:00:00 2001
From: Nick Cameron <ncameron@mozilla.com>
Date: Thu, 31 Dec 2015 14:43:42 +1300
Subject: [PATCH 1/5] Add a JSON error emitter

---
 src/libsyntax/errors/json.rs | 53 ++++++++++++++++++++++++++++++++++++
 src/libsyntax/errors/mod.rs  |  1 +
 2 files changed, 54 insertions(+)
 create mode 100644 src/libsyntax/errors/json.rs

diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs
new file mode 100644
index 0000000000000..feae71f55d3d2
--- /dev/null
+++ b/src/libsyntax/errors/json.rs
@@ -0,0 +1,53 @@
+// Copyright 2015 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.
+
+//! A JSON emitter for errors.
+
+use codemap::{Span, CodeMap};
+use diagnostics::registry::Registry;
+use errors::{Level, DiagnosticBuilder, RenderSpan};
+use errors::emitter::Emitter;
+
+use std::rc::Rc;
+
+pub struct JsonEmitter {
+    todo: i32
+}
+
+impl JsonEmitter {
+    pub fn basic() -> JsonEmitter {
+        JsonEmitter {
+            todo: 42,
+        }
+    }
+
+    pub fn stderr(registry: Option<Registry>,
+                  code_map: Rc<CodeMap>) -> JsonEmitter {
+        JsonEmitter {
+            todo: 42,
+        }
+    }
+}
+
+impl Emitter for JsonEmitter {
+    fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, lvl: Level) {
+        unimplemented!();
+
+    }
+
+    fn custom_emit(&mut self, sp: RenderSpan, msg: &str, lvl: Level) {
+        unimplemented!();
+
+    }
+
+    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
+        unimplemented!();
+    }
+}
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
index a2fae975148f9..733798c197a1c 100644
--- a/src/libsyntax/errors/mod.rs
+++ b/src/libsyntax/errors/mod.rs
@@ -24,6 +24,7 @@ use std::rc::Rc;
 use term;
 
 pub mod emitter;
+pub mod json;
 
 #[derive(Clone)]
 pub enum RenderSpan {

From fd46c78f8f5bc760a46c36fc03f97d43ac389db6 Mon Sep 17 00:00:00 2001
From: Nick Cameron <ncameron@mozilla.com>
Date: Thu, 31 Dec 2015 16:50:06 +1300
Subject: [PATCH 2/5] Add an --output option for specifying an error emitter

---
 src/librustc/lint/context.rs                  |   9 +-
 src/librustc/session/config.rs                | 110 ++++++++++++------
 src/librustc/session/mod.rs                   |  34 ++++--
 src/librustc/session/search_paths.rs          |   7 +-
 src/librustc_driver/lib.rs                    |  23 ++--
 src/librustc_trans/back/linker.rs             |   4 +-
 src/librustc_trans/back/write.rs              |  18 +--
 src/librustc_trans/trans/base.rs              |   2 +-
 .../trans/debuginfo/metadata.rs               |   2 +-
 src/librustc_trans/trans/debuginfo/mod.rs     |   4 +-
 src/libsyntax/errors/emitter.rs               |   2 +-
 src/libsyntax/errors/mod.rs                   |   1 +
 12 files changed, 135 insertions(+), 81 deletions(-)

diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 08fba2dc56fee..d7eacbfff90db 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -28,7 +28,7 @@ use self::TargetLint::*;
 use dep_graph::DepNode;
 use middle::privacy::AccessLevels;
 use middle::ty;
-use session::{early_error, Session};
+use session::{config, early_error, Session};
 use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass};
 use lint::{EarlyLintPass, EarlyLintPassObject, LateLintPass, LateLintPassObject};
 use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
@@ -37,11 +37,12 @@ use util::nodemap::FnvHashMap;
 
 use std::cell::RefCell;
 use std::cmp;
+use std::default::Default as StdDefault;
 use std::mem;
 use syntax::ast_util::{self, IdVisitingOperation};
 use syntax::attr::{self, AttrMetaMethods};
 use syntax::codemap::Span;
-use syntax::errors::{self, DiagnosticBuilder};
+use syntax::errors::DiagnosticBuilder;
 use syntax::parse::token::InternedString;
 use syntax::ast;
 use syntax::attr::ThinAttributesExt;
@@ -168,7 +169,7 @@ impl LintStore {
                 match (sess, from_plugin) {
                     // We load builtin lints first, so a duplicate is a compiler bug.
                     // Use early_error when handling -W help with no crate.
-                    (None, _) => early_error(errors::ColorConfig::Auto, &msg[..]),
+                    (None, _) => early_error(config::ErrorOutputType::default(), &msg[..]),
                     (Some(sess), false) => sess.bug(&msg[..]),
 
                     // A duplicate name from a plugin is a user error.
@@ -192,7 +193,7 @@ impl LintStore {
             match (sess, from_plugin) {
                 // We load builtin lints first, so a duplicate is a compiler bug.
                 // Use early_error when handling -W help with no crate.
-                (None, _) => early_error(errors::ColorConfig::Auto, &msg[..]),
+                (None, _) => early_error(config::ErrorOutputType::default(), &msg[..]),
                 (Some(sess), false) => sess.bug(&msg[..]),
 
                 // A duplicate name from a plugin is a user error.
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 80bfbe4edda88..6a7b411823ce9 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -14,7 +14,6 @@
 pub use self::EntryFnType::*;
 pub use self::CrateType::*;
 pub use self::Passes::*;
-pub use self::OptLevel::*;
 pub use self::DebugInfoLevel::*;
 
 use session::{early_error, early_warn, Session};
@@ -71,6 +70,18 @@ pub enum OutputType {
     DepInfo,
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ErrorOutputType {
+    Tty(ColorConfig),
+    Json,
+}
+
+impl Default for ErrorOutputType {
+    fn default() -> ErrorOutputType {
+        ErrorOutputType::Tty(ColorConfig::Auto)
+    }
+}
+
 impl OutputType {
     fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
         match *self {
@@ -124,6 +135,7 @@ pub struct Options {
     pub test: bool,
     pub parse_only: bool,
     pub no_trans: bool,
+    pub output: ErrorOutputType,
     pub treat_err_as_bug: bool,
     pub incremental_compilation: bool,
     pub dump_dep_graph: bool,
@@ -131,7 +143,6 @@ pub struct Options {
     pub debugging_opts: DebuggingOptions,
     pub prints: Vec<PrintRequest>,
     pub cg: CodegenOptions,
-    pub color: ColorConfig,
     pub externs: HashMap<String, Vec<String>>,
     pub crate_name: Option<String>,
     /// An optional name to use as the crate for std during std injection,
@@ -221,7 +232,7 @@ pub fn basic_options() -> Options {
     Options {
         crate_types: Vec::new(),
         gc: false,
-        optimize: No,
+        optimize: OptLevel::No,
         debuginfo: NoDebugInfo,
         lint_opts: Vec::new(),
         lint_cap: None,
@@ -241,7 +252,12 @@ pub fn basic_options() -> Options {
         debugging_opts: basic_debugging_options(),
         prints: Vec::new(),
         cg: basic_codegen_options(),
+<<<<<<< HEAD
         color: ColorConfig::Auto,
+=======
+        output: ErrorOutputType::default(),
+        show_span: None,
+>>>>>>> Add an --output option for specifying an error emitter
         externs: HashMap::new(),
         crate_name: None,
         alt_std_name: None,
@@ -308,7 +324,7 @@ macro_rules! options {
         $struct_name { $($opt: $init),* }
     }
 
-    pub fn $buildfn(matches: &getopts::Matches, color: ColorConfig) -> $struct_name
+    pub fn $buildfn(matches: &getopts::Matches, output: ErrorOutputType) -> $struct_name
     {
         let mut op = $defaultfn();
         for option in matches.opt_strs($prefix) {
@@ -322,17 +338,17 @@ macro_rules! options {
                 if !setter(&mut op, value) {
                     match (value, opt_type_desc) {
                         (Some(..), None) => {
-                            early_error(color, &format!("{} option `{}` takes no \
+                            early_error(output, &format!("{} option `{}` takes no \
                                                          value", $outputname, key))
                         }
                         (None, Some(type_desc)) => {
-                            early_error(color, &format!("{0} option `{1}` requires \
+                            early_error(output, &format!("{0} option `{1}` requires \
                                                          {2} ({3} {1}=<value>)",
                                                         $outputname, key,
                                                         type_desc, $prefix))
                         }
                         (Some(value), Some(type_desc)) => {
-                            early_error(color, &format!("incorrect value `{}` for {} \
+                            early_error(output, &format!("incorrect value `{}` for {} \
                                                          option `{}` - {} was expected",
                                                         value, $outputname,
                                                         key, type_desc))
@@ -344,7 +360,7 @@ macro_rules! options {
                 break;
             }
             if !found {
-                early_error(color, &format!("unknown {} option: `{}`",
+                early_error(output, &format!("unknown {} option: `{}`",
                                             $outputname, key));
             }
         }
@@ -863,6 +879,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
                  "NAME=PATH"),
         opt::opt("", "sysroot", "Override the system root", "PATH"),
         opt::multi("Z", "", "Set internal debugging options", "FLAG"),
+        opt::opt_u("", "output", "How errors and other mesasges are produced", "tty|json"),
         opt::opt("", "color", "Configure coloring of output:
             auto   = colorize, if output goes to a tty (default);
             always = always colorize output;
@@ -905,15 +922,36 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         None => ColorConfig::Auto,
 
         Some(arg) => {
-            early_error(ColorConfig::Auto, &format!("argument for --color must be auto, always \
-                                                     or never (instead was `{}`)",
-                                                    arg))
+            early_error(ErrorOutputType::default(), &format!("argument for --color must be auto, \
+                                                              always or never (instead was `{}`)",
+                                                            arg))
         }
     };
 
+    // We need the opts_present check because the driver will send us Matches
+    // with only stable options if no unstable options are used. Since output is
+    // unstable, it will not be present. We have to use opts_present not
+    // opt_present because the latter will panic.
+    let output = if matches.opts_present(&["output".to_owned()]) {
+        match matches.opt_str("output").as_ref().map(|s| &s[..]) {
+            Some("tty")   => ErrorOutputType::Tty(color),
+            Some("json") => ErrorOutputType::Json,
+
+            None => ErrorOutputType::default(),
+
+            Some(arg) => {
+                early_error(ErrorOutputType::default(), &format!("argument for --output must be tty or \
+                                                                  json (instead was `{}`)",
+                                                                 arg))
+            }
+        }
+    } else {
+        ErrorOutputType::default()
+    };
+
     let unparsed_crate_types = matches.opt_strs("crate-type");
     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
-        .unwrap_or_else(|e| early_error(color, &e[..]));
+        .unwrap_or_else(|e| early_error(output, &e[..]));
 
     let mut lint_opts = vec!();
     let mut describe_lints = false;
@@ -930,11 +968,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let lint_cap = matches.opt_str("cap-lints").map(|cap| {
         lint::Level::from_str(&cap).unwrap_or_else(|| {
-            early_error(color, &format!("unknown lint level: `{}`", cap))
+            early_error(output, &format!("unknown lint level: `{}`", cap))
         })
     });
 
-    let debugging_opts = build_debugging_options(matches, color);
+    let debugging_opts = build_debugging_options(matches, output);
 
     let parse_only = debugging_opts.parse_only;
     let no_trans = debugging_opts.no_trans;
@@ -960,7 +998,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
                     "link" => OutputType::Exe,
                     "dep-info" => OutputType::DepInfo,
                     part => {
-                        early_error(color, &format!("unknown emission type: `{}`",
+                        early_error(output, &format!("unknown emission type: `{}`",
                                                     part))
                     }
                 };
@@ -973,7 +1011,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         output_types.insert(OutputType::Exe, None);
     }
 
-    let mut cg = build_codegen_options(matches, color);
+    let mut cg = build_codegen_options(matches, output);
 
     // Issue #30063: if user requests llvm-related output to one
     // particular path, disable codegen-units.
@@ -985,11 +1023,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             }).collect();
         if !incompatible.is_empty() {
             for ot in &incompatible {
-                early_warn(color, &format!("--emit={} with -o incompatible with \
+                early_warn(output, &format!("--emit={} with -o incompatible with \
                                             -C codegen-units=N for N > 1",
                                            ot.shorthand()));
             }
-            early_warn(color, "resetting to default -C codegen-units=1");
+            early_warn(output, "resetting to default -C codegen-units=1");
             cg.codegen_units = 1;
         }
     }
@@ -1002,29 +1040,29 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let opt_level = {
         if matches.opt_present("O") {
             if cg.opt_level.is_some() {
-                early_error(color, "-O and -C opt-level both provided");
+                early_error(output, "-O and -C opt-level both provided");
             }
-            Default
+            OptLevel::Default
         } else {
             match cg.opt_level {
-                None => No,
-                Some(0) => No,
-                Some(1) => Less,
-                Some(2) => Default,
-                Some(3) => Aggressive,
+                None => OptLevel::No,
+                Some(0) => OptLevel::No,
+                Some(1) => OptLevel::Less,
+                Some(2) => OptLevel::Default,
+                Some(3) => OptLevel::Aggressive,
                 Some(arg) => {
-                    early_error(color, &format!("optimization level needs to be \
+                    early_error(output, &format!("optimization level needs to be \
                                                  between 0-3 (instead was `{}`)",
                                                 arg));
                 }
             }
         }
     };
-    let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == No);
+    let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
     let gc = debugging_opts.gc;
     let debuginfo = if matches.opt_present("g") {
         if cg.debuginfo.is_some() {
-            early_error(color, "-g and -C debuginfo both provided");
+            early_error(output, "-g and -C debuginfo both provided");
         }
         FullDebugInfo
     } else {
@@ -1033,7 +1071,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             Some(1) => LimitedDebugInfo,
             Some(2) => FullDebugInfo,
             Some(arg) => {
-                early_error(color, &format!("debug info level needs to be between \
+                early_error(output, &format!("debug info level needs to be between \
                                              0-2 (instead was `{}`)",
                                             arg));
             }
@@ -1042,7 +1080,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let mut search_paths = SearchPaths::new();
     for s in &matches.opt_strs("L") {
-        search_paths.add_path(&s[..], color);
+        search_paths.add_path(&s[..], output);
     }
 
     let libs = matches.opt_strs("l").into_iter().map(|s| {
@@ -1054,7 +1092,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             (Some(name), "framework") => (name, cstore::NativeFramework),
             (Some(name), "static") => (name, cstore::NativeStatic),
             (_, s) => {
-                early_error(color, &format!("unknown library kind `{}`, expected \
+                early_error(output, &format!("unknown library kind `{}`, expected \
                                              one of dylib, framework, or static",
                                             s));
             }
@@ -1071,13 +1109,13 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             "file-names" => PrintRequest::FileNames,
             "sysroot" => PrintRequest::Sysroot,
             req => {
-                early_error(color, &format!("unknown print request `{}`", req))
+                early_error(output, &format!("unknown print request `{}`", req))
             }
         }
     }).collect::<Vec<_>>();
 
     if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
-        early_warn(color, "-C remark will not show source locations without \
+        early_warn(output, "-C remark will not show source locations without \
                            --debuginfo");
     }
 
@@ -1086,11 +1124,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         let mut parts = arg.splitn(2, '=');
         let name = match parts.next() {
             Some(s) => s,
-            None => early_error(color, "--extern value must not be empty"),
+            None => early_error(output, "--extern value must not be empty"),
         };
         let location = match parts.next() {
             Some(s) => s,
-            None => early_error(color, "--extern value must be of the format `foo=bar`"),
+            None => early_error(output, "--extern value must be of the format `foo=bar`"),
         };
 
         externs.entry(name.to_string()).or_insert(vec![]).push(location.to_string());
@@ -1121,7 +1159,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         debugging_opts: debugging_opts,
         prints: prints,
         cg: cg,
-        color: color,
+        output: output,
         externs: externs,
         crate_name: crate_name,
         alt_std_name: None,
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 80b4c1916a816..0abf8f28a8007 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -17,7 +17,8 @@ use util::nodemap::{NodeMap, FnvHashMap};
 use syntax::ast::{NodeId, NodeIdAssigner, Name};
 use syntax::codemap::Span;
 use syntax::errors::{self, DiagnosticBuilder};
-use syntax::errors::emitter::{Emitter, BasicEmitter};
+use syntax::errors::emitter::{Emitter, BasicEmitter, EmitterWriter};
+use syntax::errors::json::JsonEmitter;
 use syntax::diagnostics;
 use syntax::feature_gate;
 use syntax::parse;
@@ -405,12 +406,19 @@ pub fn build_session(sopts: config::Options,
     let treat_err_as_bug = sopts.treat_err_as_bug;
 
     let codemap = Rc::new(codemap::CodeMap::new());
+    let emitter: Box<Emitter> = match sopts.output {
+        config::ErrorOutputType::Tty(color_config) => {
+            Box::new(EmitterWriter::stderr(color_config, Some(registry), codemap.clone()))
+        }
+        config::ErrorOutputType::Json => {
+            Box::new(JsonEmitter::stderr(Some(registry), codemap.clone()))
+        }
+    };
+
     let diagnostic_handler =
-        errors::Handler::new(sopts.color,
-                             Some(registry),
-                             can_print_warnings,
-                             treat_err_as_bug,
-                             codemap.clone());
+        errors::Handler::with_emitter(can_print_warnings,
+                                      treat_err_as_bug,
+                                      emitter);
 
     build_session_(sopts, local_crate_source_file, diagnostic_handler, codemap, cstore)
 }
@@ -473,13 +481,19 @@ pub fn build_session_(sopts: config::Options,
     sess
 }
 
-pub fn early_error(color: errors::ColorConfig, msg: &str) -> ! {
-    let mut emitter = BasicEmitter::stderr(color);
+pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
+    let mut emitter: Box<Emitter> = match output {
+        config::ErrorOutputType::Tty(color_config) => Box::new(BasicEmitter::stderr(color_config)),
+        config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
+    };
     emitter.emit(None, msg, None, errors::Level::Fatal);
     panic!(errors::FatalError);
 }
 
-pub fn early_warn(color: errors::ColorConfig, msg: &str) {
-    let mut emitter = BasicEmitter::stderr(color);
+pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
+    let mut emitter: Box<Emitter> = match output {
+        config::ErrorOutputType::Tty(color_config) => Box::new(BasicEmitter::stderr(color_config)),
+        config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
+    };
     emitter.emit(None, msg, None, errors::Level::Warning);
 }
diff --git a/src/librustc/session/search_paths.rs b/src/librustc/session/search_paths.rs
index 6a787139d77aa..3c6cd26bef6ce 100644
--- a/src/librustc/session/search_paths.rs
+++ b/src/librustc/session/search_paths.rs
@@ -10,8 +10,7 @@
 
 use std::slice;
 use std::path::{Path, PathBuf};
-use session::early_error;
-use syntax::errors;
+use session::{early_error, config};
 
 #[derive(Clone, Debug)]
 pub struct SearchPaths {
@@ -38,7 +37,7 @@ impl SearchPaths {
         SearchPaths { paths: Vec::new() }
     }
 
-    pub fn add_path(&mut self, path: &str, color: errors::ColorConfig) {
+    pub fn add_path(&mut self, path: &str, output: config::ErrorOutputType) {
         let (kind, path) = if path.starts_with("native=") {
             (PathKind::Native, &path["native=".len()..])
         } else if path.starts_with("crate=") {
@@ -53,7 +52,7 @@ impl SearchPaths {
             (PathKind::All, path)
         };
         if path.is_empty() {
-            early_error(color, "empty search path given via `-L`");
+            early_error(output, "empty search path given via `-L`");
         }
         self.paths.push((kind, PathBuf::from(path)));
     }
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index cdac3de3682e5..f3fe7b116e6bf 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -62,7 +62,7 @@ use rustc_resolve as resolve;
 use rustc_trans::back::link;
 use rustc_trans::save;
 use rustc::session::{config, Session, build_session};
-use rustc::session::config::{Input, PrintRequest, OutputType};
+use rustc::session::config::{Input, PrintRequest, OutputType, ErrorOutputType};
 use rustc::middle::cstore::CrateStore;
 use rustc::lint::Lint;
 use rustc::lint;
@@ -72,6 +72,7 @@ use rustc::util::common::time;
 
 use std::cmp::max;
 use std::cmp::Ordering::Equal;
+use std::default::Default;
 use std::env;
 use std::io::{self, Read, Write};
 use std::iter::repeat;
@@ -126,7 +127,7 @@ pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>) {
 
     let descriptions = diagnostics_registry();
 
-    do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.color));
+    do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.output));
 
     let (odir, ofile) = make_output(&matches);
     let (input, input_file_path) = match make_input(&matches.free) {
@@ -214,7 +215,7 @@ pub trait CompilerCalls<'a> {
     fn early_callback(&mut self,
                       _: &getopts::Matches,
                       _: &diagnostics::registry::Registry,
-                      _: errors::ColorConfig)
+                      _: ErrorOutputType)
                       -> Compilation {
         Compilation::Continue
     }
@@ -290,7 +291,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
     fn early_callback(&mut self,
                       matches: &getopts::Matches,
                       descriptions: &diagnostics::registry::Registry,
-                      color: errors::ColorConfig)
+                      output: ErrorOutputType)
                       -> Compilation {
         match matches.opt_str("explain") {
             Some(ref code) => {
@@ -305,7 +306,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
                         print!("{}", &description[1..]);
                     }
                     None => {
-                        early_error(color, &format!("no extended information for {}", code));
+                        early_error(output, &format!("no extended information for {}", code));
                     }
                 }
                 return Compilation::Stop;
@@ -339,10 +340,10 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
                 if should_stop == Compilation::Stop {
                     return None;
                 }
-                early_error(sopts.color, "no input filename given");
+                early_error(sopts.output, "no input filename given");
             }
             1 => panic!("make_input should have provided valid inputs"),
-            _ => early_error(sopts.color, "multiple input filenames provided"),
+            _ => early_error(sopts.output, "multiple input filenames provided"),
         }
 
         None
@@ -432,7 +433,7 @@ impl RustcDefaultCalls {
                     println!("{}", String::from_utf8(v).unwrap());
                 }
                 &Input::Str(_) => {
-                    early_error(sess.opts.color, "cannot list metadata for stdin");
+                    early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
                 }
             }
             return Compilation::Stop;
@@ -459,7 +460,7 @@ impl RustcDefaultCalls {
                 PrintRequest::CrateName => {
                     let input = match input {
                         Some(input) => input,
-                        None => early_error(sess.opts.color, "no input file provided"),
+                        None => early_error(ErrorOutputType::default(), "no input file provided"),
                     };
                     let attrs = attrs.as_ref().unwrap();
                     let t_outputs = driver::build_output_filenames(input, odir, ofile, attrs, sess);
@@ -752,7 +753,7 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
                             &opt.opt_group.short_name
                         };
                         if m.opt_present(opt_name) {
-                            early_error(errors::ColorConfig::Auto,
+                            early_error(ErrorOutputType::default(),
                                         &format!("use of unstable option '{}' requires -Z \
                                                   unstable-options",
                                                  opt_name));
@@ -761,7 +762,7 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
                 }
                 m
             }
-            Err(f) => early_error(errors::ColorConfig::Auto, &f.to_string()),
+            Err(f) => early_error(ErrorOutputType::default(), &f.to_string()),
         }
     }
 
diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs
index 90ebf364367a0..2450414d8b67e 100644
--- a/src/librustc_trans/back/linker.rs
+++ b/src/librustc_trans/back/linker.rs
@@ -149,8 +149,8 @@ impl<'a> Linker for GnuLinker<'a> {
 
         // GNU-style linkers support optimization with -O. GNU ld doesn't
         // need a numeric argument, but other linkers do.
-        if self.sess.opts.optimize == config::Default ||
-           self.sess.opts.optimize == config::Aggressive {
+        if self.sess.opts.optimize == config::OptLevel::Default ||
+           self.sess.opts.optimize == config::OptLevel::Aggressive {
             self.cmd.arg("-Wl,-O1");
         }
     }
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index 9d0a83fe36350..544df1798eaf9 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -144,10 +144,10 @@ fn target_feature(sess: &Session) -> String {
 
 fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
     match optimize {
-      config::No => llvm::CodeGenLevelNone,
-      config::Less => llvm::CodeGenLevelLess,
-      config::Default => llvm::CodeGenLevelDefault,
-      config::Aggressive => llvm::CodeGenLevelAggressive,
+      config::OptLevel::No => llvm::CodeGenLevelNone,
+      config::OptLevel::Less => llvm::CodeGenLevelLess,
+      config::OptLevel::Default => llvm::CodeGenLevelDefault,
+      config::OptLevel::Aggressive => llvm::CodeGenLevelAggressive,
     }
 }
 
@@ -303,13 +303,13 @@ impl ModuleConfig {
         // slp vectorization at O3. Otherwise configure other optimization aspects
         // of this pass manager builder.
         self.vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
-                             (sess.opts.optimize == config::Default ||
-                              sess.opts.optimize == config::Aggressive);
+                             (sess.opts.optimize == config::OptLevel::Default ||
+                              sess.opts.optimize == config::OptLevel::Aggressive);
         self.vectorize_slp = !sess.opts.cg.no_vectorize_slp &&
-                            sess.opts.optimize == config::Aggressive;
+                            sess.opts.optimize == config::OptLevel::Aggressive;
 
-        self.merge_functions = sess.opts.optimize == config::Default ||
-                               sess.opts.optimize == config::Aggressive;
+        self.merge_functions = sess.opts.optimize == config::OptLevel::Default ||
+                               sess.opts.optimize == config::OptLevel::Aggressive;
     }
 }
 
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index f8b5f8e48f499..ea4259f8262a7 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -1163,7 +1163,7 @@ fn core_lifetime_emit<'blk, 'tcx, F>(ccx: &'blk CrateContext<'blk, 'tcx>,
                                      emit: F)
     where F: FnOnce(&'blk CrateContext<'blk, 'tcx>, machine::llsize, ValueRef)
 {
-    if ccx.sess().opts.optimize == config::No {
+    if ccx.sess().opts.optimize == config::OptLevel::No {
         return;
     }
 
diff --git a/src/librustc_trans/trans/debuginfo/metadata.rs b/src/librustc_trans/trans/debuginfo/metadata.rs
index 5284971f2c219..d90acd78147c9 100644
--- a/src/librustc_trans/trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/trans/debuginfo/metadata.rs
@@ -1020,7 +1020,7 @@ pub fn compile_unit_metadata(cx: &CrateContext) -> DIDescriptor {
             compile_unit_name,
             work_dir.as_ptr(),
             producer.as_ptr(),
-            cx.sess().opts.optimize != config::No,
+            cx.sess().opts.optimize != config::OptLevel::No,
             flags.as_ptr() as *const _,
             0,
             split_name.as_ptr() as *const _)
diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs
index ee1d834fc8a89..5e11a50be2273 100644
--- a/src/librustc_trans/trans/debuginfo/mod.rs
+++ b/src/librustc_trans/trans/debuginfo/mod.rs
@@ -383,7 +383,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             true,
             scope_line as c_uint,
             FlagPrototyped as c_uint,
-            cx.sess().opts.optimize != config::No,
+            cx.sess().opts.optimize != config::OptLevel::No,
             llfn,
             template_parameters,
             ptr::null_mut())
@@ -596,7 +596,7 @@ fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                     file_metadata,
                     loc.line as c_uint,
                     type_metadata,
-                    cx.sess().opts.optimize != config::No,
+                    cx.sess().opts.optimize != config::OptLevel::No,
                     0,
                     address_operations.as_ptr(),
                     address_operations.len() as c_uint,
diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs
index a7bfdedf71813..c21bf1e6a1fa0 100644
--- a/src/libsyntax/errors/emitter.rs
+++ b/src/libsyntax/errors/emitter.rs
@@ -43,7 +43,7 @@ pub trait Emitter {
 /// maximum number of lines we will print for each error; arbitrary.
 const MAX_LINES: usize = 6;
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum ColorConfig {
     Auto,
     Always,
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
index 733798c197a1c..68f8caf755aa6 100644
--- a/src/libsyntax/errors/mod.rs
+++ b/src/libsyntax/errors/mod.rs
@@ -276,6 +276,7 @@ pub struct Handler {
 }
 
 impl Handler {
+    // TODO remove
     pub fn new(color_config: ColorConfig,
                registry: Option<diagnostics::registry::Registry>,
                can_emit_warnings: bool,

From b976d9e6660c16f4a1d5a28b11afa7ccb4f75da6 Mon Sep 17 00:00:00 2001
From: Nick Cameron <ncameron@mozilla.com>
Date: Thu, 31 Dec 2015 18:47:14 +1300
Subject: [PATCH 3/5] Implement JSON error emission

[breaking-change]

syntax::errors::Handler::new has been renamed to with_tty_emitter

Many functions which used to take a syntax::errors::ColorConfig, now take a rustc::session::config::ErrorOutputType. If you previously used ColorConfig::Auto as a default, you should now use ErrorOutputType::default().
---
 src/librustc/session/config.rs |   4 +-
 src/libsyntax/errors/json.rs   | 204 +++++++++++++++++++++++++++++++--
 src/libsyntax/errors/mod.rs    |  33 +++---
 src/libsyntax/parse/mod.rs     |   2 +-
 4 files changed, 213 insertions(+), 30 deletions(-)

diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 6a7b411823ce9..01eb14474a282 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -940,8 +940,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             None => ErrorOutputType::default(),
 
             Some(arg) => {
-                early_error(ErrorOutputType::default(), &format!("argument for --output must be tty or \
-                                                                  json (instead was `{}`)",
+                early_error(ErrorOutputType::default(), &format!("argument for --output must be \
+                                                                  tty or json (instead was `{}`)",
                                                                  arg))
             }
         }
diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs
index feae71f55d3d2..0b8da7a09b121 100644
--- a/src/libsyntax/errors/json.rs
+++ b/src/libsyntax/errors/json.rs
@@ -9,45 +9,225 @@
 // except according to those terms.
 
 //! A JSON emitter for errors.
+//!
+//! This works by converting errors to a simplified structural format (see the
+//! structs at the start of the file) and then serialising them. These should
+//! contain as much information about the error as possible.
+//!
+//! The format of the JSON output should be considered *unstable*. For now the
+//! structs at the end of this file (Diagnostic*) specify the error format.
+
+// FIXME spec the JSON output properly.
+
 
 use codemap::{Span, CodeMap};
 use diagnostics::registry::Registry;
-use errors::{Level, DiagnosticBuilder, RenderSpan};
+use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan};
 use errors::emitter::Emitter;
 
 use std::rc::Rc;
+use std::io::{self, Write};
+
+use rustc_serialize::json::as_json;
 
 pub struct JsonEmitter {
-    todo: i32
+    dst: Box<Write + Send>,
+    registry: Option<Registry>,
+    cm: Rc<CodeMap>,
 }
 
 impl JsonEmitter {
     pub fn basic() -> JsonEmitter {
-        JsonEmitter {
-            todo: 42,
-        }
+        JsonEmitter::stderr(None, Rc::new(CodeMap::new()))
     }
 
     pub fn stderr(registry: Option<Registry>,
                   code_map: Rc<CodeMap>) -> JsonEmitter {
         JsonEmitter {
-            todo: 42,
+            dst: Box::new(io::stderr()),
+            registry: registry,
+            cm: code_map,
         }
     }
 }
 
 impl Emitter for JsonEmitter {
-    fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, lvl: Level) {
-        unimplemented!();
+    fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, level: Level) {
+        let data = Diagnostic::new(span, msg, code, level, self);
+        if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
+    }
+
+    fn custom_emit(&mut self, sp: RenderSpan, msg: &str, level: Level) {
+        let data = Diagnostic::from_render_span(&sp, msg, level, self);
+        if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
+    }
+
+    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
+        let data = Diagnostic::from_diagnostic_builder(db, self);
+        if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
+    }
+}
+
+// The following data types are provided just for serialisation.
+
+#[derive(RustcEncodable)]
+struct Diagnostic<'a> {
+    /// The primary error message.
+    message: &'a str,
+    code: Option<DiagnosticCode>,
+    /// "error: internal compiler error", "error", "warning", "note", "help".
+    level: &'static str,
+    span: Option<DiagnosticSpan>,
+    /// Assocaited diagnostic messages.
+    children: Vec<Diagnostic<'a>>,
+}
+
+#[derive(RustcEncodable)]
+struct DiagnosticSpan {
+    file_name: String,
+    byte_start: u32,
+    byte_end: u32,
+    /// 1-based.
+    line_start: usize,
+    line_end: usize,
+    /// 1-based, character offset.
+    column_start: usize,
+    column_end: usize,
+}
 
+#[derive(RustcEncodable)]
+struct DiagnosticCode {
+    /// The code itself.
+    code: String,
+    /// An explanation for the code.
+    explanation: Option<&'static str>,
+}
+
+impl<'a> Diagnostic<'a> {
+    fn new(span: Option<Span>,
+           msg: &'a str,
+           code: Option<&str>,
+           level: Level,
+           je: &JsonEmitter)
+           -> Diagnostic<'a> {
+        Diagnostic {
+            message: msg,
+            code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je),
+            level: level.to_str(),
+            span: span.map(|sp| DiagnosticSpan::from_span(sp, je)),
+            children: vec![],
+        }
     }
 
-    fn custom_emit(&mut self, sp: RenderSpan, msg: &str, lvl: Level) {
-        unimplemented!();
+    fn from_render_span(span: &RenderSpan,
+                        msg: &'a str,
+                        level: Level,
+                        je: &JsonEmitter)
+                        -> Diagnostic<'a> {
+        Diagnostic {
+            msg: msg,
+            code: None,
+            level: level.to_str(),
+            span: Some(DiagnosticSpan::from_render_span(span, je)),
+            children: vec![],
+        }
+    }
 
+    fn from_diagnostic_builder<'c>(db: &'c DiagnosticBuilder,
+                                   je: &JsonEmitter)
+                                   -> Diagnostic<'c> {
+        Diagnostic {
+            message: &db.message,
+            code: DiagnosticCode::map_opt_string(db.code.clone(), je),
+            level: db.level.to_str(),
+            span: db.span.map(|sp| DiagnosticSpan::from_span(sp, je)),
+            children: db.children.iter().map(|c| {
+                Diagnostic::from_sub_diagnostic(c, je)
+            }).collect(),
+        }
     }
 
-    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
-        unimplemented!();
+    fn from_sub_diagnostic<'c>(db: &'c SubDiagnostic, je: &JsonEmitter) -> Diagnostic<'c> {
+        Diagnostic {
+            message: &db.message,
+            code: None,
+            level: db.level.to_str(),
+            span: db.render_span.as_ref()
+                    .map(|sp| DiagnosticSpan::from_render_span(sp, je))
+                    .or_else(|| db.span.map(|sp| DiagnosticSpan::from_span(sp, je))),
+            children: vec![],
+        }
+    }
+}
+
+impl DiagnosticSpan {
+    fn from_span(span: Span, je: &JsonEmitter) -> DiagnosticSpan {
+        let start = je.cm.lookup_char_pos(span.lo);
+        let end = je.cm.lookup_char_pos(span.hi);
+        DiagnosticSpan {
+            file_name: start.file.name.clone(),
+            byte_start: span.lo.0,
+            byte_end: span.hi.0,
+            line_start: start.line,
+            line_end: end.line,
+            column_start: start.col.0 + 1,
+            column_end: end.col.0 + 1,
+        }
+    }
+
+    fn from_render_span(span: &RenderSpan, je: &JsonEmitter) -> DiagnosticSpan {
+        match *span {
+            // FIXME(#30701) handle Suggestion properly
+            RenderSpan::FullSpan(sp) | RenderSpan::Suggestion(sp, _) => {
+                DiagnosticSpan::from_span(sp, je)
+            }
+            RenderSpan::EndSpan(span) => {
+                let end = je.cm.lookup_char_pos(span.hi);
+                DiagnosticSpan {
+                    file_name: end.file.name.clone(),
+                    byte_start: span.lo.0,
+                    byte_end: span.hi.0,
+                    line_start: 0,
+                    line_end: end.line,
+                    column_start: 0,
+                    column_end: end.col.0 + 1,
+                }
+            }
+            RenderSpan::FileLine(span) => {
+                let start = je.cm.lookup_char_pos(span.lo);
+                let end = je.cm.lookup_char_pos(span.hi);
+                DiagnosticSpan {
+                    file_name: start.file.name.clone(),
+                    byte_start: span.lo.0,
+                    byte_end: span.hi.0,
+                    line_start: start.line,
+                    line_end: end.line,
+                    column_start: 0,
+                    column_end: 0,
+                }
+            }
+        }
+    }
+}
+
+impl DiagnosticCode {
+    fn map_opt_string(s: Option<String>, je: &JsonEmitter) -> Option<DiagnosticCode> {
+        s.map(|s| {
+
+            let explanation = je.registry
+                                .as_ref()
+                                .and_then(|registry| registry.find_description(&s));
+
+            DiagnosticCode {
+                code: s,
+                explanation: explanation,
+            }
+        })
     }
 }
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
index 68f8caf755aa6..f269dee31d9ea 100644
--- a/src/libsyntax/errors/mod.rs
+++ b/src/libsyntax/errors/mod.rs
@@ -276,13 +276,12 @@ pub struct Handler {
 }
 
 impl Handler {
-    // TODO remove
-    pub fn new(color_config: ColorConfig,
-               registry: Option<diagnostics::registry::Registry>,
-               can_emit_warnings: bool,
-               treat_err_as_bug: bool,
-               cm: Rc<codemap::CodeMap>)
-               -> Handler {
+    pub fn with_tty_emitter(color_config: ColorConfig,
+                            registry: Option<diagnostics::registry::Registry>,
+                            can_emit_warnings: bool,
+                            treat_err_as_bug: bool,
+                            cm: Rc<codemap::CodeMap>)
+                            -> Handler {
         let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm));
         Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter)
     }
@@ -549,14 +548,7 @@ impl fmt::Display for Level {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use std::fmt::Display;
 
-        match *self {
-            Bug => "error: internal compiler error".fmt(f),
-            Fatal | Error => "error".fmt(f),
-            Warning => "warning".fmt(f),
-            Note => "note".fmt(f),
-            Help => "help".fmt(f),
-            Cancelled => unreachable!(),
-        }
+        self.to_str().fmt(f)
     }
 }
 
@@ -570,6 +562,17 @@ impl Level {
             Cancelled => unreachable!(),
         }
     }
+
+    fn to_str(self) -> &'static str {
+        match self {
+            Bug => "error: internal compiler error",
+            Fatal | Error => "error",
+            Warning => "warning",
+            Note => "note",
+            Help => "help",
+            Cancelled => panic!("Shouldn't call on cancelled error"),
+        }
+    }
 }
 
 pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index a122456550116..090b070433f46 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -49,7 +49,7 @@ pub struct ParseSess {
 impl ParseSess {
     pub fn new() -> ParseSess {
         let cm = Rc::new(CodeMap::new());
-        let handler = Handler::new(ColorConfig::Auto, None, true, false, cm.clone());
+        let handler = Handler::with_tty_emitter(ColorConfig::Auto, None, true, false, cm.clone());
         ParseSess::with_span_handler(handler, cm)
     }
 

From 11dcb48c6a99e84952de3b7d5c1f6928d7b05867 Mon Sep 17 00:00:00 2001
From: Nick Cameron <ncameron@mozilla.com>
Date: Tue, 5 Jan 2016 14:35:22 +1300
Subject: [PATCH 4/5] Add a test

And fix bustage in make check
---
 src/librustdoc/core.rs                       | 10 +++++-----
 src/librustdoc/lib.rs                        |  7 ++++---
 src/librustdoc/test.rs                       | 10 +++++-----
 src/libsyntax/errors/json.rs                 |  2 +-
 src/test/run-make/execution-engine/test.rs   |  2 +-
 src/test/run-make/json-errors/Makefile       |  7 +++++++
 src/test/run-make/json-errors/foo.rs         | 15 +++++++++++++++
 src/test/run-pass-fulldeps/compiler-calls.rs |  2 +-
 8 files changed, 39 insertions(+), 16 deletions(-)
 create mode 100644 src/test/run-make/json-errors/Makefile
 create mode 100644 src/test/run-make/json-errors/foo.rs

diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index d57d1bcd92da5..a7fd170b91c37 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -118,11 +118,11 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
     };
 
     let codemap = Rc::new(codemap::CodeMap::new());
-    let diagnostic_handler = errors::Handler::new(ColorConfig::Auto,
-                                                  None,
-                                                  true,
-                                                  false,
-                                                  codemap.clone());
+    let diagnostic_handler = errors::Handler::with_tty_emitter(ColorConfig::Auto,
+                                                               None,
+                                                               true,
+                                                               false,
+                                                               codemap.clone());
 
     let cstore = Rc::new(CStore::new(token::get_ident_interner()));
     let sess = session::build_session_(sessopts, cpath, diagnostic_handler,
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index ffda261c24f3d..dce537fe9d292 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -50,6 +50,7 @@ extern crate serialize as rustc_serialize; // used by deriving
 
 use std::cell::RefCell;
 use std::collections::HashMap;
+use std::default::Default;
 use std::env;
 use std::fs::File;
 use std::io::{self, Read, Write};
@@ -62,7 +63,7 @@ use externalfiles::ExternalHtml;
 use serialize::Decodable;
 use serialize::json::{self, Json};
 use rustc::session::search_paths::SearchPaths;
-use syntax::errors::emitter::ColorConfig;
+use rustc::session::config::ErrorOutputType;
 
 // reexported from `clean` so it can be easily updated with the mod itself
 pub use clean::SCHEMA_VERSION;
@@ -225,7 +226,7 @@ pub fn main_args(args: &[String]) -> isize {
 
     let mut libs = SearchPaths::new();
     for s in &matches.opt_strs("L") {
-        libs.add_path(s, ColorConfig::Auto);
+        libs.add_path(s, ErrorOutputType::default());
     }
     let externs = match parse_externs(&matches) {
         Ok(ex) => ex,
@@ -360,7 +361,7 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche
     // First, parse the crate and extract all relevant information.
     let mut paths = SearchPaths::new();
     for s in &matches.opt_strs("L") {
-        paths.add_path(s, ColorConfig::Auto);
+        paths.add_path(s, ErrorOutputType::default());
     }
     let cfgs = matches.opt_strs("cfg");
     let triple = matches.opt_str("target");
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 7aa97d3652f5a..d7d30f065bf0e 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -73,11 +73,11 @@ pub fn run(input: &str,
     };
 
     let codemap = Rc::new(CodeMap::new());
-    let diagnostic_handler = errors::Handler::new(ColorConfig::Auto,
-                                                  None,
-                                                  true,
-                                                  false,
-                                                  codemap.clone());
+    let diagnostic_handler = errors::Handler::with_tty_emitter(ColorConfig::Auto,
+                                                               None,
+                                                               true,
+                                                               false,
+                                                               codemap.clone());
 
     let cstore = Rc::new(CStore::new(token::get_ident_interner()));
     let sess = session::build_session_(sessopts,
diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs
index 0b8da7a09b121..713190ef419d6 100644
--- a/src/libsyntax/errors/json.rs
+++ b/src/libsyntax/errors/json.rs
@@ -131,7 +131,7 @@ impl<'a> Diagnostic<'a> {
                         je: &JsonEmitter)
                         -> Diagnostic<'a> {
         Diagnostic {
-            msg: msg,
+            message: msg,
             code: None,
             level: level.to_str(),
             span: Some(DiagnosticSpan::from_render_span(span, je)),
diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs
index 928f2f996a028..dc409f393a86a 100644
--- a/src/test/run-make/execution-engine/test.rs
+++ b/src/test/run-make/execution-engine/test.rs
@@ -195,7 +195,7 @@ fn build_exec_options(sysroot: PathBuf) -> Options {
     opts.maybe_sysroot = Some(sysroot);
 
     // Prefer faster build time
-    opts.optimize = config::No;
+    opts.optimize = config::OptLevel::No;
 
     // Don't require a `main` function
     opts.crate_types = vec![config::CrateTypeDylib];
diff --git a/src/test/run-make/json-errors/Makefile b/src/test/run-make/json-errors/Makefile
new file mode 100644
index 0000000000000..2c1eae8115086
--- /dev/null
+++ b/src/test/run-make/json-errors/Makefile
@@ -0,0 +1,7 @@
+-include ../tools.mk
+
+all:
+	cp foo.rs $(TMPDIR)
+	cd $(TMPDIR)
+	$(RUSTC) -Z unstable-options --output=json foo.rs 2>foo.log || true
+	grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"level":"error","span":{"file_name":"foo.rs","byte_start":523,"byte_end":524,"line_start":14,"line_end":14,"column_start":18,"column_end":19},"children":\[\]}' foo.log
diff --git a/src/test/run-make/json-errors/foo.rs b/src/test/run-make/json-errors/foo.rs
new file mode 100644
index 0000000000000..9a6f4ad8359e0
--- /dev/null
+++ b/src/test/run-make/json-errors/foo.rs
@@ -0,0 +1,15 @@
+// Copyright 2015 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.
+
+// ignore-tidy-linelength
+
+fn main() {
+    let x = 42 + y;
+}
diff --git a/src/test/run-pass-fulldeps/compiler-calls.rs b/src/test/run-pass-fulldeps/compiler-calls.rs
index e3eeeb8635688..56481dc646a9c 100644
--- a/src/test/run-pass-fulldeps/compiler-calls.rs
+++ b/src/test/run-pass-fulldeps/compiler-calls.rs
@@ -35,7 +35,7 @@ impl<'a> CompilerCalls<'a> for TestCalls {
     fn early_callback(&mut self,
                       _: &getopts::Matches,
                       _: &diagnostics::registry::Registry,
-                      _: errors::emitter::ColorConfig)
+                      _: config::ErrorOutputType)
                       -> Compilation {
         self.count *= 2;
         Compilation::Continue

From 82f8e5ce84c83b02fbfa720c6841f12db1a55603 Mon Sep 17 00:00:00 2001
From: Nick Cameron <ncameron@mozilla.com>
Date: Thu, 7 Jan 2016 09:23:01 +1300
Subject: [PATCH 5/5] Address reviewer comments

[breaking-change]

`OptLevel` variants are no longer `pub use`ed by rust::session::config. If you are using these variants, you must change your code to prefix the variant name with `OptLevel`.
---
 src/librustc/session/config.rs         | 110 ++++++++++++-------------
 src/librustc/session/mod.rs            |  12 ++-
 src/librustc_driver/lib.rs             |   6 +-
 src/test/run-make/json-errors/Makefile |   5 +-
 src/test/run-make/json-errors/foo.rs   |   4 +-
 5 files changed, 69 insertions(+), 68 deletions(-)

diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 01eb14474a282..47bc46ab2bc8e 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -72,13 +72,13 @@ pub enum OutputType {
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum ErrorOutputType {
-    Tty(ColorConfig),
+    HumanReadable(ColorConfig),
     Json,
 }
 
 impl Default for ErrorOutputType {
     fn default() -> ErrorOutputType {
-        ErrorOutputType::Tty(ColorConfig::Auto)
+        ErrorOutputType::HumanReadable(ColorConfig::Auto)
     }
 }
 
@@ -135,7 +135,7 @@ pub struct Options {
     pub test: bool,
     pub parse_only: bool,
     pub no_trans: bool,
-    pub output: ErrorOutputType,
+    pub error_format: ErrorOutputType,
     pub treat_err_as_bug: bool,
     pub incremental_compilation: bool,
     pub dump_dep_graph: bool,
@@ -252,12 +252,7 @@ pub fn basic_options() -> Options {
         debugging_opts: basic_debugging_options(),
         prints: Vec::new(),
         cg: basic_codegen_options(),
-<<<<<<< HEAD
-        color: ColorConfig::Auto,
-=======
-        output: ErrorOutputType::default(),
-        show_span: None,
->>>>>>> Add an --output option for specifying an error emitter
+        error_format: ErrorOutputType::default(),
         externs: HashMap::new(),
         crate_name: None,
         alt_std_name: None,
@@ -324,7 +319,7 @@ macro_rules! options {
         $struct_name { $($opt: $init),* }
     }
 
-    pub fn $buildfn(matches: &getopts::Matches, output: ErrorOutputType) -> $struct_name
+    pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $struct_name
     {
         let mut op = $defaultfn();
         for option in matches.opt_strs($prefix) {
@@ -338,20 +333,20 @@ macro_rules! options {
                 if !setter(&mut op, value) {
                     match (value, opt_type_desc) {
                         (Some(..), None) => {
-                            early_error(output, &format!("{} option `{}` takes no \
-                                                         value", $outputname, key))
+                            early_error(error_format, &format!("{} option `{}` takes no \
+                                                              value", $outputname, key))
                         }
                         (None, Some(type_desc)) => {
-                            early_error(output, &format!("{0} option `{1}` requires \
-                                                         {2} ({3} {1}=<value>)",
-                                                        $outputname, key,
-                                                        type_desc, $prefix))
+                            early_error(error_format, &format!("{0} option `{1}` requires \
+                                                              {2} ({3} {1}=<value>)",
+                                                             $outputname, key,
+                                                             type_desc, $prefix))
                         }
                         (Some(value), Some(type_desc)) => {
-                            early_error(output, &format!("incorrect value `{}` for {} \
-                                                         option `{}` - {} was expected",
-                                                        value, $outputname,
-                                                        key, type_desc))
+                            early_error(error_format, &format!("incorrect value `{}` for {} \
+                                                              option `{}` - {} was expected",
+                                                             value, $outputname,
+                                                             key, type_desc))
                         }
                         (None, None) => unreachable!()
                     }
@@ -360,8 +355,8 @@ macro_rules! options {
                 break;
             }
             if !found {
-                early_error(output, &format!("unknown {} option: `{}`",
-                                            $outputname, key));
+                early_error(error_format, &format!("unknown {} option: `{}`",
+                                                 $outputname, key));
             }
         }
         return op;
@@ -879,7 +874,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
                  "NAME=PATH"),
         opt::opt("", "sysroot", "Override the system root", "PATH"),
         opt::multi("Z", "", "Set internal debugging options", "FLAG"),
-        opt::opt_u("", "output", "How errors and other mesasges are produced", "tty|json"),
+        opt::opt_u("", "error-format", "How errors and other messages are produced", "human|json"),
         opt::opt("", "color", "Configure coloring of output:
             auto   = colorize, if output goes to a tty (default);
             always = always colorize output;
@@ -929,19 +924,20 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     };
 
     // We need the opts_present check because the driver will send us Matches
-    // with only stable options if no unstable options are used. Since output is
-    // unstable, it will not be present. We have to use opts_present not
+    // with only stable options if no unstable options are used. Since error-format
+    // is unstable, it will not be present. We have to use opts_present not
     // opt_present because the latter will panic.
-    let output = if matches.opts_present(&["output".to_owned()]) {
-        match matches.opt_str("output").as_ref().map(|s| &s[..]) {
-            Some("tty")   => ErrorOutputType::Tty(color),
+    let error_format = if matches.opts_present(&["error-format".to_owned()]) {
+        match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
+            Some("human")   => ErrorOutputType::HumanReadable(color),
             Some("json") => ErrorOutputType::Json,
 
             None => ErrorOutputType::default(),
 
             Some(arg) => {
-                early_error(ErrorOutputType::default(), &format!("argument for --output must be \
-                                                                  tty or json (instead was `{}`)",
+                early_error(ErrorOutputType::default(), &format!("argument for --error-format must \
+                                                                  be human or json (instead was \
+                                                                  `{}`)",
                                                                  arg))
             }
         }
@@ -951,7 +947,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let unparsed_crate_types = matches.opt_strs("crate-type");
     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
-        .unwrap_or_else(|e| early_error(output, &e[..]));
+        .unwrap_or_else(|e| early_error(error_format, &e[..]));
 
     let mut lint_opts = vec!();
     let mut describe_lints = false;
@@ -968,11 +964,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let lint_cap = matches.opt_str("cap-lints").map(|cap| {
         lint::Level::from_str(&cap).unwrap_or_else(|| {
-            early_error(output, &format!("unknown lint level: `{}`", cap))
+            early_error(error_format, &format!("unknown lint level: `{}`", cap))
         })
     });
 
-    let debugging_opts = build_debugging_options(matches, output);
+    let debugging_opts = build_debugging_options(matches, error_format);
 
     let parse_only = debugging_opts.parse_only;
     let no_trans = debugging_opts.no_trans;
@@ -998,7 +994,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
                     "link" => OutputType::Exe,
                     "dep-info" => OutputType::DepInfo,
                     part => {
-                        early_error(output, &format!("unknown emission type: `{}`",
+                        early_error(error_format, &format!("unknown emission type: `{}`",
                                                     part))
                     }
                 };
@@ -1011,7 +1007,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         output_types.insert(OutputType::Exe, None);
     }
 
-    let mut cg = build_codegen_options(matches, output);
+    let mut cg = build_codegen_options(matches, error_format);
 
     // Issue #30063: if user requests llvm-related output to one
     // particular path, disable codegen-units.
@@ -1023,11 +1019,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             }).collect();
         if !incompatible.is_empty() {
             for ot in &incompatible {
-                early_warn(output, &format!("--emit={} with -o incompatible with \
-                                            -C codegen-units=N for N > 1",
-                                           ot.shorthand()));
+                early_warn(error_format, &format!("--emit={} with -o incompatible with \
+                                                 -C codegen-units=N for N > 1",
+                                                ot.shorthand()));
             }
-            early_warn(output, "resetting to default -C codegen-units=1");
+            early_warn(error_format, "resetting to default -C codegen-units=1");
             cg.codegen_units = 1;
         }
     }
@@ -1040,7 +1036,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let opt_level = {
         if matches.opt_present("O") {
             if cg.opt_level.is_some() {
-                early_error(output, "-O and -C opt-level both provided");
+                early_error(error_format, "-O and -C opt-level both provided");
             }
             OptLevel::Default
         } else {
@@ -1051,9 +1047,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
                 Some(2) => OptLevel::Default,
                 Some(3) => OptLevel::Aggressive,
                 Some(arg) => {
-                    early_error(output, &format!("optimization level needs to be \
-                                                 between 0-3 (instead was `{}`)",
-                                                arg));
+                    early_error(error_format, &format!("optimization level needs to be \
+                                                      between 0-3 (instead was `{}`)",
+                                                     arg));
                 }
             }
         }
@@ -1062,7 +1058,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let gc = debugging_opts.gc;
     let debuginfo = if matches.opt_present("g") {
         if cg.debuginfo.is_some() {
-            early_error(output, "-g and -C debuginfo both provided");
+            early_error(error_format, "-g and -C debuginfo both provided");
         }
         FullDebugInfo
     } else {
@@ -1071,16 +1067,16 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             Some(1) => LimitedDebugInfo,
             Some(2) => FullDebugInfo,
             Some(arg) => {
-                early_error(output, &format!("debug info level needs to be between \
-                                             0-2 (instead was `{}`)",
-                                            arg));
+                early_error(error_format, &format!("debug info level needs to be between \
+                                                  0-2 (instead was `{}`)",
+                                                 arg));
             }
         }
     };
 
     let mut search_paths = SearchPaths::new();
     for s in &matches.opt_strs("L") {
-        search_paths.add_path(&s[..], output);
+        search_paths.add_path(&s[..], error_format);
     }
 
     let libs = matches.opt_strs("l").into_iter().map(|s| {
@@ -1092,9 +1088,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             (Some(name), "framework") => (name, cstore::NativeFramework),
             (Some(name), "static") => (name, cstore::NativeStatic),
             (_, s) => {
-                early_error(output, &format!("unknown library kind `{}`, expected \
-                                             one of dylib, framework, or static",
-                                            s));
+                early_error(error_format, &format!("unknown library kind `{}`, expected \
+                                                  one of dylib, framework, or static",
+                                                 s));
             }
         };
         (name.to_string(), kind)
@@ -1109,14 +1105,14 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             "file-names" => PrintRequest::FileNames,
             "sysroot" => PrintRequest::Sysroot,
             req => {
-                early_error(output, &format!("unknown print request `{}`", req))
+                early_error(error_format, &format!("unknown print request `{}`", req))
             }
         }
     }).collect::<Vec<_>>();
 
     if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
-        early_warn(output, "-C remark will not show source locations without \
-                           --debuginfo");
+        early_warn(error_format, "-C remark will not show source locations without \
+                                --debuginfo");
     }
 
     let mut externs = HashMap::new();
@@ -1124,11 +1120,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         let mut parts = arg.splitn(2, '=');
         let name = match parts.next() {
             Some(s) => s,
-            None => early_error(output, "--extern value must not be empty"),
+            None => early_error(error_format, "--extern value must not be empty"),
         };
         let location = match parts.next() {
             Some(s) => s,
-            None => early_error(output, "--extern value must be of the format `foo=bar`"),
+            None => early_error(error_format, "--extern value must be of the format `foo=bar`"),
         };
 
         externs.entry(name.to_string()).or_insert(vec![]).push(location.to_string());
@@ -1159,7 +1155,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         debugging_opts: debugging_opts,
         prints: prints,
         cg: cg,
-        output: output,
+        error_format: error_format,
         externs: externs,
         crate_name: crate_name,
         alt_std_name: None,
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 0abf8f28a8007..2f3af1c0d09b5 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -406,8 +406,8 @@ pub fn build_session(sopts: config::Options,
     let treat_err_as_bug = sopts.treat_err_as_bug;
 
     let codemap = Rc::new(codemap::CodeMap::new());
-    let emitter: Box<Emitter> = match sopts.output {
-        config::ErrorOutputType::Tty(color_config) => {
+    let emitter: Box<Emitter> = match sopts.error_format {
+        config::ErrorOutputType::HumanReadable(color_config) => {
             Box::new(EmitterWriter::stderr(color_config, Some(registry), codemap.clone()))
         }
         config::ErrorOutputType::Json => {
@@ -483,7 +483,9 @@ pub fn build_session_(sopts: config::Options,
 
 pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
     let mut emitter: Box<Emitter> = match output {
-        config::ErrorOutputType::Tty(color_config) => Box::new(BasicEmitter::stderr(color_config)),
+        config::ErrorOutputType::HumanReadable(color_config) => {
+            Box::new(BasicEmitter::stderr(color_config))
+        }
         config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
     };
     emitter.emit(None, msg, None, errors::Level::Fatal);
@@ -492,7 +494,9 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
 
 pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
     let mut emitter: Box<Emitter> = match output {
-        config::ErrorOutputType::Tty(color_config) => Box::new(BasicEmitter::stderr(color_config)),
+        config::ErrorOutputType::HumanReadable(color_config) => {
+            Box::new(BasicEmitter::stderr(color_config))
+        }
         config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()),
     };
     emitter.emit(None, msg, None, errors::Level::Warning);
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index f3fe7b116e6bf..62bea61216888 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -127,7 +127,7 @@ pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>) {
 
     let descriptions = diagnostics_registry();
 
-    do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.output));
+    do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.error_format));
 
     let (odir, ofile) = make_output(&matches);
     let (input, input_file_path) = match make_input(&matches.free) {
@@ -340,10 +340,10 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
                 if should_stop == Compilation::Stop {
                     return None;
                 }
-                early_error(sopts.output, "no input filename given");
+                early_error(sopts.error_format, "no input filename given");
             }
             1 => panic!("make_input should have provided valid inputs"),
-            _ => early_error(sopts.output, "multiple input filenames provided"),
+            _ => early_error(sopts.error_format, "multiple input filenames provided"),
         }
 
         None
diff --git a/src/test/run-make/json-errors/Makefile b/src/test/run-make/json-errors/Makefile
index 2c1eae8115086..2467e08300c18 100644
--- a/src/test/run-make/json-errors/Makefile
+++ b/src/test/run-make/json-errors/Makefile
@@ -3,5 +3,6 @@
 all:
 	cp foo.rs $(TMPDIR)
 	cd $(TMPDIR)
-	$(RUSTC) -Z unstable-options --output=json foo.rs 2>foo.log || true
-	grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"level":"error","span":{"file_name":"foo.rs","byte_start":523,"byte_end":524,"line_start":14,"line_end":14,"column_start":18,"column_end":19},"children":\[\]}' foo.log
+	-$(RUSTC) -Z unstable-options --error-format=json foo.rs 2>foo.log
+	grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"level":"error","span":{"file_name":"foo.rs","byte_start":496,"byte_end":497,"line_start":12,"line_end":12,"column_start":18,"column_end":19},"children":\[\]}' foo.log
+	grep -q '{"message":".*","code":{"code":"E0277","explanation":"\\nYou tried.*"},"level":"error","span":{.*},"children":\[{"message":"the .*","code":null,"level":"help","span":{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":0,"column_end":0},"children":\[\]},{"message":"  <u8 as core::ops::Add>","code":null,"level":"help",' foo.log
diff --git a/src/test/run-make/json-errors/foo.rs b/src/test/run-make/json-errors/foo.rs
index 9a6f4ad8359e0..4db33940d8843 100644
--- a/src/test/run-make/json-errors/foo.rs
+++ b/src/test/run-make/json-errors/foo.rs
@@ -8,8 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// ignore-tidy-linelength
-
 fn main() {
     let x = 42 + y;
+
+    42u8 + 42i32;
 }