Auto merge of #30711 - nrc:json-errs, r=huonw
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.
This commit is contained in:
commit
d8869d3487
21 changed files with 444 additions and 129 deletions
|
@ -28,7 +28,7 @@ use self::TargetLint::*;
|
||||||
use dep_graph::DepNode;
|
use dep_graph::DepNode;
|
||||||
use middle::privacy::AccessLevels;
|
use middle::privacy::AccessLevels;
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use session::{early_error, Session};
|
use session::{config, early_error, Session};
|
||||||
use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass};
|
use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass};
|
||||||
use lint::{EarlyLintPass, EarlyLintPassObject, LateLintPass, LateLintPassObject};
|
use lint::{EarlyLintPass, EarlyLintPassObject, LateLintPass, LateLintPassObject};
|
||||||
use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
|
use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
|
||||||
|
@ -37,11 +37,12 @@ use util::nodemap::FnvHashMap;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
use std::default::Default as StdDefault;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use syntax::ast_util::{self, IdVisitingOperation};
|
use syntax::ast_util::{self, IdVisitingOperation};
|
||||||
use syntax::attr::{self, AttrMetaMethods};
|
use syntax::attr::{self, AttrMetaMethods};
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::errors::{self, DiagnosticBuilder};
|
use syntax::errors::DiagnosticBuilder;
|
||||||
use syntax::parse::token::InternedString;
|
use syntax::parse::token::InternedString;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::attr::ThinAttributesExt;
|
use syntax::attr::ThinAttributesExt;
|
||||||
|
@ -168,7 +169,7 @@ impl LintStore {
|
||||||
match (sess, from_plugin) {
|
match (sess, from_plugin) {
|
||||||
// We load builtin lints first, so a duplicate is a compiler bug.
|
// We load builtin lints first, so a duplicate is a compiler bug.
|
||||||
// Use early_error when handling -W help with no crate.
|
// 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[..]),
|
(Some(sess), false) => sess.bug(&msg[..]),
|
||||||
|
|
||||||
// A duplicate name from a plugin is a user error.
|
// A duplicate name from a plugin is a user error.
|
||||||
|
@ -192,7 +193,7 @@ impl LintStore {
|
||||||
match (sess, from_plugin) {
|
match (sess, from_plugin) {
|
||||||
// We load builtin lints first, so a duplicate is a compiler bug.
|
// We load builtin lints first, so a duplicate is a compiler bug.
|
||||||
// Use early_error when handling -W help with no crate.
|
// 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[..]),
|
(Some(sess), false) => sess.bug(&msg[..]),
|
||||||
|
|
||||||
// A duplicate name from a plugin is a user error.
|
// A duplicate name from a plugin is a user error.
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
pub use self::EntryFnType::*;
|
pub use self::EntryFnType::*;
|
||||||
pub use self::CrateType::*;
|
pub use self::CrateType::*;
|
||||||
pub use self::Passes::*;
|
pub use self::Passes::*;
|
||||||
pub use self::OptLevel::*;
|
|
||||||
pub use self::DebugInfoLevel::*;
|
pub use self::DebugInfoLevel::*;
|
||||||
|
|
||||||
use session::{early_error, early_warn, Session};
|
use session::{early_error, early_warn, Session};
|
||||||
|
@ -71,6 +70,18 @@ pub enum OutputType {
|
||||||
DepInfo,
|
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 {
|
impl OutputType {
|
||||||
fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
|
fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -124,6 +135,7 @@ pub struct Options {
|
||||||
pub test: bool,
|
pub test: bool,
|
||||||
pub parse_only: bool,
|
pub parse_only: bool,
|
||||||
pub no_trans: bool,
|
pub no_trans: bool,
|
||||||
|
pub error_format: ErrorOutputType,
|
||||||
pub treat_err_as_bug: bool,
|
pub treat_err_as_bug: bool,
|
||||||
pub incremental_compilation: bool,
|
pub incremental_compilation: bool,
|
||||||
pub dump_dep_graph: bool,
|
pub dump_dep_graph: bool,
|
||||||
|
@ -131,7 +143,6 @@ pub struct Options {
|
||||||
pub debugging_opts: DebuggingOptions,
|
pub debugging_opts: DebuggingOptions,
|
||||||
pub prints: Vec<PrintRequest>,
|
pub prints: Vec<PrintRequest>,
|
||||||
pub cg: CodegenOptions,
|
pub cg: CodegenOptions,
|
||||||
pub color: ColorConfig,
|
|
||||||
pub externs: HashMap<String, Vec<String>>,
|
pub externs: HashMap<String, Vec<String>>,
|
||||||
pub crate_name: Option<String>,
|
pub crate_name: Option<String>,
|
||||||
/// An optional name to use as the crate for std during std injection,
|
/// An optional name to use as the crate for std during std injection,
|
||||||
|
@ -221,7 +232,7 @@ pub fn basic_options() -> Options {
|
||||||
Options {
|
Options {
|
||||||
crate_types: Vec::new(),
|
crate_types: Vec::new(),
|
||||||
gc: false,
|
gc: false,
|
||||||
optimize: No,
|
optimize: OptLevel::No,
|
||||||
debuginfo: NoDebugInfo,
|
debuginfo: NoDebugInfo,
|
||||||
lint_opts: Vec::new(),
|
lint_opts: Vec::new(),
|
||||||
lint_cap: None,
|
lint_cap: None,
|
||||||
|
@ -241,7 +252,7 @@ pub fn basic_options() -> Options {
|
||||||
debugging_opts: basic_debugging_options(),
|
debugging_opts: basic_debugging_options(),
|
||||||
prints: Vec::new(),
|
prints: Vec::new(),
|
||||||
cg: basic_codegen_options(),
|
cg: basic_codegen_options(),
|
||||||
color: ColorConfig::Auto,
|
error_format: ErrorOutputType::default(),
|
||||||
externs: HashMap::new(),
|
externs: HashMap::new(),
|
||||||
crate_name: None,
|
crate_name: None,
|
||||||
alt_std_name: None,
|
alt_std_name: None,
|
||||||
|
@ -308,7 +319,7 @@ macro_rules! options {
|
||||||
$struct_name { $($opt: $init),* }
|
$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();
|
let mut op = $defaultfn();
|
||||||
for option in matches.opt_strs($prefix) {
|
for option in matches.opt_strs($prefix) {
|
||||||
|
@ -322,20 +333,20 @@ macro_rules! options {
|
||||||
if !setter(&mut op, value) {
|
if !setter(&mut op, value) {
|
||||||
match (value, opt_type_desc) {
|
match (value, opt_type_desc) {
|
||||||
(Some(..), None) => {
|
(Some(..), None) => {
|
||||||
early_error(color, &format!("{} option `{}` takes no \
|
early_error(error_format, &format!("{} option `{}` takes no \
|
||||||
value", $outputname, key))
|
value", $outputname, key))
|
||||||
}
|
}
|
||||||
(None, Some(type_desc)) => {
|
(None, Some(type_desc)) => {
|
||||||
early_error(color, &format!("{0} option `{1}` requires \
|
early_error(error_format, &format!("{0} option `{1}` requires \
|
||||||
{2} ({3} {1}=<value>)",
|
{2} ({3} {1}=<value>)",
|
||||||
$outputname, key,
|
$outputname, key,
|
||||||
type_desc, $prefix))
|
type_desc, $prefix))
|
||||||
}
|
}
|
||||||
(Some(value), Some(type_desc)) => {
|
(Some(value), Some(type_desc)) => {
|
||||||
early_error(color, &format!("incorrect value `{}` for {} \
|
early_error(error_format, &format!("incorrect value `{}` for {} \
|
||||||
option `{}` - {} was expected",
|
option `{}` - {} was expected",
|
||||||
value, $outputname,
|
value, $outputname,
|
||||||
key, type_desc))
|
key, type_desc))
|
||||||
}
|
}
|
||||||
(None, None) => unreachable!()
|
(None, None) => unreachable!()
|
||||||
}
|
}
|
||||||
|
@ -344,8 +355,8 @@ macro_rules! options {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
early_error(color, &format!("unknown {} option: `{}`",
|
early_error(error_format, &format!("unknown {} option: `{}`",
|
||||||
$outputname, key));
|
$outputname, key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return op;
|
return op;
|
||||||
|
@ -861,6 +872,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
|
||||||
"NAME=PATH"),
|
"NAME=PATH"),
|
||||||
opt::opt("", "sysroot", "Override the system root", "PATH"),
|
opt::opt("", "sysroot", "Override the system root", "PATH"),
|
||||||
opt::multi("Z", "", "Set internal debugging options", "FLAG"),
|
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:
|
opt::opt("", "color", "Configure coloring of output:
|
||||||
auto = colorize, if output goes to a tty (default);
|
auto = colorize, if output goes to a tty (default);
|
||||||
always = always colorize output;
|
always = always colorize output;
|
||||||
|
@ -903,15 +915,37 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
None => ColorConfig::Auto,
|
None => ColorConfig::Auto,
|
||||||
|
|
||||||
Some(arg) => {
|
Some(arg) => {
|
||||||
early_error(ColorConfig::Auto, &format!("argument for --color must be auto, always \
|
early_error(ErrorOutputType::default(), &format!("argument for --color must be auto, \
|
||||||
or never (instead was `{}`)",
|
always or never (instead was `{}`)",
|
||||||
arg))
|
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 unparsed_crate_types = matches.opt_strs("crate-type");
|
||||||
let crate_types = parse_crate_types_from_list(unparsed_crate_types)
|
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 lint_opts = vec!();
|
||||||
let mut describe_lints = false;
|
let mut describe_lints = false;
|
||||||
|
@ -928,11 +962,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
|
|
||||||
let lint_cap = matches.opt_str("cap-lints").map(|cap| {
|
let lint_cap = matches.opt_str("cap-lints").map(|cap| {
|
||||||
lint::Level::from_str(&cap).unwrap_or_else(|| {
|
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 parse_only = debugging_opts.parse_only;
|
||||||
let no_trans = debugging_opts.no_trans;
|
let no_trans = debugging_opts.no_trans;
|
||||||
|
@ -958,7 +992,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
"link" => OutputType::Exe,
|
"link" => OutputType::Exe,
|
||||||
"dep-info" => OutputType::DepInfo,
|
"dep-info" => OutputType::DepInfo,
|
||||||
part => {
|
part => {
|
||||||
early_error(color, &format!("unknown emission type: `{}`",
|
early_error(error_format, &format!("unknown emission type: `{}`",
|
||||||
part))
|
part))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -971,7 +1005,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
output_types.insert(OutputType::Exe, None);
|
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
|
// Issue #30063: if user requests llvm-related output to one
|
||||||
// particular path, disable codegen-units.
|
// particular path, disable codegen-units.
|
||||||
|
@ -983,11 +1017,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
}).collect();
|
}).collect();
|
||||||
if !incompatible.is_empty() {
|
if !incompatible.is_empty() {
|
||||||
for ot in &incompatible {
|
for ot in &incompatible {
|
||||||
early_warn(color, &format!("--emit={} with -o incompatible with \
|
early_warn(error_format, &format!("--emit={} with -o incompatible with \
|
||||||
-C codegen-units=N for N > 1",
|
-C codegen-units=N for N > 1",
|
||||||
ot.shorthand()));
|
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;
|
cg.codegen_units = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1000,29 +1034,29 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
let opt_level = {
|
let opt_level = {
|
||||||
if matches.opt_present("O") {
|
if matches.opt_present("O") {
|
||||||
if cg.opt_level.is_some() {
|
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 {
|
} else {
|
||||||
match cg.opt_level {
|
match cg.opt_level {
|
||||||
None => No,
|
None => OptLevel::No,
|
||||||
Some(0) => No,
|
Some(0) => OptLevel::No,
|
||||||
Some(1) => Less,
|
Some(1) => OptLevel::Less,
|
||||||
Some(2) => Default,
|
Some(2) => OptLevel::Default,
|
||||||
Some(3) => Aggressive,
|
Some(3) => OptLevel::Aggressive,
|
||||||
Some(arg) => {
|
Some(arg) => {
|
||||||
early_error(color, &format!("optimization level needs to be \
|
early_error(error_format, &format!("optimization level needs to be \
|
||||||
between 0-3 (instead was `{}`)",
|
between 0-3 (instead was `{}`)",
|
||||||
arg));
|
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 gc = debugging_opts.gc;
|
||||||
let debuginfo = if matches.opt_present("g") {
|
let debuginfo = if matches.opt_present("g") {
|
||||||
if cg.debuginfo.is_some() {
|
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
|
FullDebugInfo
|
||||||
} else {
|
} else {
|
||||||
|
@ -1031,16 +1065,16 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
Some(1) => LimitedDebugInfo,
|
Some(1) => LimitedDebugInfo,
|
||||||
Some(2) => FullDebugInfo,
|
Some(2) => FullDebugInfo,
|
||||||
Some(arg) => {
|
Some(arg) => {
|
||||||
early_error(color, &format!("debug info level needs to be between \
|
early_error(error_format, &format!("debug info level needs to be between \
|
||||||
0-2 (instead was `{}`)",
|
0-2 (instead was `{}`)",
|
||||||
arg));
|
arg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut search_paths = SearchPaths::new();
|
let mut search_paths = SearchPaths::new();
|
||||||
for s in &matches.opt_strs("L") {
|
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| {
|
let libs = matches.opt_strs("l").into_iter().map(|s| {
|
||||||
|
@ -1052,9 +1086,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
(Some(name), "framework") => (name, cstore::NativeFramework),
|
(Some(name), "framework") => (name, cstore::NativeFramework),
|
||||||
(Some(name), "static") => (name, cstore::NativeStatic),
|
(Some(name), "static") => (name, cstore::NativeStatic),
|
||||||
(_, s) => {
|
(_, s) => {
|
||||||
early_error(color, &format!("unknown library kind `{}`, expected \
|
early_error(error_format, &format!("unknown library kind `{}`, expected \
|
||||||
one of dylib, framework, or static",
|
one of dylib, framework, or static",
|
||||||
s));
|
s));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(name.to_string(), kind)
|
(name.to_string(), kind)
|
||||||
|
@ -1069,14 +1103,14 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
"file-names" => PrintRequest::FileNames,
|
"file-names" => PrintRequest::FileNames,
|
||||||
"sysroot" => PrintRequest::Sysroot,
|
"sysroot" => PrintRequest::Sysroot,
|
||||||
req => {
|
req => {
|
||||||
early_error(color, &format!("unknown print request `{}`", req))
|
early_error(error_format, &format!("unknown print request `{}`", req))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
|
if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
|
||||||
early_warn(color, "-C remark will not show source locations without \
|
early_warn(error_format, "-C remark will not show source locations without \
|
||||||
--debuginfo");
|
--debuginfo");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut externs = HashMap::new();
|
let mut externs = HashMap::new();
|
||||||
|
@ -1084,11 +1118,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
let mut parts = arg.splitn(2, '=');
|
let mut parts = arg.splitn(2, '=');
|
||||||
let name = match parts.next() {
|
let name = match parts.next() {
|
||||||
Some(s) => s,
|
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() {
|
let location = match parts.next() {
|
||||||
Some(s) => s,
|
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());
|
externs.entry(name.to_string()).or_insert(vec![]).push(location.to_string());
|
||||||
|
@ -1119,7 +1153,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||||
debugging_opts: debugging_opts,
|
debugging_opts: debugging_opts,
|
||||||
prints: prints,
|
prints: prints,
|
||||||
cg: cg,
|
cg: cg,
|
||||||
color: color,
|
error_format: error_format,
|
||||||
externs: externs,
|
externs: externs,
|
||||||
crate_name: crate_name,
|
crate_name: crate_name,
|
||||||
alt_std_name: None,
|
alt_std_name: None,
|
||||||
|
|
|
@ -17,7 +17,8 @@ use util::nodemap::{NodeMap, FnvHashMap};
|
||||||
use syntax::ast::{NodeId, NodeIdAssigner, Name};
|
use syntax::ast::{NodeId, NodeIdAssigner, Name};
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::errors::{self, DiagnosticBuilder};
|
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::diagnostics;
|
||||||
use syntax::feature_gate;
|
use syntax::feature_gate;
|
||||||
use syntax::parse;
|
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 treat_err_as_bug = sopts.treat_err_as_bug;
|
||||||
|
|
||||||
let codemap = Rc::new(codemap::CodeMap::new());
|
let codemap = Rc::new(codemap::CodeMap::new());
|
||||||
|
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 => {
|
||||||
|
Box::new(JsonEmitter::stderr(Some(registry), codemap.clone()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let diagnostic_handler =
|
let diagnostic_handler =
|
||||||
errors::Handler::new(sopts.color,
|
errors::Handler::with_emitter(can_print_warnings,
|
||||||
Some(registry),
|
treat_err_as_bug,
|
||||||
can_print_warnings,
|
emitter);
|
||||||
treat_err_as_bug,
|
|
||||||
codemap.clone());
|
|
||||||
|
|
||||||
build_session_(sopts, local_crate_source_file, diagnostic_handler, codemap, cstore)
|
build_session_(sopts, local_crate_source_file, diagnostic_handler, codemap, cstore)
|
||||||
}
|
}
|
||||||
|
@ -473,13 +481,23 @@ pub fn build_session_(sopts: config::Options,
|
||||||
sess
|
sess
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn early_error(color: errors::ColorConfig, msg: &str) -> ! {
|
pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! {
|
||||||
let mut emitter = BasicEmitter::stderr(color);
|
let mut emitter: Box<Emitter> = match output {
|
||||||
|
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);
|
emitter.emit(None, msg, None, errors::Level::Fatal);
|
||||||
panic!(errors::FatalError);
|
panic!(errors::FatalError);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn early_warn(color: errors::ColorConfig, msg: &str) {
|
pub fn early_warn(output: config::ErrorOutputType, msg: &str) {
|
||||||
let mut emitter = BasicEmitter::stderr(color);
|
let mut emitter: Box<Emitter> = match output {
|
||||||
|
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);
|
emitter.emit(None, msg, None, errors::Level::Warning);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
|
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use session::early_error;
|
use session::{early_error, config};
|
||||||
use syntax::errors;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SearchPaths {
|
pub struct SearchPaths {
|
||||||
|
@ -38,7 +37,7 @@ impl SearchPaths {
|
||||||
SearchPaths { paths: Vec::new() }
|
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=") {
|
let (kind, path) = if path.starts_with("native=") {
|
||||||
(PathKind::Native, &path["native=".len()..])
|
(PathKind::Native, &path["native=".len()..])
|
||||||
} else if path.starts_with("crate=") {
|
} else if path.starts_with("crate=") {
|
||||||
|
@ -53,7 +52,7 @@ impl SearchPaths {
|
||||||
(PathKind::All, path)
|
(PathKind::All, path)
|
||||||
};
|
};
|
||||||
if path.is_empty() {
|
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)));
|
self.paths.push((kind, PathBuf::from(path)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ use rustc_resolve as resolve;
|
||||||
use rustc_trans::back::link;
|
use rustc_trans::back::link;
|
||||||
use rustc_trans::save;
|
use rustc_trans::save;
|
||||||
use rustc::session::{config, Session, build_session};
|
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::middle::cstore::CrateStore;
|
||||||
use rustc::lint::Lint;
|
use rustc::lint::Lint;
|
||||||
use rustc::lint;
|
use rustc::lint;
|
||||||
|
@ -72,6 +72,7 @@ use rustc::util::common::time;
|
||||||
|
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::cmp::Ordering::Equal;
|
use std::cmp::Ordering::Equal;
|
||||||
|
use std::default::Default;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::iter::repeat;
|
use std::iter::repeat;
|
||||||
|
@ -126,7 +127,7 @@ pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>) {
|
||||||
|
|
||||||
let descriptions = diagnostics_registry();
|
let descriptions = diagnostics_registry();
|
||||||
|
|
||||||
do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.color));
|
do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.error_format));
|
||||||
|
|
||||||
let (odir, ofile) = make_output(&matches);
|
let (odir, ofile) = make_output(&matches);
|
||||||
let (input, input_file_path) = match make_input(&matches.free) {
|
let (input, input_file_path) = match make_input(&matches.free) {
|
||||||
|
@ -214,7 +215,7 @@ pub trait CompilerCalls<'a> {
|
||||||
fn early_callback(&mut self,
|
fn early_callback(&mut self,
|
||||||
_: &getopts::Matches,
|
_: &getopts::Matches,
|
||||||
_: &diagnostics::registry::Registry,
|
_: &diagnostics::registry::Registry,
|
||||||
_: errors::ColorConfig)
|
_: ErrorOutputType)
|
||||||
-> Compilation {
|
-> Compilation {
|
||||||
Compilation::Continue
|
Compilation::Continue
|
||||||
}
|
}
|
||||||
|
@ -290,7 +291,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
|
||||||
fn early_callback(&mut self,
|
fn early_callback(&mut self,
|
||||||
matches: &getopts::Matches,
|
matches: &getopts::Matches,
|
||||||
descriptions: &diagnostics::registry::Registry,
|
descriptions: &diagnostics::registry::Registry,
|
||||||
color: errors::ColorConfig)
|
output: ErrorOutputType)
|
||||||
-> Compilation {
|
-> Compilation {
|
||||||
match matches.opt_str("explain") {
|
match matches.opt_str("explain") {
|
||||||
Some(ref code) => {
|
Some(ref code) => {
|
||||||
|
@ -305,7 +306,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
|
||||||
print!("{}", &description[1..]);
|
print!("{}", &description[1..]);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
early_error(color, &format!("no extended information for {}", code));
|
early_error(output, &format!("no extended information for {}", code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Compilation::Stop;
|
return Compilation::Stop;
|
||||||
|
@ -339,10 +340,10 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
|
||||||
if should_stop == Compilation::Stop {
|
if should_stop == Compilation::Stop {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
early_error(sopts.color, "no input filename given");
|
early_error(sopts.error_format, "no input filename given");
|
||||||
}
|
}
|
||||||
1 => panic!("make_input should have provided valid inputs"),
|
1 => panic!("make_input should have provided valid inputs"),
|
||||||
_ => early_error(sopts.color, "multiple input filenames provided"),
|
_ => early_error(sopts.error_format, "multiple input filenames provided"),
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
@ -432,7 +433,7 @@ impl RustcDefaultCalls {
|
||||||
println!("{}", String::from_utf8(v).unwrap());
|
println!("{}", String::from_utf8(v).unwrap());
|
||||||
}
|
}
|
||||||
&Input::Str(_) => {
|
&Input::Str(_) => {
|
||||||
early_error(sess.opts.color, "cannot list metadata for stdin");
|
early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Compilation::Stop;
|
return Compilation::Stop;
|
||||||
|
@ -459,7 +460,7 @@ impl RustcDefaultCalls {
|
||||||
PrintRequest::CrateName => {
|
PrintRequest::CrateName => {
|
||||||
let input = match input {
|
let input = match input {
|
||||||
Some(input) => 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 attrs = attrs.as_ref().unwrap();
|
||||||
let t_outputs = driver::build_output_filenames(input, odir, ofile, attrs, sess);
|
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
|
&opt.opt_group.short_name
|
||||||
};
|
};
|
||||||
if m.opt_present(opt_name) {
|
if m.opt_present(opt_name) {
|
||||||
early_error(errors::ColorConfig::Auto,
|
early_error(ErrorOutputType::default(),
|
||||||
&format!("use of unstable option '{}' requires -Z \
|
&format!("use of unstable option '{}' requires -Z \
|
||||||
unstable-options",
|
unstable-options",
|
||||||
opt_name));
|
opt_name));
|
||||||
|
@ -761,7 +762,7 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
|
||||||
}
|
}
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
Err(f) => early_error(errors::ColorConfig::Auto, &f.to_string()),
|
Err(f) => early_error(ErrorOutputType::default(), &f.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,8 +147,8 @@ impl<'a> Linker for GnuLinker<'a> {
|
||||||
|
|
||||||
// GNU-style linkers support optimization with -O. GNU ld doesn't
|
// GNU-style linkers support optimization with -O. GNU ld doesn't
|
||||||
// need a numeric argument, but other linkers do.
|
// need a numeric argument, but other linkers do.
|
||||||
if self.sess.opts.optimize == config::Default ||
|
if self.sess.opts.optimize == config::OptLevel::Default ||
|
||||||
self.sess.opts.optimize == config::Aggressive {
|
self.sess.opts.optimize == config::OptLevel::Aggressive {
|
||||||
self.cmd.arg("-Wl,-O1");
|
self.cmd.arg("-Wl,-O1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,10 +144,10 @@ fn target_feature(sess: &Session) -> String {
|
||||||
|
|
||||||
fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
|
fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
|
||||||
match optimize {
|
match optimize {
|
||||||
config::No => llvm::CodeGenLevelNone,
|
config::OptLevel::No => llvm::CodeGenLevelNone,
|
||||||
config::Less => llvm::CodeGenLevelLess,
|
config::OptLevel::Less => llvm::CodeGenLevelLess,
|
||||||
config::Default => llvm::CodeGenLevelDefault,
|
config::OptLevel::Default => llvm::CodeGenLevelDefault,
|
||||||
config::Aggressive => llvm::CodeGenLevelAggressive,
|
config::OptLevel::Aggressive => llvm::CodeGenLevelAggressive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,13 +303,13 @@ impl ModuleConfig {
|
||||||
// slp vectorization at O3. Otherwise configure other optimization aspects
|
// slp vectorization at O3. Otherwise configure other optimization aspects
|
||||||
// of this pass manager builder.
|
// of this pass manager builder.
|
||||||
self.vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
|
self.vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
|
||||||
(sess.opts.optimize == config::Default ||
|
(sess.opts.optimize == config::OptLevel::Default ||
|
||||||
sess.opts.optimize == config::Aggressive);
|
sess.opts.optimize == config::OptLevel::Aggressive);
|
||||||
self.vectorize_slp = !sess.opts.cg.no_vectorize_slp &&
|
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 ||
|
self.merge_functions = sess.opts.optimize == config::OptLevel::Default ||
|
||||||
sess.opts.optimize == config::Aggressive;
|
sess.opts.optimize == config::OptLevel::Aggressive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1163,7 +1163,7 @@ fn core_lifetime_emit<'blk, 'tcx, F>(ccx: &'blk CrateContext<'blk, 'tcx>,
|
||||||
emit: F)
|
emit: F)
|
||||||
where F: FnOnce(&'blk CrateContext<'blk, 'tcx>, machine::llsize, ValueRef)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1020,7 +1020,7 @@ pub fn compile_unit_metadata(cx: &CrateContext) -> DIDescriptor {
|
||||||
compile_unit_name,
|
compile_unit_name,
|
||||||
work_dir.as_ptr(),
|
work_dir.as_ptr(),
|
||||||
producer.as_ptr(),
|
producer.as_ptr(),
|
||||||
cx.sess().opts.optimize != config::No,
|
cx.sess().opts.optimize != config::OptLevel::No,
|
||||||
flags.as_ptr() as *const _,
|
flags.as_ptr() as *const _,
|
||||||
0,
|
0,
|
||||||
split_name.as_ptr() as *const _)
|
split_name.as_ptr() as *const _)
|
||||||
|
|
|
@ -383,7 +383,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||||
true,
|
true,
|
||||||
scope_line as c_uint,
|
scope_line as c_uint,
|
||||||
FlagPrototyped as c_uint,
|
FlagPrototyped as c_uint,
|
||||||
cx.sess().opts.optimize != config::No,
|
cx.sess().opts.optimize != config::OptLevel::No,
|
||||||
llfn,
|
llfn,
|
||||||
template_parameters,
|
template_parameters,
|
||||||
ptr::null_mut())
|
ptr::null_mut())
|
||||||
|
@ -596,7 +596,7 @@ fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||||
file_metadata,
|
file_metadata,
|
||||||
loc.line as c_uint,
|
loc.line as c_uint,
|
||||||
type_metadata,
|
type_metadata,
|
||||||
cx.sess().opts.optimize != config::No,
|
cx.sess().opts.optimize != config::OptLevel::No,
|
||||||
0,
|
0,
|
||||||
address_operations.as_ptr(),
|
address_operations.as_ptr(),
|
||||||
address_operations.len() as c_uint,
|
address_operations.len() as c_uint,
|
||||||
|
|
|
@ -118,11 +118,11 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
|
||||||
};
|
};
|
||||||
|
|
||||||
let codemap = Rc::new(codemap::CodeMap::new());
|
let codemap = Rc::new(codemap::CodeMap::new());
|
||||||
let diagnostic_handler = errors::Handler::new(ColorConfig::Auto,
|
let diagnostic_handler = errors::Handler::with_tty_emitter(ColorConfig::Auto,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
codemap.clone());
|
codemap.clone());
|
||||||
|
|
||||||
let cstore = Rc::new(CStore::new(token::get_ident_interner()));
|
let cstore = Rc::new(CStore::new(token::get_ident_interner()));
|
||||||
let sess = session::build_session_(sessopts, cpath, diagnostic_handler,
|
let sess = session::build_session_(sessopts, cpath, diagnostic_handler,
|
||||||
|
|
|
@ -50,6 +50,7 @@ extern crate serialize as rustc_serialize; // used by deriving
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::default::Default;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
@ -62,7 +63,7 @@ use externalfiles::ExternalHtml;
|
||||||
use serialize::Decodable;
|
use serialize::Decodable;
|
||||||
use serialize::json::{self, Json};
|
use serialize::json::{self, Json};
|
||||||
use rustc::session::search_paths::SearchPaths;
|
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
|
// reexported from `clean` so it can be easily updated with the mod itself
|
||||||
pub use clean::SCHEMA_VERSION;
|
pub use clean::SCHEMA_VERSION;
|
||||||
|
@ -225,7 +226,7 @@ pub fn main_args(args: &[String]) -> isize {
|
||||||
|
|
||||||
let mut libs = SearchPaths::new();
|
let mut libs = SearchPaths::new();
|
||||||
for s in &matches.opt_strs("L") {
|
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) {
|
let externs = match parse_externs(&matches) {
|
||||||
Ok(ex) => ex,
|
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.
|
// First, parse the crate and extract all relevant information.
|
||||||
let mut paths = SearchPaths::new();
|
let mut paths = SearchPaths::new();
|
||||||
for s in &matches.opt_strs("L") {
|
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 cfgs = matches.opt_strs("cfg");
|
||||||
let triple = matches.opt_str("target");
|
let triple = matches.opt_str("target");
|
||||||
|
|
|
@ -73,11 +73,11 @@ pub fn run(input: &str,
|
||||||
};
|
};
|
||||||
|
|
||||||
let codemap = Rc::new(CodeMap::new());
|
let codemap = Rc::new(CodeMap::new());
|
||||||
let diagnostic_handler = errors::Handler::new(ColorConfig::Auto,
|
let diagnostic_handler = errors::Handler::with_tty_emitter(ColorConfig::Auto,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
codemap.clone());
|
codemap.clone());
|
||||||
|
|
||||||
let cstore = Rc::new(CStore::new(token::get_ident_interner()));
|
let cstore = Rc::new(CStore::new(token::get_ident_interner()));
|
||||||
let sess = session::build_session_(sessopts,
|
let sess = session::build_session_(sessopts,
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub trait Emitter {
|
||||||
/// maximum number of lines we will print for each error; arbitrary.
|
/// maximum number of lines we will print for each error; arbitrary.
|
||||||
const MAX_LINES: usize = 6;
|
const MAX_LINES: usize = 6;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum ColorConfig {
|
pub enum ColorConfig {
|
||||||
Auto,
|
Auto,
|
||||||
Always,
|
Always,
|
||||||
|
|
233
src/libsyntax/errors/json.rs
Normal file
233
src/libsyntax/errors/json.rs
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
// 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.
|
||||||
|
//!
|
||||||
|
//! 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, SubDiagnostic, RenderSpan};
|
||||||
|
use errors::emitter::Emitter;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
use rustc_serialize::json::as_json;
|
||||||
|
|
||||||
|
pub struct JsonEmitter {
|
||||||
|
dst: Box<Write + Send>,
|
||||||
|
registry: Option<Registry>,
|
||||||
|
cm: Rc<CodeMap>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JsonEmitter {
|
||||||
|
pub fn basic() -> JsonEmitter {
|
||||||
|
JsonEmitter::stderr(None, Rc::new(CodeMap::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stderr(registry: Option<Registry>,
|
||||||
|
code_map: Rc<CodeMap>) -> JsonEmitter {
|
||||||
|
JsonEmitter {
|
||||||
|
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>, 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 from_render_span(span: &RenderSpan,
|
||||||
|
msg: &'a str,
|
||||||
|
level: Level,
|
||||||
|
je: &JsonEmitter)
|
||||||
|
-> Diagnostic<'a> {
|
||||||
|
Diagnostic {
|
||||||
|
message: 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 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,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ use std::rc::Rc;
|
||||||
use term;
|
use term;
|
||||||
|
|
||||||
pub mod emitter;
|
pub mod emitter;
|
||||||
|
pub mod json;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum RenderSpan {
|
pub enum RenderSpan {
|
||||||
|
@ -275,12 +276,12 @@ pub struct Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler {
|
impl Handler {
|
||||||
pub fn new(color_config: ColorConfig,
|
pub fn with_tty_emitter(color_config: ColorConfig,
|
||||||
registry: Option<diagnostics::registry::Registry>,
|
registry: Option<diagnostics::registry::Registry>,
|
||||||
can_emit_warnings: bool,
|
can_emit_warnings: bool,
|
||||||
treat_err_as_bug: bool,
|
treat_err_as_bug: bool,
|
||||||
cm: Rc<codemap::CodeMap>)
|
cm: Rc<codemap::CodeMap>)
|
||||||
-> Handler {
|
-> Handler {
|
||||||
let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm));
|
let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm));
|
||||||
Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter)
|
Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter)
|
||||||
}
|
}
|
||||||
|
@ -547,14 +548,7 @@ impl fmt::Display for Level {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
match *self {
|
self.to_str().fmt(f)
|
||||||
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!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,6 +562,17 @@ impl Level {
|
||||||
Cancelled => unreachable!(),
|
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
|
pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where
|
||||||
|
|
|
@ -49,7 +49,7 @@ pub struct ParseSess {
|
||||||
impl ParseSess {
|
impl ParseSess {
|
||||||
pub fn new() -> ParseSess {
|
pub fn new() -> ParseSess {
|
||||||
let cm = Rc::new(CodeMap::new());
|
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)
|
ParseSess::with_span_handler(handler, cm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -195,7 +195,7 @@ fn build_exec_options(sysroot: PathBuf) -> Options {
|
||||||
opts.maybe_sysroot = Some(sysroot);
|
opts.maybe_sysroot = Some(sysroot);
|
||||||
|
|
||||||
// Prefer faster build time
|
// Prefer faster build time
|
||||||
opts.optimize = config::No;
|
opts.optimize = config::OptLevel::No;
|
||||||
|
|
||||||
// Don't require a `main` function
|
// Don't require a `main` function
|
||||||
opts.crate_types = vec![config::CrateTypeDylib];
|
opts.crate_types = vec![config::CrateTypeDylib];
|
||||||
|
|
8
src/test/run-make/json-errors/Makefile
Normal file
8
src/test/run-make/json-errors/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
-include ../tools.mk
|
||||||
|
|
||||||
|
all:
|
||||||
|
cp foo.rs $(TMPDIR)
|
||||||
|
cd $(TMPDIR)
|
||||||
|
-$(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
|
15
src/test/run-make/json-errors/foo.rs
Normal file
15
src/test/run-make/json-errors/foo.rs
Normal file
|
@ -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.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = 42 + y;
|
||||||
|
|
||||||
|
42u8 + 42i32;
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ impl<'a> CompilerCalls<'a> for TestCalls {
|
||||||
fn early_callback(&mut self,
|
fn early_callback(&mut self,
|
||||||
_: &getopts::Matches,
|
_: &getopts::Matches,
|
||||||
_: &diagnostics::registry::Registry,
|
_: &diagnostics::registry::Registry,
|
||||||
_: errors::emitter::ColorConfig)
|
_: config::ErrorOutputType)
|
||||||
-> Compilation {
|
-> Compilation {
|
||||||
self.count *= 2;
|
self.count *= 2;
|
||||||
Compilation::Continue
|
Compilation::Continue
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue