1
Fork 0

Rollup merge of #59128 - oli-obk:colorful_json, r=mark-i-m,eddyb

Emit ansi color codes in the `rendered` field of json diagnostics

cc @ljedrz

Implemented for https://github.com/rust-lang/rust/pull/56595#issuecomment-447645115 (x.py clippy)
This commit is contained in:
Mazdak Farrokhzad 2019-04-17 10:31:30 +02:00 committed by GitHub
commit c89bc54d4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 287 additions and 230 deletions

View file

@ -19,6 +19,7 @@ use syntax::parse::token;
use syntax::parse; use syntax::parse;
use syntax::symbol::Symbol; use syntax::symbol::Symbol;
use syntax::feature_gate::UnstableFeatures; use syntax::feature_gate::UnstableFeatures;
use errors::emitter::HumanReadableErrorType;
use errors::{ColorConfig, FatalError, Handler}; use errors::{ColorConfig, FatalError, Handler};
@ -219,14 +220,18 @@ impl OutputType {
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorOutputType { pub enum ErrorOutputType {
HumanReadable(ColorConfig), HumanReadable(HumanReadableErrorType),
Json(bool), Json {
Short(ColorConfig), /// Render the json in a human readable way (with indents and newlines)
pretty: bool,
/// The way the `rendered` field is created
json_rendered: HumanReadableErrorType,
},
} }
impl Default for ErrorOutputType { impl Default for ErrorOutputType {
fn default() -> ErrorOutputType { fn default() -> ErrorOutputType {
ErrorOutputType::HumanReadable(ColorConfig::Auto) ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto))
} }
} }
@ -1372,6 +1377,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"print some statistics about AST and HIR"), "print some statistics about AST and HIR"),
always_encode_mir: bool = (false, parse_bool, [TRACKED], always_encode_mir: bool = (false, parse_bool, [TRACKED],
"encode MIR of all functions into the crate metadata"), "encode MIR of all functions into the crate metadata"),
json_rendered: Option<String> = (None, parse_opt_string, [UNTRACKED],
"describes how to render the `rendered` field of json diagnostics"),
unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED],
"take the breaks off const evaluation. NOTE: this is unsound"), "take the breaks off const evaluation. NOTE: this is unsound"),
osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
@ -1825,6 +1832,12 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
"How errors and other messages are produced", "How errors and other messages are produced",
"human|json|short", "human|json|short",
), ),
opt::opt(
"",
"json-rendered",
"Choose `rendered` field of json diagnostics render scheme",
"plain|termcolor",
),
opt::opt_s( opt::opt_s(
"", "",
"color", "color",
@ -1965,6 +1978,17 @@ pub fn build_session_options_and_crate_config(
) )
} }
let json_rendered = matches.opt_str("json-rendered").and_then(|s| match s.as_str() {
"plain" => None,
"termcolor" => Some(HumanReadableErrorType::Default(ColorConfig::Always)),
_ => early_error(
ErrorOutputType::default(),
&format!(
"argument for --json-rendered must be `plain` or `termcolor` (instead was `{}`)",
s,
),
),
}).unwrap_or(HumanReadableErrorType::Default(ColorConfig::Never));
// We need the opts_present check because the driver will send us Matches // 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 // with only stable options if no unstable options are used. Since error-format
@ -1972,14 +1996,14 @@ pub fn build_session_options_and_crate_config(
// opt_present because the latter will panic. // opt_present because the latter will panic.
let error_format = if matches.opts_present(&["error-format".to_owned()]) { let error_format = if matches.opts_present(&["error-format".to_owned()]) {
match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
Some("human") => ErrorOutputType::HumanReadable(color), None |
Some("json") => ErrorOutputType::Json(false), Some("human") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)),
Some("pretty-json") => ErrorOutputType::Json(true), Some("json") => ErrorOutputType::Json { pretty: false, json_rendered },
Some("short") => ErrorOutputType::Short(color), Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered },
None => ErrorOutputType::HumanReadable(color), Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)),
Some(arg) => early_error( Some(arg) => early_error(
ErrorOutputType::HumanReadable(color), ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)),
&format!( &format!(
"argument for --error-format must be `human`, `json` or \ "argument for --error-format must be `human`, `json` or \
`short` (instead was `{}`)", `short` (instead was `{}`)",
@ -1988,7 +2012,7 @@ pub fn build_session_options_and_crate_config(
), ),
} }
} else { } else {
ErrorOutputType::HumanReadable(color) ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color))
}; };
let unparsed_crate_types = matches.opt_strs("crate-type"); let unparsed_crate_types = matches.opt_strs("crate-type");
@ -2000,11 +2024,16 @@ pub fn build_session_options_and_crate_config(
let mut debugging_opts = build_debugging_options(matches, error_format); let mut debugging_opts = build_debugging_options(matches, error_format);
if !debugging_opts.unstable_options && error_format == ErrorOutputType::Json(true) { if !debugging_opts.unstable_options {
early_error( if matches.opt_str("json-rendered").is_some() {
ErrorOutputType::Json(false), early_error(error_format, "`--json-rendered=x` is unstable");
"--error-format=pretty-json is unstable", }
); if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format {
early_error(
ErrorOutputType::Json { pretty: false, json_rendered },
"--error-format=pretty-json is unstable",
);
}
} }
if debugging_opts.pgo_gen.enabled() && !debugging_opts.pgo_use.is_empty() { if debugging_opts.pgo_gen.enabled() && !debugging_opts.pgo_use.is_empty() {
@ -2928,50 +2957,55 @@ mod tests {
let mut v3 = Options::default(); let mut v3 = Options::default();
let mut v4 = Options::default(); let mut v4 = Options::default();
const JSON: super::ErrorOutputType = super::ErrorOutputType::Json {
pretty: false,
json_rendered: super::HumanReadableErrorType::Default(super::ColorConfig::Never),
};
// Reference // Reference
v1.search_paths v1.search_paths
.push(SearchPath::from_cli_opt("native=abc", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("native=abc", JSON));
v1.search_paths v1.search_paths
.push(SearchPath::from_cli_opt("crate=def", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("crate=def", JSON));
v1.search_paths v1.search_paths
.push(SearchPath::from_cli_opt("dependency=ghi", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("dependency=ghi", JSON));
v1.search_paths v1.search_paths
.push(SearchPath::from_cli_opt("framework=jkl", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("framework=jkl", JSON));
v1.search_paths v1.search_paths
.push(SearchPath::from_cli_opt("all=mno", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("all=mno", JSON));
v2.search_paths v2.search_paths
.push(SearchPath::from_cli_opt("native=abc", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("native=abc", JSON));
v2.search_paths v2.search_paths
.push(SearchPath::from_cli_opt("dependency=ghi", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("dependency=ghi", JSON));
v2.search_paths v2.search_paths
.push(SearchPath::from_cli_opt("crate=def", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("crate=def", JSON));
v2.search_paths v2.search_paths
.push(SearchPath::from_cli_opt("framework=jkl", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("framework=jkl", JSON));
v2.search_paths v2.search_paths
.push(SearchPath::from_cli_opt("all=mno", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("all=mno", JSON));
v3.search_paths v3.search_paths
.push(SearchPath::from_cli_opt("crate=def", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("crate=def", JSON));
v3.search_paths v3.search_paths
.push(SearchPath::from_cli_opt("framework=jkl", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("framework=jkl", JSON));
v3.search_paths v3.search_paths
.push(SearchPath::from_cli_opt("native=abc", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("native=abc", JSON));
v3.search_paths v3.search_paths
.push(SearchPath::from_cli_opt("dependency=ghi", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("dependency=ghi", JSON));
v3.search_paths v3.search_paths
.push(SearchPath::from_cli_opt("all=mno", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("all=mno", JSON));
v4.search_paths v4.search_paths
.push(SearchPath::from_cli_opt("all=mno", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("all=mno", JSON));
v4.search_paths v4.search_paths
.push(SearchPath::from_cli_opt("native=abc", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("native=abc", JSON));
v4.search_paths v4.search_paths
.push(SearchPath::from_cli_opt("crate=def", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("crate=def", JSON));
v4.search_paths v4.search_paths
.push(SearchPath::from_cli_opt("dependency=ghi", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("dependency=ghi", JSON));
v4.search_paths v4.search_paths
.push(SearchPath::from_cli_opt("framework=jkl", super::ErrorOutputType::Json(false))); .push(SearchPath::from_cli_opt("framework=jkl", JSON));
assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash());
assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash());

View file

@ -1033,39 +1033,42 @@ fn default_emitter(
emitter_dest: Option<Box<dyn Write + Send>>, emitter_dest: Option<Box<dyn Write + Send>>,
) -> Box<dyn Emitter + sync::Send> { ) -> Box<dyn Emitter + sync::Send> {
match (sopts.error_format, emitter_dest) { match (sopts.error_format, emitter_dest) {
(config::ErrorOutputType::HumanReadable(color_config), None) => Box::new( (config::ErrorOutputType::HumanReadable(kind), dst) => {
EmitterWriter::stderr( let (short, color_config) = kind.unzip();
color_config, let emitter = match dst {
Some(source_map.clone()), None => EmitterWriter::stderr(
false, color_config,
sopts.debugging_opts.teach, Some(source_map.clone()),
).ui_testing(sopts.debugging_opts.ui_testing), short,
), sopts.debugging_opts.teach,
(config::ErrorOutputType::HumanReadable(_), Some(dst)) => Box::new( ),
EmitterWriter::new(dst, Some(source_map.clone()), false, false) Some(dst) => EmitterWriter::new(
.ui_testing(sopts.debugging_opts.ui_testing), dst,
), Some(source_map.clone()),
(config::ErrorOutputType::Json(pretty), None) => Box::new( short,
false, // no teach messages when writing to a buffer
false, // no colors when writing to a buffer
),
};
Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing))
},
(config::ErrorOutputType::Json { pretty, json_rendered }, None) => Box::new(
JsonEmitter::stderr( JsonEmitter::stderr(
Some(registry), Some(registry),
source_map.clone(), source_map.clone(),
pretty, pretty,
json_rendered,
).ui_testing(sopts.debugging_opts.ui_testing), ).ui_testing(sopts.debugging_opts.ui_testing),
), ),
(config::ErrorOutputType::Json(pretty), Some(dst)) => Box::new( (config::ErrorOutputType::Json { pretty, json_rendered }, Some(dst)) => Box::new(
JsonEmitter::new( JsonEmitter::new(
dst, dst,
Some(registry), Some(registry),
source_map.clone(), source_map.clone(),
pretty, pretty,
json_rendered,
).ui_testing(sopts.debugging_opts.ui_testing), ).ui_testing(sopts.debugging_opts.ui_testing),
), ),
(config::ErrorOutputType::Short(color_config), None) => Box::new(
EmitterWriter::stderr(color_config, Some(source_map.clone()), true, false),
),
(config::ErrorOutputType::Short(_), Some(dst)) => {
Box::new(EmitterWriter::new(dst, Some(source_map.clone()), true, false))
}
} }
} }
@ -1322,13 +1325,12 @@ pub enum IncrCompSession {
pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
let emitter: Box<dyn Emitter + sync::Send> = match output { let emitter: Box<dyn Emitter + sync::Send> = match output {
config::ErrorOutputType::HumanReadable(color_config) => { config::ErrorOutputType::HumanReadable(kind) => {
Box::new(EmitterWriter::stderr(color_config, None, false, false)) let (short, color_config) = kind.unzip();
} Box::new(EmitterWriter::stderr(color_config, None, short, false))
config::ErrorOutputType::Json(pretty) => Box::new(JsonEmitter::basic(pretty)),
config::ErrorOutputType::Short(color_config) => {
Box::new(EmitterWriter::stderr(color_config, None, true, false))
} }
config::ErrorOutputType::Json { pretty, json_rendered } =>
Box::new(JsonEmitter::basic(pretty, json_rendered)),
}; };
let handler = errors::Handler::with_emitter(true, None, emitter); let handler = errors::Handler::with_emitter(true, None, emitter);
handler.emit(&MultiSpan::new(), msg, errors::Level::Fatal); handler.emit(&MultiSpan::new(), msg, errors::Level::Fatal);
@ -1337,13 +1339,12 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
pub fn early_warn(output: config::ErrorOutputType, msg: &str) { pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
let emitter: Box<dyn Emitter + sync::Send> = match output { let emitter: Box<dyn Emitter + sync::Send> = match output {
config::ErrorOutputType::HumanReadable(color_config) => { config::ErrorOutputType::HumanReadable(kind) => {
Box::new(EmitterWriter::stderr(color_config, None, false, false)) let (short, color_config) = kind.unzip();
} Box::new(EmitterWriter::stderr(color_config, None, short, false))
config::ErrorOutputType::Json(pretty) => Box::new(JsonEmitter::basic(pretty)),
config::ErrorOutputType::Short(color_config) => {
Box::new(EmitterWriter::stderr(color_config, None, true, false))
} }
config::ErrorOutputType::Json { pretty, json_rendered } =>
Box::new(JsonEmitter::basic(pretty, json_rendered)),
}; };
let handler = errors::Handler::with_emitter(true, None, emitter); let handler = errors::Handler::with_emitter(true, None, emitter);
handler.emit(&MultiSpan::new(), msg, errors::Level::Warning); handler.emit(&MultiSpan::new(), msg, errors::Level::Warning);

View file

@ -16,9 +16,35 @@ use std::borrow::Cow;
use std::io::prelude::*; use std::io::prelude::*;
use std::io; use std::io;
use std::cmp::{min, Reverse}; use std::cmp::{min, Reverse};
use termcolor::{StandardStream, ColorChoice, ColorSpec, BufferWriter}; use termcolor::{StandardStream, ColorChoice, ColorSpec, BufferWriter, Ansi};
use termcolor::{WriteColor, Color, Buffer}; use termcolor::{WriteColor, Color, Buffer};
/// Describes the way the content of the `rendered` field of the json output is generated
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HumanReadableErrorType {
Default(ColorConfig),
Short(ColorConfig),
}
impl HumanReadableErrorType {
/// Returns a (`short`, `color`) tuple
pub fn unzip(self) -> (bool, ColorConfig) {
match self {
HumanReadableErrorType::Default(cc) => (false, cc),
HumanReadableErrorType::Short(cc) => (true, cc),
}
}
pub fn new_emitter(
self,
dst: Box<dyn Write + Send>,
source_map: Option<Lrc<SourceMapperDyn>>,
teach: bool,
) -> EmitterWriter {
let (short, color_config) = self.unzip();
EmitterWriter::new(dst, source_map, short, teach, color_config.suggests_using_colors())
}
}
const ANONYMIZED_LINE_NUM: &str = "LL"; const ANONYMIZED_LINE_NUM: &str = "LL";
/// Emitter trait for emitting errors. /// Emitter trait for emitting errors.
@ -104,8 +130,8 @@ pub enum ColorConfig {
} }
impl ColorConfig { impl ColorConfig {
fn to_color_choice(&self) -> ColorChoice { fn to_color_choice(self) -> ColorChoice {
match *self { match self {
ColorConfig::Always => { ColorConfig::Always => {
if atty::is(atty::Stream::Stderr) { if atty::is(atty::Stream::Stderr) {
ColorChoice::Always ColorChoice::Always
@ -120,6 +146,14 @@ impl ColorConfig {
ColorConfig::Auto => ColorChoice::Never, ColorConfig::Auto => ColorChoice::Never,
} }
} }
fn suggests_using_colors(self) -> bool {
match self {
| ColorConfig::Always
| ColorConfig::Auto
=> true,
ColorConfig::Never => false,
}
}
} }
pub struct EmitterWriter { pub struct EmitterWriter {
@ -152,13 +186,15 @@ impl EmitterWriter {
} }
} }
pub fn new(dst: Box<dyn Write + Send>, pub fn new(
source_map: Option<Lrc<SourceMapperDyn>>, dst: Box<dyn Write + Send>,
short_message: bool, source_map: Option<Lrc<SourceMapperDyn>>,
teach: bool) short_message: bool,
-> EmitterWriter { teach: bool,
colored: bool,
) -> EmitterWriter {
EmitterWriter { EmitterWriter {
dst: Raw(dst), dst: Raw(dst, colored),
sm: source_map, sm: source_map,
short_message, short_message,
teach, teach,
@ -1538,13 +1574,15 @@ fn emit_to_destination(rendered_buffer: &[Vec<StyledString>],
pub enum Destination { pub enum Destination {
Terminal(StandardStream), Terminal(StandardStream),
Buffered(BufferWriter), Buffered(BufferWriter),
Raw(Box<dyn Write + Send>), // The bool denotes whether we should be emitting ansi color codes or not
Raw(Box<(dyn Write + Send)>, bool),
} }
pub enum WritableDst<'a> { pub enum WritableDst<'a> {
Terminal(&'a mut StandardStream), Terminal(&'a mut StandardStream),
Buffered(&'a mut BufferWriter, Buffer), Buffered(&'a mut BufferWriter, Buffer),
Raw(&'a mut Box<dyn Write + Send>), Raw(&'a mut (dyn Write + Send)),
ColoredRaw(Ansi<&'a mut (dyn Write + Send)>),
} }
impl Destination { impl Destination {
@ -1570,7 +1608,8 @@ impl Destination {
let buf = t.buffer(); let buf = t.buffer();
WritableDst::Buffered(t, buf) WritableDst::Buffered(t, buf)
} }
Destination::Raw(ref mut t) => WritableDst::Raw(t), Destination::Raw(ref mut t, false) => WritableDst::Raw(t),
Destination::Raw(ref mut t, true) => WritableDst::ColoredRaw(Ansi::new(t)),
} }
} }
} }
@ -1628,6 +1667,7 @@ impl<'a> WritableDst<'a> {
match *self { match *self {
WritableDst::Terminal(ref mut t) => t.set_color(color), WritableDst::Terminal(ref mut t) => t.set_color(color),
WritableDst::Buffered(_, ref mut t) => t.set_color(color), WritableDst::Buffered(_, ref mut t) => t.set_color(color),
WritableDst::ColoredRaw(ref mut t) => t.set_color(color),
WritableDst::Raw(_) => Ok(()) WritableDst::Raw(_) => Ok(())
} }
} }
@ -1636,6 +1676,7 @@ impl<'a> WritableDst<'a> {
match *self { match *self {
WritableDst::Terminal(ref mut t) => t.reset(), WritableDst::Terminal(ref mut t) => t.reset(),
WritableDst::Buffered(_, ref mut t) => t.reset(), WritableDst::Buffered(_, ref mut t) => t.reset(),
WritableDst::ColoredRaw(ref mut t) => t.reset(),
WritableDst::Raw(_) => Ok(()), WritableDst::Raw(_) => Ok(()),
} }
} }
@ -1647,6 +1688,7 @@ impl<'a> Write for WritableDst<'a> {
WritableDst::Terminal(ref mut t) => t.write(bytes), WritableDst::Terminal(ref mut t) => t.write(bytes),
WritableDst::Buffered(_, ref mut buf) => buf.write(bytes), WritableDst::Buffered(_, ref mut buf) => buf.write(bytes),
WritableDst::Raw(ref mut w) => w.write(bytes), WritableDst::Raw(ref mut w) => w.write(bytes),
WritableDst::ColoredRaw(ref mut t) => t.write(bytes),
} }
} }
@ -1655,6 +1697,7 @@ impl<'a> Write for WritableDst<'a> {
WritableDst::Terminal(ref mut t) => t.flush(), WritableDst::Terminal(ref mut t) => t.flush(),
WritableDst::Buffered(_, ref mut buf) => buf.flush(), WritableDst::Buffered(_, ref mut buf) => buf.flush(),
WritableDst::Raw(ref mut w) => w.flush(), WritableDst::Raw(ref mut w) => w.flush(),
WritableDst::ColoredRaw(ref mut w) => w.flush(),
} }
} }
} }

View file

@ -3,7 +3,7 @@ use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use errors; use errors;
use errors::emitter::ColorConfig; use errors::emitter::{ColorConfig, HumanReadableErrorType};
use getopts; use getopts;
use rustc::lint::Level; use rustc::lint::Level;
use rustc::session::early_error; use rustc::session::early_error;
@ -254,12 +254,19 @@ impl Options {
(instead was `{}`)", arg)); (instead was `{}`)", arg));
} }
}; };
// FIXME: deduplicate this code from the identical code in librustc/session/config.rs
let error_format = match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { let error_format = match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
Some("human") => ErrorOutputType::HumanReadable(color), None |
Some("json") => ErrorOutputType::Json(false), Some("human") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)),
Some("pretty-json") => ErrorOutputType::Json(true), Some("json") => ErrorOutputType::Json {
Some("short") => ErrorOutputType::Short(color), pretty: false,
None => ErrorOutputType::HumanReadable(color), json_rendered: HumanReadableErrorType::Default(ColorConfig::Never),
},
Some("pretty-json") => ErrorOutputType::Json {
pretty: true,
json_rendered: HumanReadableErrorType::Default(ColorConfig::Never),
},
Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)),
Some(arg) => { Some(arg) => {
early_error(ErrorOutputType::default(), early_error(ErrorOutputType::default(),
&format!("argument for --error-format must be `human`, `json` or \ &format!("argument for --error-format must be `human`, `json` or \

View file

@ -299,15 +299,18 @@ pub fn new_handler(error_format: ErrorOutputType,
// stick to the defaults // stick to the defaults
let sessopts = Options::default(); let sessopts = Options::default();
let emitter: Box<dyn Emitter + sync::Send> = match error_format { let emitter: Box<dyn Emitter + sync::Send> = match error_format {
ErrorOutputType::HumanReadable(color_config) => Box::new( ErrorOutputType::HumanReadable(kind) => {
EmitterWriter::stderr( let (short, color_config) = kind.unzip();
color_config, Box::new(
source_map.map(|cm| cm as _), EmitterWriter::stderr(
false, color_config,
sessopts.debugging_opts.teach, source_map.map(|cm| cm as _),
).ui_testing(ui_testing) short,
), sessopts.debugging_opts.teach,
ErrorOutputType::Json(pretty) => { ).ui_testing(ui_testing)
)
},
ErrorOutputType::Json { pretty, json_rendered } => {
let source_map = source_map.unwrap_or_else( let source_map = source_map.unwrap_or_else(
|| Lrc::new(source_map::SourceMap::new(sessopts.file_path_mapping()))); || Lrc::new(source_map::SourceMap::new(sessopts.file_path_mapping())));
Box::new( Box::new(
@ -315,16 +318,10 @@ pub fn new_handler(error_format: ErrorOutputType,
None, None,
source_map, source_map,
pretty, pretty,
json_rendered,
).ui_testing(ui_testing) ).ui_testing(ui_testing)
) )
}, },
ErrorOutputType::Short(color_config) => Box::new(
EmitterWriter::stderr(
color_config,
source_map.map(|cm| cm as _),
true,
false)
),
}; };
errors::Handler::with_emitter_and_flags( errors::Handler::with_emitter_and_flags(

View file

@ -381,7 +381,7 @@ pub fn make_test(s: &str,
// Any errors in parsing should also appear when the doctest is compiled for real, so just // Any errors in parsing should also appear when the doctest is compiled for real, so just
// send all the errors that libsyntax emits directly into a `Sink` instead of stderr. // send all the errors that libsyntax emits directly into a `Sink` instead of stderr.
let cm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let emitter = EmitterWriter::new(box io::sink(), None, false, false); let emitter = EmitterWriter::new(box io::sink(), None, false, false, false);
let handler = Handler::with_emitter(false, None, box emitter); let handler = Handler::with_emitter(false, None, box emitter);
let sess = ParseSess::with_span_handler(handler, cm); let sess = ParseSess::with_span_handler(handler, cm);

View file

@ -14,7 +14,7 @@ use crate::source_map::{SourceMap, FilePathMapping};
use errors::registry::Registry; use errors::registry::Registry;
use errors::{DiagnosticBuilder, SubDiagnostic, CodeSuggestion, SourceMapper}; use errors::{DiagnosticBuilder, SubDiagnostic, CodeSuggestion, SourceMapper};
use errors::{DiagnosticId, Applicability}; use errors::{DiagnosticId, Applicability};
use errors::emitter::{Emitter, EmitterWriter}; use errors::emitter::{Emitter, HumanReadableErrorType};
use syntax_pos::{MacroBacktrace, Span, SpanLabel, MultiSpan}; use syntax_pos::{MacroBacktrace, Span, SpanLabel, MultiSpan};
use rustc_data_structures::sync::{self, Lrc}; use rustc_data_structures::sync::{self, Lrc};
@ -30,37 +30,46 @@ pub struct JsonEmitter {
sm: Lrc<dyn SourceMapper + sync::Send + sync::Sync>, sm: Lrc<dyn SourceMapper + sync::Send + sync::Sync>,
pretty: bool, pretty: bool,
ui_testing: bool, ui_testing: bool,
json_rendered: HumanReadableErrorType,
} }
impl JsonEmitter { impl JsonEmitter {
pub fn stderr(registry: Option<Registry>, pub fn stderr(
source_map: Lrc<SourceMap>, registry: Option<Registry>,
pretty: bool) -> JsonEmitter { source_map: Lrc<SourceMap>,
pretty: bool,
json_rendered: HumanReadableErrorType,
) -> JsonEmitter {
JsonEmitter { JsonEmitter {
dst: Box::new(io::stderr()), dst: Box::new(io::stderr()),
registry, registry,
sm: source_map, sm: source_map,
pretty, pretty,
ui_testing: false, ui_testing: false,
json_rendered,
} }
} }
pub fn basic(pretty: bool) -> JsonEmitter { pub fn basic(pretty: bool, json_rendered: HumanReadableErrorType) -> JsonEmitter {
let file_path_mapping = FilePathMapping::empty(); let file_path_mapping = FilePathMapping::empty();
JsonEmitter::stderr(None, Lrc::new(SourceMap::new(file_path_mapping)), JsonEmitter::stderr(None, Lrc::new(SourceMap::new(file_path_mapping)),
pretty) pretty, json_rendered)
} }
pub fn new(dst: Box<dyn Write + Send>, pub fn new(
registry: Option<Registry>, dst: Box<dyn Write + Send>,
source_map: Lrc<SourceMap>, registry: Option<Registry>,
pretty: bool) -> JsonEmitter { source_map: Lrc<SourceMap>,
pretty: bool,
json_rendered: HumanReadableErrorType,
) -> JsonEmitter {
JsonEmitter { JsonEmitter {
dst, dst,
registry, registry,
sm: source_map, sm: source_map,
pretty, pretty,
ui_testing: false, ui_testing: false,
json_rendered,
} }
} }
@ -190,7 +199,7 @@ impl Diagnostic {
} }
let buf = BufWriter::default(); let buf = BufWriter::default();
let output = buf.clone(); let output = buf.clone();
EmitterWriter::new(Box::new(buf), Some(je.sm.clone()), false, false) je.json_rendered.new_emitter(Box::new(buf), Some(je.sm.clone()), false)
.ui_testing(je.ui_testing).emit(db); .ui_testing(je.ui_testing).emit(db);
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
let output = String::from_utf8(output).unwrap(); let output = String::from_utf8(output).unwrap();

View file

@ -1920,6 +1920,7 @@ mod tests {
let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()),
Some(sm.clone()), Some(sm.clone()),
false, false,
false,
false); false);
ParseSess { ParseSess {
span_diagnostic: errors::Handler::with_emitter(true, None, Box::new(emitter)), span_diagnostic: errors::Handler::with_emitter(true, None, Box::new(emitter)),

View file

@ -57,6 +57,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }), let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }),
Some(source_map.clone()), Some(source_map.clone()),
false, false,
false,
false); false);
let handler = Handler::with_emitter(true, None, Box::new(emitter)); let handler = Handler::with_emitter(true, None, Box::new(emitter));
handler.span_err(msp, "foo"); handler.span_err(msp, "foo");

View file

@ -1,5 +1,6 @@
// ignore-cloudabi // ignore-cloudabi
// compile-flags: --error-format pretty-json -Zunstable-options // ignore-windows
// compile-flags: --error-format pretty-json -Zunstable-options --json-rendered=termcolor
// The output for humans should just highlight the whole span without showing // The output for humans should just highlight the whole span without showing
// the suggested replacement, but we also want to test that suggested // the suggested replacement, but we also want to test that suggested

View file

@ -73,10 +73,10 @@ mod foo {
"spans": [ "spans": [
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 440, "byte_start": 484,
"byte_end": 444, "byte_end": 488,
"line_start": 11, "line_start": 12,
"line_end": 11, "line_end": 12,
"column_start": 12, "column_start": 12,
"column_end": 16, "column_end": 16,
"is_primary": true, "is_primary": true,
@ -101,10 +101,10 @@ mod foo {
"spans": [ "spans": [
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -124,10 +124,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -147,10 +147,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -170,10 +170,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -193,10 +193,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -216,10 +216,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -239,10 +239,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -262,10 +262,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -285,10 +285,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -308,10 +308,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -331,10 +331,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -354,10 +354,10 @@ mod foo {
}, },
{ {
"file_name": "$DIR/use_suggestion_json.rs", "file_name": "$DIR/use_suggestion_json.rs",
"byte_start": 417, "byte_start": 461,
"byte_end": 417, "byte_end": 461,
"line_start": 10, "line_start": 11,
"line_end": 10, "line_end": 11,
"column_start": 1, "column_start": 1,
"column_end": 1, "column_end": 1,
"is_primary": true, "is_primary": true,
@ -380,22 +380,22 @@ mod foo {
"rendered": null "rendered": null
} }
], ],
"rendered": "error[E0412]: cannot find type `Iter` in this scope "rendered": "\u001b[0m\u001b[1m\u001b[38;5;9merror[E0412]\u001b[0m\u001b[0m\u001b[1m: cannot find type `Iter` in this scope\u001b[0m
--> $DIR/use_suggestion_json.rs:11:12 \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0m$DIR/use_suggestion_json.rs:12:12\u001b[0m
| \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m
LL | let x: Iter; \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m let x: Iter;\u001b[0m
| ^^^^ not found in this scope \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m
help: possible candidates are found in other modules, you can import them into scope \u001b[0m\u001b[1m\u001b[38;5;14mhelp\u001b[0m\u001b[0m: possible candidates are found in other modules, you can import them into scope\u001b[0m
| \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m
LL | use std::collections::binary_heap::Iter; \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0muse std::collections::binary_heap::Iter;\u001b[0m
| \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m
LL | use std::collections::btree_map::Iter; \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0muse std::collections::btree_map::Iter;\u001b[0m
| \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m
LL | use std::collections::btree_set::Iter; \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0muse std::collections::btree_set::Iter;\u001b[0m
| \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m
LL | use std::collections::hash_map::Iter; \u001b[0m\u001b[1m\u001b[38;5;12mLL\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0muse std::collections::hash_map::Iter;\u001b[0m
| \u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m
and 8 other candidates \u001b[0mand 8 other candidates\u001b[0m
" "
} }
@ -405,7 +405,7 @@ and 8 other candidates
"level": "error", "level": "error",
"spans": [], "spans": [],
"children": [], "children": [],
"rendered": "error: aborting due to previous error "rendered": "\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: aborting due to previous error\u001b[0m
" "
} }
@ -415,6 +415,6 @@ and 8 other candidates
"level": "", "level": "",
"spans": [], "spans": [],
"children": [], "children": [],
"rendered": "For more information about this error, try `rustc --explain E0412`. "rendered": "\u001b[0m\u001b[1mFor more information about this error, try `rustc --explain E0412`.\u001b[0m
" "
} }

View file

@ -62,7 +62,7 @@ struct DiagnosticCode {
explanation: Option<String>, explanation: Option<String>,
} }
pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String { pub fn extract_rendered(output: &str) -> String {
output output
.lines() .lines()
.filter_map(|line| { .filter_map(|line| {
@ -70,11 +70,12 @@ pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String {
match serde_json::from_str::<Diagnostic>(line) { match serde_json::from_str::<Diagnostic>(line) {
Ok(diagnostic) => diagnostic.rendered, Ok(diagnostic) => diagnostic.rendered,
Err(error) => { Err(error) => {
proc_res.fatal(Some(&format!( print!(
"failed to decode compiler output as json: \ "failed to decode compiler output as json: \
`{}`\nline: {}\noutput: {}", `{}`\nline: {}\noutput: {}",
error, line, output error, line, output
))); );
panic!()
} }
} }
} else { } else {

View file

@ -2090,50 +2090,10 @@ impl<'test> TestCx<'test> {
} }
fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! { fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! {
self.try_print_open_handles();
self.error(err); self.error(err);
proc_res.fatal(None); proc_res.fatal(None);
} }
// This function is a poor man's attempt to debug rust-lang/rust#38620, if
// that's closed then this should be deleted
//
// This is a very "opportunistic" debugging attempt, so we ignore all
// errors here.
fn try_print_open_handles(&self) {
if !cfg!(windows) {
return;
}
if self.config.mode != Incremental {
return;
}
let filename = match self.testpaths.file.file_stem() {
Some(path) => path,
None => return,
};
let mut cmd = Command::new("handle.exe");
cmd.arg("-a").arg("-u");
cmd.arg(filename);
cmd.arg("-nobanner");
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::piped());
let output = match cmd.spawn().and_then(read2_abbreviated) {
Ok(output) => output,
Err(_) => return,
};
println!("---------------------------------------------------");
println!("ran extra command to debug rust-lang/rust#38620: ");
println!("{:?}", cmd);
println!("result: {}", output.status);
println!("--- stdout ----------------------------------------");
println!("{}", String::from_utf8_lossy(&output.stdout));
println!("--- stderr ----------------------------------------");
println!("{}", String::from_utf8_lossy(&output.stderr));
println!("---------------------------------------------------");
}
// codegen tests (using FileCheck) // codegen tests (using FileCheck)
fn compile_test_and_save_ir(&self) -> ProcRes { fn compile_test_and_save_ir(&self) -> ProcRes {
@ -2844,7 +2804,7 @@ impl<'test> TestCx<'test> {
let stderr = if explicit { let stderr = if explicit {
proc_res.stderr.clone() proc_res.stderr.clone()
} else { } else {
json::extract_rendered(&proc_res.stderr, &proc_res) json::extract_rendered(&proc_res.stderr)
}; };
let normalized_stderr = self.normalize_output(&stderr, &self.props.normalize_stderr); let normalized_stderr = self.normalize_output(&stderr, &self.props.normalize_stderr);
@ -3464,7 +3424,9 @@ impl ProcRes {
{}\n\ {}\n\
------------------------------------------\n\ ------------------------------------------\n\
\n", \n",
self.status, self.cmdline, self.stdout, self.stderr self.status, self.cmdline,
json::extract_rendered(&self.stdout),
json::extract_rendered(&self.stderr),
); );
// Use resume_unwind instead of panic!() to prevent a panic message + backtrace from // Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
// compiletest, which is unnecessary noise. // compiletest, which is unnecessary noise.