Skip to content

Commit

Permalink
Auto merge of #30711 - nrc:json-errs, r=huonw
Browse files Browse the repository at this point in the history
The compiler can emit errors and warning in JSON format. This is a more easily machine readable form then the usual error output.

Closes #10492, closes #14863.
  • Loading branch information
bors committed Jan 15, 2016
2 parents 2fb0c5e + 82f8e5c commit d8869d3
Show file tree
Hide file tree
Showing 21 changed files with 444 additions and 129 deletions.
9 changes: 5 additions & 4 deletions src/librustc/lint/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
142 changes: 88 additions & 54 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -71,6 +70,18 @@ pub enum OutputType {
DepInfo,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorOutputType {
HumanReadable(ColorConfig),
Json,
}

impl Default for ErrorOutputType {
fn default() -> ErrorOutputType {
ErrorOutputType::HumanReadable(ColorConfig::Auto)
}
}

impl OutputType {
fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
match *self {
Expand Down Expand Up @@ -124,14 +135,14 @@ pub struct Options {
pub test: bool,
pub parse_only: bool,
pub no_trans: bool,
pub error_format: ErrorOutputType,
pub treat_err_as_bug: bool,
pub incremental_compilation: bool,
pub dump_dep_graph: bool,
pub no_analysis: bool,
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,
Expand Down Expand Up @@ -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,
Expand All @@ -241,7 +252,7 @@ pub fn basic_options() -> Options {
debugging_opts: basic_debugging_options(),
prints: Vec::new(),
cg: basic_codegen_options(),
color: ColorConfig::Auto,
error_format: ErrorOutputType::default(),
externs: HashMap::new(),
crate_name: None,
alt_std_name: None,
Expand Down Expand Up @@ -308,7 +319,7 @@ macro_rules! options {
$struct_name { $($opt: $init),* }
}

pub fn $buildfn(matches: &getopts::Matches, color: ColorConfig) -> $struct_name
pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $struct_name
{
let mut op = $defaultfn();
for option in matches.opt_strs($prefix) {
Expand All @@ -322,20 +333,20 @@ macro_rules! options {
if !setter(&mut op, value) {
match (value, opt_type_desc) {
(Some(..), None) => {
early_error(color, &format!("{} option `{}` takes no \
value", $outputname, key))
early_error(error_format, &format!("{} option `{}` takes no \
value", $outputname, key))
}
(None, Some(type_desc)) => {
early_error(color, &format!("{0} option `{1}` requires \
{2} ({3} {1}=<value>)",
$outputname, key,
type_desc, $prefix))
early_error(error_format, &format!("{0} option `{1}` requires \
{2} ({3} {1}=<value>)",
$outputname, key,
type_desc, $prefix))
}
(Some(value), Some(type_desc)) => {
early_error(color, &format!("incorrect value `{}` for {} \
option `{}` - {} was expected",
value, $outputname,
key, type_desc))
early_error(error_format, &format!("incorrect value `{}` for {} \
option `{}` - {} was expected",
value, $outputname,
key, type_desc))
}
(None, None) => unreachable!()
}
Expand All @@ -344,8 +355,8 @@ macro_rules! options {
break;
}
if !found {
early_error(color, &format!("unknown {} option: `{}`",
$outputname, key));
early_error(error_format, &format!("unknown {} option: `{}`",
$outputname, key));
}
}
return op;
Expand Down Expand Up @@ -861,6 +872,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
"NAME=PATH"),
opt::opt("", "sysroot", "Override the system root", "PATH"),
opt::multi("Z", "", "Set internal debugging options", "FLAG"),
opt::opt_u("", "error-format", "How errors and other messages are produced", "human|json"),
opt::opt("", "color", "Configure coloring of output:
auto = colorize, if output goes to a tty (default);
always = always colorize output;
Expand Down Expand Up @@ -903,15 +915,37 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
None => ColorConfig::Auto,

Some(arg) => {
early_error(ColorConfig::Auto, &format!("argument for --color must be auto, always \
or never (instead was `{}`)",
arg))
early_error(ErrorOutputType::default(), &format!("argument for --color must be auto, \
always or never (instead was `{}`)",
arg))
}
};

// We need the opts_present check because the driver will send us Matches
// with only stable options if no unstable options are used. Since error-format
// is unstable, it will not be present. We have to use opts_present not
// opt_present because the latter will panic.
let error_format = if matches.opts_present(&["error-format".to_owned()]) {
match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
Some("human") => ErrorOutputType::HumanReadable(color),
Some("json") => ErrorOutputType::Json,

None => ErrorOutputType::default(),

Some(arg) => {
early_error(ErrorOutputType::default(), &format!("argument for --error-format must \
be human or json (instead was \
`{}`)",
arg))
}
}
} else {
ErrorOutputType::default()
};

let unparsed_crate_types = matches.opt_strs("crate-type");
let crate_types = parse_crate_types_from_list(unparsed_crate_types)
.unwrap_or_else(|e| early_error(color, &e[..]));
.unwrap_or_else(|e| early_error(error_format, &e[..]));

let mut lint_opts = vec!();
let mut describe_lints = false;
Expand All @@ -928,11 +962,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {

let lint_cap = matches.opt_str("cap-lints").map(|cap| {
lint::Level::from_str(&cap).unwrap_or_else(|| {
early_error(color, &format!("unknown lint level: `{}`", cap))
early_error(error_format, &format!("unknown lint level: `{}`", cap))
})
});

let debugging_opts = build_debugging_options(matches, color);
let debugging_opts = build_debugging_options(matches, error_format);

let parse_only = debugging_opts.parse_only;
let no_trans = debugging_opts.no_trans;
Expand All @@ -958,7 +992,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
"link" => OutputType::Exe,
"dep-info" => OutputType::DepInfo,
part => {
early_error(color, &format!("unknown emission type: `{}`",
early_error(error_format, &format!("unknown emission type: `{}`",
part))
}
};
Expand All @@ -971,7 +1005,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
output_types.insert(OutputType::Exe, None);
}

let mut cg = build_codegen_options(matches, color);
let mut cg = build_codegen_options(matches, error_format);

// Issue #30063: if user requests llvm-related output to one
// particular path, disable codegen-units.
Expand All @@ -983,11 +1017,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
}).collect();
if !incompatible.is_empty() {
for ot in &incompatible {
early_warn(color, &format!("--emit={} with -o incompatible with \
-C codegen-units=N for N > 1",
ot.shorthand()));
early_warn(error_format, &format!("--emit={} with -o incompatible with \
-C codegen-units=N for N > 1",
ot.shorthand()));
}
early_warn(color, "resetting to default -C codegen-units=1");
early_warn(error_format, "resetting to default -C codegen-units=1");
cg.codegen_units = 1;
}
}
Expand All @@ -1000,29 +1034,29 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let opt_level = {
if matches.opt_present("O") {
if cg.opt_level.is_some() {
early_error(color, "-O and -C opt-level both provided");
early_error(error_format, "-O and -C opt-level both provided");
}
Default
OptLevel::Default
} else {
match cg.opt_level {
None => No,
Some(0) => No,
Some(1) => Less,
Some(2) => Default,
Some(3) => Aggressive,
None => OptLevel::No,
Some(0) => OptLevel::No,
Some(1) => OptLevel::Less,
Some(2) => OptLevel::Default,
Some(3) => OptLevel::Aggressive,
Some(arg) => {
early_error(color, &format!("optimization level needs to be \
between 0-3 (instead was `{}`)",
arg));
early_error(error_format, &format!("optimization level needs to be \
between 0-3 (instead was `{}`)",
arg));
}
}
}
};
let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == No);
let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
let gc = debugging_opts.gc;
let debuginfo = if matches.opt_present("g") {
if cg.debuginfo.is_some() {
early_error(color, "-g and -C debuginfo both provided");
early_error(error_format, "-g and -C debuginfo both provided");
}
FullDebugInfo
} else {
Expand All @@ -1031,16 +1065,16 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
Some(1) => LimitedDebugInfo,
Some(2) => FullDebugInfo,
Some(arg) => {
early_error(color, &format!("debug info level needs to be between \
0-2 (instead was `{}`)",
arg));
early_error(error_format, &format!("debug info level needs to be between \
0-2 (instead was `{}`)",
arg));
}
}
};

let mut search_paths = SearchPaths::new();
for s in &matches.opt_strs("L") {
search_paths.add_path(&s[..], color);
search_paths.add_path(&s[..], error_format);
}

let libs = matches.opt_strs("l").into_iter().map(|s| {
Expand All @@ -1052,9 +1086,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
(Some(name), "framework") => (name, cstore::NativeFramework),
(Some(name), "static") => (name, cstore::NativeStatic),
(_, s) => {
early_error(color, &format!("unknown library kind `{}`, expected \
one of dylib, framework, or static",
s));
early_error(error_format, &format!("unknown library kind `{}`, expected \
one of dylib, framework, or static",
s));
}
};
(name.to_string(), kind)
Expand All @@ -1069,26 +1103,26 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
"file-names" => PrintRequest::FileNames,
"sysroot" => PrintRequest::Sysroot,
req => {
early_error(color, &format!("unknown print request `{}`", req))
early_error(error_format, &format!("unknown print request `{}`", req))
}
}
}).collect::<Vec<_>>();

if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
early_warn(color, "-C remark will not show source locations without \
--debuginfo");
early_warn(error_format, "-C remark will not show source locations without \
--debuginfo");
}

let mut externs = HashMap::new();
for arg in &matches.opt_strs("extern") {
let mut parts = arg.splitn(2, '=');
let name = match parts.next() {
Some(s) => s,
None => early_error(color, "--extern value must not be empty"),
None => early_error(error_format, "--extern value must not be empty"),
};
let location = match parts.next() {
Some(s) => s,
None => early_error(color, "--extern value must be of the format `foo=bar`"),
None => early_error(error_format, "--extern value must be of the format `foo=bar`"),
};

externs.entry(name.to_string()).or_insert(vec![]).push(location.to_string());
Expand Down Expand Up @@ -1119,7 +1153,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
debugging_opts: debugging_opts,
prints: prints,
cg: cg,
color: color,
error_format: error_format,
externs: externs,
crate_name: crate_name,
alt_std_name: None,
Expand Down
Loading

0 comments on commit d8869d3

Please sign in to comment.