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; }