From 96404ee8443c7475b3b6a7b9beb142e7e9354673 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 12 Mar 2019 13:06:43 +0100 Subject: [PATCH] Emit ansi color codes in the `rendered` field of json diagnostics --- src/librustc/session/config.rs | 35 ++++++-- src/librustc/session/mod.rs | 21 +++-- src/librustc_errors/emitter.rs | 28 ++++--- src/libsyntax/json.rs | 29 ++++--- src/test/ui/lint/use_suggestion_json.rs | 2 +- src/test/ui/lint/use_suggestion_json.stderr | 88 ++++++++++----------- 6 files changed, 123 insertions(+), 80 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 988e4a9ff23..7dcccdb74b4 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -205,7 +205,12 @@ impl OutputType { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ErrorOutputType { HumanReadable(ColorConfig), - Json(bool), + Json { + /// Render the json in a human readable way (with indents and newlines) + pretty: bool, + /// The `rendered` field with the command line diagnostics include color codes + colorful_rendered: bool, + }, Short(ColorConfig), } @@ -1345,6 +1350,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print some statistics about AST and HIR"), always_encode_mir: bool = (false, parse_bool, [TRACKED], "encode MIR of all functions into the crate metadata"), + colorful_json: bool = (false, parse_bool, [UNTRACKED], + "encode color codes in the `rendered` field of json diagnostics"), unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], "take the breaks off const evaluation. NOTE: this is unsound"), osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], @@ -1798,6 +1805,12 @@ pub fn rustc_optgroups() -> Vec { "How errors and other messages are produced", "human|json|short", ), + opt::opt( + "", + "colorful-json", + "Emit ansi color codes to the `rendered` field of json diagnostics", + "TYPE", + ), opt::opt_s( "", "color", @@ -1938,6 +1951,7 @@ pub fn build_session_options_and_crate_config( ) } + let colorful_rendered = matches.opt_present("colorful-json"); // 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 @@ -1946,8 +1960,8 @@ pub fn build_session_options_and_crate_config( 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(false), - Some("pretty-json") => ErrorOutputType::Json(true), + Some("json") => ErrorOutputType::Json { pretty: false, colorful_rendered }, + Some("pretty-json") => ErrorOutputType::Json { pretty: true, colorful_rendered }, Some("short") => ErrorOutputType::Short(color), None => ErrorOutputType::HumanReadable(color), @@ -1973,11 +1987,16 @@ pub fn build_session_options_and_crate_config( let mut debugging_opts = build_debugging_options(matches, error_format); - if !debugging_opts.unstable_options && error_format == ErrorOutputType::Json(true) { - early_error( - ErrorOutputType::Json(false), - "--error-format=pretty-json is unstable", - ); + if !debugging_opts.unstable_options { + if colorful_rendered { + early_error(error_format, "--colorful-json=true is unstable"); + } + if let ErrorOutputType::Json { pretty: true, .. } = error_format { + early_error( + ErrorOutputType::Json { pretty: false, colorful_rendered: false }, + "--error-format=pretty-json is unstable", + ); + } } if debugging_opts.pgo_gen.is_some() && !debugging_opts.pgo_use.is_empty() { diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 2f3df32945c..88eee1cd306 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -1046,29 +1046,34 @@ fn default_emitter( ).ui_testing(sopts.debugging_opts.ui_testing), ), (config::ErrorOutputType::HumanReadable(_), Some(dst)) => Box::new( - EmitterWriter::new(dst, Some(source_map.clone()), false, false) - .ui_testing(sopts.debugging_opts.ui_testing), + EmitterWriter::new( + dst, Some(source_map.clone()), false, false, sopts.debugging_opts.colorful_json, + ).ui_testing(sopts.debugging_opts.ui_testing), ), - (config::ErrorOutputType::Json(pretty), None) => Box::new( + (config::ErrorOutputType::Json { pretty, colorful_rendered }, None) => Box::new( JsonEmitter::stderr( Some(registry), source_map.clone(), pretty, + colorful_rendered, ).ui_testing(sopts.debugging_opts.ui_testing), ), - (config::ErrorOutputType::Json(pretty), Some(dst)) => Box::new( + (config::ErrorOutputType::Json { pretty, colorful_rendered }, Some(dst)) => Box::new( JsonEmitter::new( dst, Some(registry), source_map.clone(), pretty, + colorful_rendered, ).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)) + Box::new(EmitterWriter::new( + dst, Some(source_map.clone()), true, false, sopts.debugging_opts.colorful_json, + )) } } } @@ -1317,7 +1322,8 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { config::ErrorOutputType::HumanReadable(color_config) => { Box::new(EmitterWriter::stderr(color_config, None, false, false)) } - config::ErrorOutputType::Json(pretty) => Box::new(JsonEmitter::basic(pretty)), + config::ErrorOutputType::Json { pretty, colorful_rendered } => + Box::new(JsonEmitter::basic(pretty, colorful_rendered)), config::ErrorOutputType::Short(color_config) => { Box::new(EmitterWriter::stderr(color_config, None, true, false)) } @@ -1332,7 +1338,8 @@ pub fn early_warn(output: config::ErrorOutputType, msg: &str) { config::ErrorOutputType::HumanReadable(color_config) => { Box::new(EmitterWriter::stderr(color_config, None, false, false)) } - config::ErrorOutputType::Json(pretty) => Box::new(JsonEmitter::basic(pretty)), + config::ErrorOutputType::Json { pretty, colorful_rendered } => + Box::new(JsonEmitter::basic(pretty, colorful_rendered)), config::ErrorOutputType::Short(color_config) => { Box::new(EmitterWriter::stderr(color_config, None, true, false)) } diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index ee2a1b69cbd..0790f9bc761 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -16,7 +16,7 @@ use std::borrow::Cow; use std::io::prelude::*; use std::io; use std::cmp::{min, Reverse}; -use termcolor::{StandardStream, ColorChoice, ColorSpec, BufferWriter}; +use termcolor::{StandardStream, ColorChoice, ColorSpec, BufferWriter, Ansi}; use termcolor::{WriteColor, Color, Buffer}; const ANONYMIZED_LINE_NUM: &str = "LL"; @@ -152,13 +152,15 @@ impl EmitterWriter { } } - pub fn new(dst: Box, - source_map: Option>, - short_message: bool, - teach: bool) - -> EmitterWriter { + pub fn new( + dst: Box, + source_map: Option>, + short_message: bool, + teach: bool, + colored: bool, + ) -> EmitterWriter { EmitterWriter { - dst: Raw(dst), + dst: Raw(dst, colored), sm: source_map, short_message, teach, @@ -1538,13 +1540,14 @@ fn emit_to_destination(rendered_buffer: &[Vec], pub enum Destination { Terminal(StandardStream), Buffered(BufferWriter), - Raw(Box), + Raw(Box<(dyn Write + Send)>, bool), } pub enum WritableDst<'a> { Terminal(&'a mut StandardStream), Buffered(&'a mut BufferWriter, Buffer), - Raw(&'a mut Box), + Raw(&'a mut (dyn Write + Send)), + ColoredRaw(Ansi<&'a mut (dyn Write + Send)>), } impl Destination { @@ -1570,7 +1573,8 @@ impl Destination { let buf = t.buffer(); 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 +1632,7 @@ impl<'a> WritableDst<'a> { match *self { WritableDst::Terminal(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(()) } } @@ -1636,6 +1641,7 @@ impl<'a> WritableDst<'a> { match *self { WritableDst::Terminal(ref mut t) => t.reset(), WritableDst::Buffered(_, ref mut t) => t.reset(), + WritableDst::ColoredRaw(ref mut t) => t.reset(), WritableDst::Raw(_) => Ok(()), } } @@ -1647,6 +1653,7 @@ impl<'a> Write for WritableDst<'a> { WritableDst::Terminal(ref mut t) => t.write(bytes), WritableDst::Buffered(_, ref mut buf) => buf.write(bytes), WritableDst::Raw(ref mut w) => w.write(bytes), + WritableDst::ColoredRaw(ref mut t) => t.write(bytes), } } @@ -1655,6 +1662,7 @@ impl<'a> Write for WritableDst<'a> { WritableDst::Terminal(ref mut t) => t.flush(), WritableDst::Buffered(_, ref mut buf) => buf.flush(), WritableDst::Raw(ref mut w) => w.flush(), + WritableDst::ColoredRaw(ref mut w) => w.flush(), } } } diff --git a/src/libsyntax/json.rs b/src/libsyntax/json.rs index 9acd0d099a0..55005a821b2 100644 --- a/src/libsyntax/json.rs +++ b/src/libsyntax/json.rs @@ -30,37 +30,46 @@ pub struct JsonEmitter { sm: Lrc, pretty: bool, ui_testing: bool, + colorful_rendered: bool, } impl JsonEmitter { - pub fn stderr(registry: Option, - source_map: Lrc, - pretty: bool) -> JsonEmitter { + pub fn stderr( + registry: Option, + source_map: Lrc, + pretty: bool, + colorful_rendered: bool, + ) -> JsonEmitter { JsonEmitter { dst: Box::new(io::stderr()), registry, sm: source_map, pretty, ui_testing: false, + colorful_rendered, } } - pub fn basic(pretty: bool) -> JsonEmitter { + pub fn basic(pretty: bool, colorful_rendered: bool) -> JsonEmitter { let file_path_mapping = FilePathMapping::empty(); JsonEmitter::stderr(None, Lrc::new(SourceMap::new(file_path_mapping)), - pretty) + pretty, colorful_rendered) } - pub fn new(dst: Box, - registry: Option, - source_map: Lrc, - pretty: bool) -> JsonEmitter { + pub fn new( + dst: Box, + registry: Option, + source_map: Lrc, + pretty: bool, + colorful_rendered: bool, + ) -> JsonEmitter { JsonEmitter { dst, registry, sm: source_map, pretty, ui_testing: false, + colorful_rendered, } } @@ -190,7 +199,7 @@ impl Diagnostic { } let buf = BufWriter::default(); let output = buf.clone(); - EmitterWriter::new(Box::new(buf), Some(je.sm.clone()), false, false) + EmitterWriter::new(Box::new(buf), Some(je.sm.clone()), false, false, je.colorful_rendered) .ui_testing(je.ui_testing).emit(db); let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); let output = String::from_utf8(output).unwrap(); diff --git a/src/test/ui/lint/use_suggestion_json.rs b/src/test/ui/lint/use_suggestion_json.rs index 3bdbaa55f2d..9cf3d0fa403 100644 --- a/src/test/ui/lint/use_suggestion_json.rs +++ b/src/test/ui/lint/use_suggestion_json.rs @@ -1,5 +1,5 @@ // ignore-cloudabi -// compile-flags: --error-format pretty-json -Zunstable-options +// compile-flags: --error-format pretty-json -Zunstable-options --colorful-json=true // The output for humans should just highlight the whole span without showing // the suggested replacement, but we also want to test that suggested diff --git a/src/test/ui/lint/use_suggestion_json.stderr b/src/test/ui/lint/use_suggestion_json.stderr index dee7f2f9b16..789bb5ea72f 100644 --- a/src/test/ui/lint/use_suggestion_json.stderr +++ b/src/test/ui/lint/use_suggestion_json.stderr @@ -73,8 +73,8 @@ mod foo { "spans": [ { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 440, - "byte_end": 444, + "byte_start": 461, + "byte_end": 465, "line_start": 11, "line_end": 11, "column_start": 12, @@ -101,8 +101,8 @@ mod foo { "spans": [ { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -124,8 +124,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -147,8 +147,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -170,8 +170,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -193,8 +193,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -216,8 +216,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -239,8 +239,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -262,8 +262,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -285,8 +285,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -308,8 +308,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -331,8 +331,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -354,8 +354,8 @@ mod foo { }, { "file_name": "$DIR/use_suggestion_json.rs", - "byte_start": 417, - "byte_end": 417, + "byte_start": 438, + "byte_end": 438, "line_start": 10, "line_end": 10, "column_start": 1, @@ -380,22 +380,22 @@ mod foo { "rendered": null } ], - "rendered": "error[E0412]: cannot find type `Iter` in this scope - --> $DIR/use_suggestion_json.rs:11:12 - | -LL | let x: Iter; - | ^^^^ not found in this scope -help: possible candidates are found in other modules, you can import them into scope - | -LL | use std::collections::binary_heap::Iter; - | -LL | use std::collections::btree_map::Iter; - | -LL | use std::collections::btree_set::Iter; - | -LL | use std::collections::hash_map::Iter; - | -and 8 other candidates + "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 +/u001b[0m /u001b[0m/u001b[0m/u001b[1m/u001b[38;5;12m--> /u001b[0m/u001b[0m$DIR/use_suggestion_json.rs:11:12/u001b[0m +/u001b[0m /u001b[0m/u001b[0m/u001b[1m/u001b[38;5;12m|/u001b[0m +/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 +/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 +/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 +/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 +/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 +/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 +/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 +/u001b[0mand 8 other candidates/u001b[0m " } @@ -405,7 +405,7 @@ and 8 other candidates "level": "error", "spans": [], "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": "", "spans": [], "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 " }