Skip to content

Emit ansi color codes in the rendered field of json diagnostics #59128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 17, 2019
106 changes: 70 additions & 36 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use syntax::parse::token;
use syntax::parse;
use syntax::symbol::Symbol;
use syntax::feature_gate::UnstableFeatures;
use errors::emitter::HumanReadableErrorType;

use errors::{ColorConfig, FatalError, Handler};

Expand Down Expand Up @@ -204,14 +205,18 @@ impl OutputType {

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorOutputType {
HumanReadable(ColorConfig),
Json(bool),
Short(ColorConfig),
HumanReadable(HumanReadableErrorType),
Json {
/// 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 {
fn default() -> ErrorOutputType {
ErrorOutputType::HumanReadable(ColorConfig::Auto)
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto))
}
}

Expand Down Expand Up @@ -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"),
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],
"take the breaks off const evaluation. NOTE: this is unsound"),
osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
Expand Down Expand Up @@ -1798,6 +1805,12 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
"How errors and other messages are produced",
"human|json|short",
),
opt::opt(
"",
"json-rendered",
"Choose `rendered` field of json diagnostics render scheme",
"plain|termcolor",
),
opt::opt_s(
"",
"color",
Expand Down Expand Up @@ -1938,21 +1951,32 @@ 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
// with only stable options if no unstable options are used. Since error-format
// is unstable, it will not be present. We have to use opts_present not
// opt_present because the latter will panic.
let error_format = if matches.opts_present(&["error-format".to_owned()]) {
match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
Some("human") => ErrorOutputType::HumanReadable(color),
Some("json") => ErrorOutputType::Json(false),
Some("pretty-json") => ErrorOutputType::Json(true),
Some("short") => ErrorOutputType::Short(color),
None => ErrorOutputType::HumanReadable(color),
None |
Some("human") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)),
Some("json") => ErrorOutputType::Json { pretty: false, json_rendered },
Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered },
Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)),

Some(arg) => early_error(
ErrorOutputType::HumanReadable(color),
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)),
&format!(
"argument for --error-format must be `human`, `json` or \
`short` (instead was `{}`)",
Expand All @@ -1961,7 +1985,7 @@ pub fn build_session_options_and_crate_config(
),
}
} else {
ErrorOutputType::HumanReadable(color)
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color))
};

let unparsed_crate_types = matches.opt_strs("crate-type");
Expand All @@ -1973,11 +1997,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 matches.opt_str("json-rendered").is_some() {
early_error(error_format, "`--json-rendered=x` 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.is_some() && !debugging_opts.pgo_use.is_empty() {
Expand Down Expand Up @@ -2881,50 +2910,55 @@ mod tests {
let mut v3 = 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
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
.push(SearchPath::from_cli_opt("crate=def", super::ErrorOutputType::Json(false)));
.push(SearchPath::from_cli_opt("crate=def", JSON));
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
.push(SearchPath::from_cli_opt("framework=jkl", super::ErrorOutputType::Json(false)));
.push(SearchPath::from_cli_opt("framework=jkl", JSON));
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
.push(SearchPath::from_cli_opt("native=abc", super::ErrorOutputType::Json(false)));
.push(SearchPath::from_cli_opt("native=abc", JSON));
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
.push(SearchPath::from_cli_opt("crate=def", super::ErrorOutputType::Json(false)));
.push(SearchPath::from_cli_opt("crate=def", JSON));
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
.push(SearchPath::from_cli_opt("all=mno", super::ErrorOutputType::Json(false)));
.push(SearchPath::from_cli_opt("all=mno", JSON));

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
.push(SearchPath::from_cli_opt("framework=jkl", super::ErrorOutputType::Json(false)));
.push(SearchPath::from_cli_opt("framework=jkl", JSON));
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
.push(SearchPath::from_cli_opt("dependency=ghi", super::ErrorOutputType::Json(false)));
.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
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
.push(SearchPath::from_cli_opt("all=mno", super::ErrorOutputType::Json(false)));
.push(SearchPath::from_cli_opt("all=mno", JSON));
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
.push(SearchPath::from_cli_opt("crate=def", super::ErrorOutputType::Json(false)));
.push(SearchPath::from_cli_opt("crate=def", JSON));
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
.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() == v3.dep_tracking_hash());
Expand Down
65 changes: 33 additions & 32 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1037,39 +1037,42 @@ fn default_emitter(
emitter_dest: Option<Box<dyn Write + Send>>,
) -> Box<dyn Emitter + sync::Send> {
match (sopts.error_format, emitter_dest) {
(config::ErrorOutputType::HumanReadable(color_config), None) => Box::new(
EmitterWriter::stderr(
color_config,
Some(source_map.clone()),
false,
sopts.debugging_opts.teach,
).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),
),
(config::ErrorOutputType::Json(pretty), None) => Box::new(
(config::ErrorOutputType::HumanReadable(kind), dst) => {
let (short, color_config) = kind.unzip();
let emitter = match dst {
None => EmitterWriter::stderr(
color_config,
Some(source_map.clone()),
short,
sopts.debugging_opts.teach,
),
Some(dst) => EmitterWriter::new(
dst,
Some(source_map.clone()),
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(
Some(registry),
source_map.clone(),
pretty,
json_rendered,
).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(
dst,
Some(registry),
source_map.clone(),
pretty,
json_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))
}
}
}

Expand Down Expand Up @@ -1314,13 +1317,12 @@ pub enum IncrCompSession {

pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
let emitter: Box<dyn Emitter + sync::Send> = match output {
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::Short(color_config) => {
Box::new(EmitterWriter::stderr(color_config, None, true, false))
config::ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip();
Box::new(EmitterWriter::stderr(color_config, None, short, false))
}
config::ErrorOutputType::Json { pretty, json_rendered } =>
Box::new(JsonEmitter::basic(pretty, json_rendered)),
};
let handler = errors::Handler::with_emitter(true, None, emitter);
handler.emit(&MultiSpan::new(), msg, errors::Level::Fatal);
Expand All @@ -1329,13 +1331,12 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {

pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
let emitter: Box<dyn Emitter + sync::Send> = match output {
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::Short(color_config) => {
Box::new(EmitterWriter::stderr(color_config, None, true, false))
config::ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip();
Box::new(EmitterWriter::stderr(color_config, None, short, false))
}
config::ErrorOutputType::Json { pretty, json_rendered } =>
Box::new(JsonEmitter::basic(pretty, json_rendered)),
};
let handler = errors::Handler::with_emitter(true, None, emitter);
handler.emit(&MultiSpan::new(), msg, errors::Level::Warning);
Expand Down
Loading