about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-01-15 01:52:01 +0000
committerbors <bors@rust-lang.org>2016-01-15 01:52:01 +0000
commitd8869d3487a569b4a6b86c1b585cc15db48abc4a (patch)
treef061c00a00e41e3518bf7f8b16579b5853ac6b3b
parent2fb0c5ebcf6d912224532265776fb96febea9797 (diff)
parent82f8e5ce84c83b02fbfa720c6841f12db1a55603 (diff)
Auto merge of #30711 - nrc:json-errs, r=huonw
The compiler can emit errors and warning in JSON format. This is a more easily machine readable form then the usual error output.

Closes #10492, closes #14863.
-rw-r--r--src/librustc/lint/context.rs9
-rw-r--r--src/librustc/session/config.rs142
-rw-r--r--src/librustc/session/mod.rs38
-rw-r--r--src/librustc/session/search_paths.rs7
-rw-r--r--src/librustc_driver/lib.rs23
-rw-r--r--src/librustc_trans/back/linker.rs4
-rw-r--r--src/librustc_trans/back/write.rs18
-rw-r--r--src/librustc_trans/trans/base.rs2
-rw-r--r--src/librustc_trans/trans/debuginfo/metadata.rs2
-rw-r--r--src/librustc_trans/trans/debuginfo/mod.rs4
-rw-r--r--src/librustdoc/core.rs10
-rw-r--r--src/librustdoc/lib.rs7
-rw-r--r--src/librustdoc/test.rs10
-rw-r--r--src/libsyntax/errors/emitter.rs2
-rw-r--r--src/libsyntax/errors/json.rs233
-rw-r--r--src/libsyntax/errors/mod.rs33
-rw-r--r--src/libsyntax/parse/mod.rs2
-rw-r--r--src/test/run-make/execution-engine/test.rs2
-rw-r--r--src/test/run-make/json-errors/Makefile8
-rw-r--r--src/test/run-make/json-errors/foo.rs15
-rw-r--r--src/test/run-pass-fulldeps/compiler-calls.rs2
21 files changed, 444 insertions, 129 deletions
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 08fba2dc56f..d7eacbfff90 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 fd7ee0b13ac..1a99aba591a 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 {
+    HumanReadable(ColorConfig),
+    Json,
+}
+
+impl Default for ErrorOutputType {
+    fn default() -> ErrorOutputType {
+        ErrorOutputType::HumanReadable(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 error_format: 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,7 @@ pub fn basic_options() -> Options {
         debugging_opts: basic_debugging_options(),
         prints: Vec::new(),
         cg: basic_codegen_options(),
-        color: ColorConfig::Auto,
+        error_format: ErrorOutputType::default(),
         externs: HashMap::new(),
         crate_name: None,
         alt_std_name: None,
@@ -308,7 +319,7 @@ macro_rules! options {
         $struct_name { $($opt: $init),* }
     }
 
-    pub fn $buildfn(matches: &getopts::Matches, color: ColorConfig) -> $struct_name
+    pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $struct_name
     {
         let mut op = $defaultfn();
         for option in matches.opt_strs($prefix) {
@@ -322,20 +333,20 @@ macro_rules! options {
                 if !setter(&mut op, value) {
                     match (value, opt_type_desc) {
                         (Some(..), None) => {
-                            early_error(color, &format!("{} option `{}` takes no \
-                                                         value", $outputname, key))
+                            early_error(error_format, &format!("{} option `{}` takes no \
+                                                              value", $outputname, key))
                         }
                         (None, Some(type_desc)) => {
-                            early_error(color, &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(color, &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!()
                     }
@@ -344,8 +355,8 @@ macro_rules! options {
                 break;
             }
             if !found {
-                early_error(color, &format!("unknown {} option: `{}`",
-                                            $outputname, key));
+                early_error(error_format, &format!("unknown {} option: `{}`",
+                                                 $outputname, key));
             }
         }
         return op;
@@ -861,6 +872,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("", "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;
@@ -903,15 +915,37 @@ 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 error-format
+    // is unstable, it will not be present. We have to use opts_present not
+    // opt_present because the latter will panic.
+    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 --error-format must \
+                                                                  be human 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(error_format, &e[..]));
 
     let mut lint_opts = vec!();
     let mut describe_lints = false;
@@ -928,11 +962,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(error_format, &format!("unknown lint level: `{}`", cap))
         })
     });
 
-    let debugging_opts = build_debugging_options(matches, color);
+    let debugging_opts = build_debugging_options(matches, error_format);
 
     let parse_only = debugging_opts.parse_only;
     let no_trans = debugging_opts.no_trans;
@@ -958,7 +992,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(error_format, &format!("unknown emission type: `{}`",
                                                     part))
                     }
                 };
@@ -971,7 +1005,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, error_format);
 
     // Issue #30063: if user requests llvm-related output to one
     // particular path, disable codegen-units.
@@ -983,11 +1017,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 \
-                                            -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(color, "resetting to default -C codegen-units=1");
+            early_warn(error_format, "resetting to default -C codegen-units=1");
             cg.codegen_units = 1;
         }
     }
@@ -1000,29 +1034,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(error_format, "-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 \
-                                                 between 0-3 (instead was `{}`)",
-                                                arg));
+                    early_error(error_format, &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(error_format, "-g and -C debuginfo both provided");
         }
         FullDebugInfo
     } else {
@@ -1031,16 +1065,16 @@ 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 \
-                                             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[..], color);
+        search_paths.add_path(&s[..], error_format);
     }
 
     let libs = matches.opt_strs("l").into_iter().map(|s| {
@@ -1052,9 +1086,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(color, &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)
@@ -1069,14 +1103,14 @@ 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(error_format, &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 \
-                           --debuginfo");
+        early_warn(error_format, "-C remark will not show source locations without \
+                                --debuginfo");
     }
 
     let mut externs = HashMap::new();
@@ -1084,11 +1118,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(error_format, "--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(error_format, "--extern value must be of the format `foo=bar`"),
         };
 
         externs.entry(name.to_string()).or_insert(vec![]).push(location.to_string());
@@ -1119,7 +1153,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         debugging_opts: debugging_opts,
         prints: prints,
         cg: cg,
-        color: color,
+        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 80b4c1916a8..2f3af1c0d09 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.error_format {
+        config::ErrorOutputType::HumanReadable(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,23 @@ 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::HumanReadable(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::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/session/search_paths.rs b/src/librustc/session/search_paths.rs
index 6a787139d77..3c6cd26bef6 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 cdac3de3682..62bea612168 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.error_format));
 
     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.error_format, "no input filename given");
             }
             1 => panic!("make_input should have provided valid inputs"),
-            _ => early_error(sopts.color, "multiple input filenames provided"),
+            _ => early_error(sopts.error_format, "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 1ee1c9f1912..f585c65228a 100644
--- a/src/librustc_trans/back/linker.rs
+++ b/src/librustc_trans/back/linker.rs
@@ -147,8 +147,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 9d0a83fe363..544df1798ea 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 f8b5f8e48f4..ea4259f8262 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 5284971f2c2..d90acd78147 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 ee1d834fc8a..5e11a50be22 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/librustdoc/core.rs b/src/librustdoc/core.rs
index d57d1bcd92d..a7fd170b91c 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 ffda261c24f..dce537fe9d2 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 7aa97d3652f..d7d30f065bf 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/emitter.rs b/src/libsyntax/errors/emitter.rs
index a7bfdedf718..c21bf1e6a1f 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/json.rs b/src/libsyntax/errors/json.rs
new file mode 100644
index 00000000000..713190ef419
--- /dev/null
+++ b/src/libsyntax/errors/json.rs
@@ -0,0 +1,233 @@
+// 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.
+//!
+//! 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, SubDiagnostic, RenderSpan};
+use errors::emitter::Emitter;
+
+use std::rc::Rc;
+use std::io::{self, Write};
+
+use rustc_serialize::json::as_json;
+
+pub struct JsonEmitter {
+    dst: Box<Write + Send>,
+    registry: Option<Registry>,
+    cm: Rc<CodeMap>,
+}
+
+impl JsonEmitter {
+    pub fn basic() -> JsonEmitter {
+        JsonEmitter::stderr(None, Rc::new(CodeMap::new()))
+    }
+
+    pub fn stderr(registry: Option<Registry>,
+                  code_map: Rc<CodeMap>) -> JsonEmitter {
+        JsonEmitter {
+            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>, 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 from_render_span(span: &RenderSpan,
+                        msg: &'a str,
+                        level: Level,
+                        je: &JsonEmitter)
+                        -> Diagnostic<'a> {
+        Diagnostic {
+            message: 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 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 a2fae975148..f269dee31d9 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 {
@@ -275,12 +276,12 @@ pub struct Handler {
 }
 
 impl Handler {
-    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)
     }
@@ -547,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)
     }
 }
 
@@ -568,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 a1224565501..090b070433f 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)
     }
 
diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs
index 928f2f996a0..dc409f393a8 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 00000000000..2467e08300c
--- /dev/null
+++ b/src/test/run-make/json-errors/Makefile
@@ -0,0 +1,8 @@
+-include ../tools.mk
+
+all:
+	cp foo.rs $(TMPDIR)
+	cd $(TMPDIR)
+	-$(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
new file mode 100644
index 00000000000..4db33940d88
--- /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.
+
+fn main() {
+    let x = 42 + y;
+
+    42u8 + 42i32;
+}
diff --git a/src/test/run-pass-fulldeps/compiler-calls.rs b/src/test/run-pass-fulldeps/compiler-calls.rs
index e3eeeb86356..56481dc646a 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