auto merge of #15336 : jakub-/rust/diagnostics, r=brson
This is a continuation of @brson's work from https://github.com/rust-lang/rust/pull/12144. This implements the minimal scaffolding that allows mapping diagnostic messages to alpha-numeric codes, which could improve the searchability of errors. In addition, there's a new compiler option, `--explain {code}` which takes an error code and prints out a somewhat detailed explanation of the error. Example: ```rust fn f(x: Option<bool>) { match x { Some(true) | Some(false) => (), None => (), Some(true) => () } } ``` ```shell [~/rust]$ ./build/x86_64-apple-darwin/stage2/bin/rustc ./diagnostics.rs --crate-type dylib diagnostics.rs:5:3: 5:13 error: unreachable pattern [E0001] (pass `--explain E0001` to see a detailed explanation) diagnostics.rs:5 Some(true) => () ^~~~~~~~~~ error: aborting due to previous error [~/rust]$ ./build/x86_64-apple-darwin/stage2/bin/rustc --explain E0001 This error suggests that the expression arm corresponding to the noted pattern will never be reached as for all possible values of the expression being matched, one of the preceeding patterns will match. This means that perhaps some of the preceeding patterns are too general, this one is too specific or the ordering is incorrect. ``` I've refrained from migrating many errors to actually use the new macros as it can be done in an incremental fashion but if we're happy with the approach, it'd be good to do all of them sooner rather than later. Originally, I was going to make libdiagnostics a separate crate but that's posing some interesting challenges with semi-circular dependencies. In particular, librustc would have a plugin-phase dependency on libdiagnostics, which itself depends on librustc. Per my conversation with @alexcrichton, it seems like the snapshotting process would also have to change. So for now the relevant modules from libdiagnostics are included using `#[path = ...] mod`.
This commit is contained in:
18
src/librustc/diagnostics.rs
Normal file
18
src/librustc/diagnostics.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
register_diagnostic!(E0001, r##"
|
||||||
|
This error suggests that the expression arm corresponding to the noted pattern
|
||||||
|
will never be reached as for all possible values of the expression being matched,
|
||||||
|
one of the preceeding patterns will match.
|
||||||
|
|
||||||
|
This means that perhaps some of the preceeding patterns are too general, this
|
||||||
|
one is too specific or the ordering is incorrect.
|
||||||
|
"##)
|
||||||
@@ -532,6 +532,7 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
|
|||||||
optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
|
optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
|
||||||
optopt( "", "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
|
optopt( "", "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
|
||||||
optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
|
optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
|
||||||
|
optopt("", "explain", "Provide a detailed explanation of an error message", "OPT"),
|
||||||
optflagopt("", "pretty",
|
optflagopt("", "pretty",
|
||||||
"Pretty-print the input instead of compiling;
|
"Pretty-print the input instead of compiling;
|
||||||
valid types are: `normal` (un-annotated source),
|
valid types are: `normal` (un-annotated source),
|
||||||
@@ -807,6 +808,7 @@ mod test {
|
|||||||
use getopts::getopts;
|
use getopts::getopts;
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::attr::AttrMetaMethods;
|
use syntax::attr::AttrMetaMethods;
|
||||||
|
use syntax::diagnostics;
|
||||||
|
|
||||||
// When the user supplies --test we should implicitly supply --cfg test
|
// When the user supplies --test we should implicitly supply --cfg test
|
||||||
#[test]
|
#[test]
|
||||||
@@ -816,8 +818,9 @@ mod test {
|
|||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(f) => fail!("test_switch_implies_cfg_test: {}", f)
|
Err(f) => fail!("test_switch_implies_cfg_test: {}", f)
|
||||||
};
|
};
|
||||||
|
let registry = diagnostics::registry::Registry::new([]);
|
||||||
let sessopts = build_session_options(matches);
|
let sessopts = build_session_options(matches);
|
||||||
let sess = build_session(sessopts, None);
|
let sess = build_session(sessopts, None, registry);
|
||||||
let cfg = build_configuration(&sess);
|
let cfg = build_configuration(&sess);
|
||||||
assert!((attr::contains_name(cfg.as_slice(), "test")));
|
assert!((attr::contains_name(cfg.as_slice(), "test")));
|
||||||
}
|
}
|
||||||
@@ -834,8 +837,9 @@ mod test {
|
|||||||
fail!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
|
fail!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let registry = diagnostics::registry::Registry::new([]);
|
||||||
let sessopts = build_session_options(matches);
|
let sessopts = build_session_options(matches);
|
||||||
let sess = build_session(sessopts, None);
|
let sess = build_session(sessopts, None, registry);
|
||||||
let cfg = build_configuration(&sess);
|
let cfg = build_configuration(&sess);
|
||||||
let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
|
let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
|
||||||
assert!(test_items.next().is_some());
|
assert!(test_items.next().is_some());
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use driver::{PpmFlowGraph, PpmExpanded, PpmExpandedIdentified, PpmTyped};
|
|||||||
use driver::{PpmIdentified};
|
use driver::{PpmIdentified};
|
||||||
use front;
|
use front;
|
||||||
use lib::llvm::{ContextRef, ModuleRef};
|
use lib::llvm::{ContextRef, ModuleRef};
|
||||||
|
use lint;
|
||||||
use metadata::common::LinkMeta;
|
use metadata::common::LinkMeta;
|
||||||
use metadata::creader;
|
use metadata::creader;
|
||||||
use middle::cfg;
|
use middle::cfg;
|
||||||
@@ -26,7 +27,7 @@ use middle;
|
|||||||
use plugin::load::Plugins;
|
use plugin::load::Plugins;
|
||||||
use plugin::registry::Registry;
|
use plugin::registry::Registry;
|
||||||
use plugin;
|
use plugin;
|
||||||
use lint;
|
|
||||||
use util::common::time;
|
use util::common::time;
|
||||||
use util::ppaux;
|
use util::ppaux;
|
||||||
use util::nodemap::{NodeSet};
|
use util::nodemap::{NodeSet};
|
||||||
@@ -41,6 +42,7 @@ use std::io::MemReader;
|
|||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::attr::{AttrMetaMethods};
|
use syntax::attr::{AttrMetaMethods};
|
||||||
|
use syntax::diagnostics;
|
||||||
use syntax::parse;
|
use syntax::parse;
|
||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
use syntax::print::{pp, pprust};
|
use syntax::print::{pp, pprust};
|
||||||
@@ -213,6 +215,15 @@ pub fn phase_2_configure_and_expand(sess: &Session,
|
|||||||
let mut registry = Registry::new(&krate);
|
let mut registry = Registry::new(&krate);
|
||||||
|
|
||||||
time(time_passes, "plugin registration", (), |_| {
|
time(time_passes, "plugin registration", (), |_| {
|
||||||
|
if sess.features.rustc_diagnostic_macros.get() {
|
||||||
|
registry.register_macro("__diagnostic_used",
|
||||||
|
diagnostics::plugin::expand_diagnostic_used);
|
||||||
|
registry.register_macro("__register_diagnostic",
|
||||||
|
diagnostics::plugin::expand_register_diagnostic);
|
||||||
|
registry.register_macro("__build_diagnostic_array",
|
||||||
|
diagnostics::plugin::expand_build_diagnostic_array);
|
||||||
|
}
|
||||||
|
|
||||||
for ®istrar in registrars.iter() {
|
for ®istrar in registrars.iter() {
|
||||||
registrar(&mut registry);
|
registrar(&mut registry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use std::task::TaskBuilder;
|
|||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::parse;
|
use syntax::parse;
|
||||||
use syntax::diagnostic::Emitter;
|
use syntax::diagnostic::Emitter;
|
||||||
|
use syntax::diagnostics;
|
||||||
|
|
||||||
use getopts;
|
use getopts;
|
||||||
|
|
||||||
@@ -49,8 +50,24 @@ fn run_compiler(args: &[String]) {
|
|||||||
Some(matches) => matches,
|
Some(matches) => matches,
|
||||||
None => return
|
None => return
|
||||||
};
|
};
|
||||||
let sopts = config::build_session_options(&matches);
|
|
||||||
|
|
||||||
|
let descriptions = diagnostics::registry::Registry::new(super::DIAGNOSTICS);
|
||||||
|
match matches.opt_str("explain") {
|
||||||
|
Some(ref code) => {
|
||||||
|
match descriptions.find_description(code.as_slice()) {
|
||||||
|
Some(ref description) => {
|
||||||
|
println!("{}", description);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
early_error(format!("no extended information for {}", code).as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
let sopts = config::build_session_options(&matches);
|
||||||
let (input, input_file_path) = match matches.free.len() {
|
let (input, input_file_path) = match matches.free.len() {
|
||||||
0u => {
|
0u => {
|
||||||
if sopts.describe_lints {
|
if sopts.describe_lints {
|
||||||
@@ -75,7 +92,7 @@ fn run_compiler(args: &[String]) {
|
|||||||
_ => early_error("multiple input filenames provided")
|
_ => early_error("multiple input filenames provided")
|
||||||
};
|
};
|
||||||
|
|
||||||
let sess = build_session(sopts, input_file_path);
|
let sess = build_session(sopts, input_file_path, descriptions);
|
||||||
let cfg = config::build_configuration(&sess);
|
let cfg = config::build_configuration(&sess);
|
||||||
let odir = matches.opt_str("out-dir").map(|o| Path::new(o));
|
let odir = matches.opt_str("out-dir").map(|o| Path::new(o));
|
||||||
let ofile = matches.opt_str("o").map(|o| Path::new(o));
|
let ofile = matches.opt_str("o").map(|o| Path::new(o));
|
||||||
@@ -383,14 +400,14 @@ fn parse_crate_attrs(sess: &Session, input: &Input) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn early_error(msg: &str) -> ! {
|
pub fn early_error(msg: &str) -> ! {
|
||||||
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto);
|
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);
|
||||||
emitter.emit(None, msg, diagnostic::Fatal);
|
emitter.emit(None, msg, None, diagnostic::Fatal);
|
||||||
fail!(diagnostic::FatalError);
|
fail!(diagnostic::FatalError);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn early_warn(msg: &str) {
|
pub fn early_warn(msg: &str) {
|
||||||
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto);
|
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);
|
||||||
emitter.emit(None, msg, diagnostic::Warning);
|
emitter.emit(None, msg, None, diagnostic::Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_metadata(sess: &Session, path: &Path,
|
pub fn list_metadata(sess: &Session, path: &Path,
|
||||||
@@ -429,7 +446,7 @@ fn monitor(f: proc():Send) {
|
|||||||
Err(value) => {
|
Err(value) => {
|
||||||
// Task failed without emitting a fatal diagnostic
|
// Task failed without emitting a fatal diagnostic
|
||||||
if !value.is::<diagnostic::FatalError>() {
|
if !value.is::<diagnostic::FatalError>() {
|
||||||
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto);
|
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);
|
||||||
|
|
||||||
// a .span_bug or .bug call has already printed what
|
// a .span_bug or .bug call has already printed what
|
||||||
// it wants to print.
|
// it wants to print.
|
||||||
@@ -437,6 +454,7 @@ fn monitor(f: proc():Send) {
|
|||||||
emitter.emit(
|
emitter.emit(
|
||||||
None,
|
None,
|
||||||
"unexpected failure",
|
"unexpected failure",
|
||||||
|
None,
|
||||||
diagnostic::Bug);
|
diagnostic::Bug);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +465,7 @@ fn monitor(f: proc():Send) {
|
|||||||
"run with `RUST_BACKTRACE=1` for a backtrace".to_string(),
|
"run with `RUST_BACKTRACE=1` for a backtrace".to_string(),
|
||||||
];
|
];
|
||||||
for note in xs.iter() {
|
for note in xs.iter() {
|
||||||
emitter.emit(None, note.as_slice(), diagnostic::Note)
|
emitter.emit(None, note.as_slice(), None, diagnostic::Note)
|
||||||
}
|
}
|
||||||
|
|
||||||
match r.read_to_string() {
|
match r.read_to_string() {
|
||||||
@@ -457,6 +475,7 @@ fn monitor(f: proc():Send) {
|
|||||||
format!("failed to read internal \
|
format!("failed to read internal \
|
||||||
stderr: {}",
|
stderr: {}",
|
||||||
e).as_slice(),
|
e).as_slice(),
|
||||||
|
None,
|
||||||
diagnostic::Error)
|
diagnostic::Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use util::nodemap::NodeMap;
|
|||||||
use syntax::ast::NodeId;
|
use syntax::ast::NodeId;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::diagnostic;
|
use syntax::diagnostic;
|
||||||
|
use syntax::diagnostics;
|
||||||
use syntax::parse;
|
use syntax::parse;
|
||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
use syntax::parse::ParseSess;
|
use syntax::parse::ParseSess;
|
||||||
@@ -65,6 +66,9 @@ impl Session {
|
|||||||
pub fn span_err(&self, sp: Span, msg: &str) {
|
pub fn span_err(&self, sp: Span, msg: &str) {
|
||||||
self.diagnostic().span_err(sp, msg)
|
self.diagnostic().span_err(sp, msg)
|
||||||
}
|
}
|
||||||
|
pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
|
||||||
|
self.diagnostic().span_err_with_code(sp, msg, code)
|
||||||
|
}
|
||||||
pub fn err(&self, msg: &str) {
|
pub fn err(&self, msg: &str) {
|
||||||
self.diagnostic().handler().err(msg)
|
self.diagnostic().handler().err(msg)
|
||||||
}
|
}
|
||||||
@@ -197,11 +201,12 @@ impl Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_session(sopts: config::Options,
|
pub fn build_session(sopts: config::Options,
|
||||||
local_crate_source_file: Option<Path>)
|
local_crate_source_file: Option<Path>,
|
||||||
|
registry: diagnostics::registry::Registry)
|
||||||
-> Session {
|
-> Session {
|
||||||
let codemap = codemap::CodeMap::new();
|
let codemap = codemap::CodeMap::new();
|
||||||
let diagnostic_handler =
|
let diagnostic_handler =
|
||||||
diagnostic::default_handler(sopts.color);
|
diagnostic::default_handler(sopts.color, Some(registry));
|
||||||
let span_diagnostic_handler =
|
let span_diagnostic_handler =
|
||||||
diagnostic::mk_span_handler(diagnostic_handler, codemap);
|
diagnostic::mk_span_handler(diagnostic_handler, codemap);
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
|
|||||||
|
|
||||||
("quad_precision_float", Removed),
|
("quad_precision_float", Removed),
|
||||||
|
|
||||||
|
("rustc_diagnostic_macros", Active),
|
||||||
|
|
||||||
// A temporary feature gate used to enable parser extensions needed
|
// A temporary feature gate used to enable parser extensions needed
|
||||||
// to bootstrap fix for #5723.
|
// to bootstrap fix for #5723.
|
||||||
("issue_5723_bootstrap", Active),
|
("issue_5723_bootstrap", Active),
|
||||||
@@ -93,6 +95,7 @@ pub struct Features {
|
|||||||
pub default_type_params: Cell<bool>,
|
pub default_type_params: Cell<bool>,
|
||||||
pub issue_5723_bootstrap: Cell<bool>,
|
pub issue_5723_bootstrap: Cell<bool>,
|
||||||
pub overloaded_calls: Cell<bool>,
|
pub overloaded_calls: Cell<bool>,
|
||||||
|
pub rustc_diagnostic_macros: Cell<bool>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Features {
|
impl Features {
|
||||||
@@ -101,6 +104,7 @@ impl Features {
|
|||||||
default_type_params: Cell::new(false),
|
default_type_params: Cell::new(false),
|
||||||
issue_5723_bootstrap: Cell::new(false),
|
issue_5723_bootstrap: Cell::new(false),
|
||||||
overloaded_calls: Cell::new(false),
|
overloaded_calls: Cell::new(false),
|
||||||
|
rustc_diagnostic_macros: Cell::new(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,4 +429,5 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) {
|
|||||||
sess.features.default_type_params.set(cx.has_feature("default_type_params"));
|
sess.features.default_type_params.set(cx.has_feature("default_type_params"));
|
||||||
sess.features.issue_5723_bootstrap.set(cx.has_feature("issue_5723_bootstrap"));
|
sess.features.issue_5723_bootstrap.set(cx.has_feature("issue_5723_bootstrap"));
|
||||||
sess.features.overloaded_calls.set(cx.has_feature("overloaded_calls"));
|
sess.features.overloaded_calls.set(cx.has_feature("overloaded_calls"));
|
||||||
|
sess.features.rustc_diagnostic_macros.set(cx.has_feature("rustc_diagnostic_macros"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ This API is completely unstable and subject to change.
|
|||||||
#![feature(macro_rules, globs, struct_variant, managed_boxes, quote)]
|
#![feature(macro_rules, globs, struct_variant, managed_boxes, quote)]
|
||||||
#![feature(default_type_params, phase, unsafe_destructor)]
|
#![feature(default_type_params, phase, unsafe_destructor)]
|
||||||
|
|
||||||
|
#![allow(unknown_features)] // NOTE: Remove after next snapshot
|
||||||
|
#![feature(rustc_diagnostic_macros)]
|
||||||
|
|
||||||
extern crate arena;
|
extern crate arena;
|
||||||
extern crate debug;
|
extern crate debug;
|
||||||
extern crate flate;
|
extern crate flate;
|
||||||
@@ -39,9 +42,11 @@ extern crate getopts;
|
|||||||
extern crate graphviz;
|
extern crate graphviz;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate serialize;
|
extern crate serialize;
|
||||||
extern crate syntax;
|
|
||||||
extern crate time;
|
extern crate time;
|
||||||
#[phase(plugin, link)] extern crate log;
|
#[phase(plugin, link)] extern crate log;
|
||||||
|
#[phase(plugin, link)] extern crate syntax;
|
||||||
|
|
||||||
|
mod diagnostics;
|
||||||
|
|
||||||
pub mod middle {
|
pub mod middle {
|
||||||
pub mod def;
|
pub mod def;
|
||||||
@@ -127,6 +132,8 @@ pub mod lib {
|
|||||||
pub mod llvmdeps;
|
pub mod llvmdeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__build_diagnostic_array!(DIAGNOSTICS)
|
||||||
|
|
||||||
// A private module so that macro-expanded idents like
|
// A private module so that macro-expanded idents like
|
||||||
// `::rustc::lint::Lint` will also work in `rustc` itself.
|
// `::rustc::lint::Lint` will also work in `rustc` itself.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
|
|||||||
|
|
||||||
let v = vec!(*pat);
|
let v = vec!(*pat);
|
||||||
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
|
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
|
||||||
NotUseful => cx.tcx.sess.span_err(pat.span, "unreachable pattern"),
|
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
|
||||||
Useful => (),
|
Useful => (),
|
||||||
UsefulWithWitness(_) => unreachable!()
|
UsefulWithWitness(_) => unreachable!()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ impl Emitter for ExpectErrorEmitter {
|
|||||||
fn emit(&mut self,
|
fn emit(&mut self,
|
||||||
_cmsp: Option<(&codemap::CodeMap, Span)>,
|
_cmsp: Option<(&codemap::CodeMap, Span)>,
|
||||||
msg: &str,
|
msg: &str,
|
||||||
|
_: Option<&str>,
|
||||||
lvl: Level)
|
lvl: Level)
|
||||||
{
|
{
|
||||||
remove_message(self, msg, lvl);
|
remove_message(self, msg, lvl);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
|
|||||||
|
|
||||||
|
|
||||||
let codemap = syntax::codemap::CodeMap::new();
|
let codemap = syntax::codemap::CodeMap::new();
|
||||||
let diagnostic_handler = syntax::diagnostic::default_handler(syntax::diagnostic::Auto);
|
let diagnostic_handler = syntax::diagnostic::default_handler(syntax::diagnostic::Auto, None);
|
||||||
let span_diagnostic_handler =
|
let span_diagnostic_handler =
|
||||||
syntax::diagnostic::mk_span_handler(diagnostic_handler, codemap);
|
syntax::diagnostic::mk_span_handler(diagnostic_handler, codemap);
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ pub fn run(input: &str,
|
|||||||
|
|
||||||
|
|
||||||
let codemap = CodeMap::new();
|
let codemap = CodeMap::new();
|
||||||
let diagnostic_handler = diagnostic::default_handler(diagnostic::Auto);
|
let diagnostic_handler = diagnostic::default_handler(diagnostic::Auto, None);
|
||||||
let span_diagnostic_handler =
|
let span_diagnostic_handler =
|
||||||
diagnostic::mk_span_handler(diagnostic_handler, codemap);
|
diagnostic::mk_span_handler(diagnostic_handler, codemap);
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool,
|
|||||||
};
|
};
|
||||||
io::util::copy(&mut p, &mut err).unwrap();
|
io::util::copy(&mut p, &mut err).unwrap();
|
||||||
});
|
});
|
||||||
let emitter = diagnostic::EmitterWriter::new(box w2);
|
let emitter = diagnostic::EmitterWriter::new(box w2, None);
|
||||||
|
|
||||||
// Compile the code
|
// Compile the code
|
||||||
let codemap = CodeMap::new();
|
let codemap = CodeMap::new();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ extern crate libc;
|
|||||||
|
|
||||||
use codemap::{Pos, Span};
|
use codemap::{Pos, Span};
|
||||||
use codemap;
|
use codemap;
|
||||||
|
use diagnostics;
|
||||||
|
|
||||||
use std::cell::{RefCell, Cell};
|
use std::cell::{RefCell, Cell};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -59,7 +60,7 @@ pub enum ColorConfig {
|
|||||||
|
|
||||||
pub trait Emitter {
|
pub trait Emitter {
|
||||||
fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, Span)>,
|
fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, Span)>,
|
||||||
msg: &str, lvl: Level);
|
msg: &str, code: Option<&str>, lvl: Level);
|
||||||
fn custom_emit(&mut self, cm: &codemap::CodeMap,
|
fn custom_emit(&mut self, cm: &codemap::CodeMap,
|
||||||
sp: RenderSpan, msg: &str, lvl: Level);
|
sp: RenderSpan, msg: &str, lvl: Level);
|
||||||
}
|
}
|
||||||
@@ -90,6 +91,10 @@ impl SpanHandler {
|
|||||||
self.handler.emit(Some((&self.cm, sp)), msg, Error);
|
self.handler.emit(Some((&self.cm, sp)), msg, Error);
|
||||||
self.handler.bump_err_count();
|
self.handler.bump_err_count();
|
||||||
}
|
}
|
||||||
|
pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
|
||||||
|
self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Error);
|
||||||
|
self.handler.bump_err_count();
|
||||||
|
}
|
||||||
pub fn span_warn(&self, sp: Span, msg: &str) {
|
pub fn span_warn(&self, sp: Span, msg: &str) {
|
||||||
self.handler.emit(Some((&self.cm, sp)), msg, Warning);
|
self.handler.emit(Some((&self.cm, sp)), msg, Warning);
|
||||||
}
|
}
|
||||||
@@ -124,11 +129,11 @@ pub struct Handler {
|
|||||||
|
|
||||||
impl Handler {
|
impl Handler {
|
||||||
pub fn fatal(&self, msg: &str) -> ! {
|
pub fn fatal(&self, msg: &str) -> ! {
|
||||||
self.emit.borrow_mut().emit(None, msg, Fatal);
|
self.emit.borrow_mut().emit(None, msg, None, Fatal);
|
||||||
fail!(FatalError);
|
fail!(FatalError);
|
||||||
}
|
}
|
||||||
pub fn err(&self, msg: &str) {
|
pub fn err(&self, msg: &str) {
|
||||||
self.emit.borrow_mut().emit(None, msg, Error);
|
self.emit.borrow_mut().emit(None, msg, None, Error);
|
||||||
self.bump_err_count();
|
self.bump_err_count();
|
||||||
}
|
}
|
||||||
pub fn bump_err_count(&self) {
|
pub fn bump_err_count(&self) {
|
||||||
@@ -153,13 +158,13 @@ impl Handler {
|
|||||||
self.fatal(s.as_slice());
|
self.fatal(s.as_slice());
|
||||||
}
|
}
|
||||||
pub fn warn(&self, msg: &str) {
|
pub fn warn(&self, msg: &str) {
|
||||||
self.emit.borrow_mut().emit(None, msg, Warning);
|
self.emit.borrow_mut().emit(None, msg, None, Warning);
|
||||||
}
|
}
|
||||||
pub fn note(&self, msg: &str) {
|
pub fn note(&self, msg: &str) {
|
||||||
self.emit.borrow_mut().emit(None, msg, Note);
|
self.emit.borrow_mut().emit(None, msg, None, Note);
|
||||||
}
|
}
|
||||||
pub fn bug(&self, msg: &str) -> ! {
|
pub fn bug(&self, msg: &str) -> ! {
|
||||||
self.emit.borrow_mut().emit(None, msg, Bug);
|
self.emit.borrow_mut().emit(None, msg, None, Bug);
|
||||||
fail!(ExplicitBug);
|
fail!(ExplicitBug);
|
||||||
}
|
}
|
||||||
pub fn unimpl(&self, msg: &str) -> ! {
|
pub fn unimpl(&self, msg: &str) -> ! {
|
||||||
@@ -169,7 +174,14 @@ impl Handler {
|
|||||||
cmsp: Option<(&codemap::CodeMap, Span)>,
|
cmsp: Option<(&codemap::CodeMap, Span)>,
|
||||||
msg: &str,
|
msg: &str,
|
||||||
lvl: Level) {
|
lvl: Level) {
|
||||||
self.emit.borrow_mut().emit(cmsp, msg, lvl);
|
self.emit.borrow_mut().emit(cmsp, msg, None, lvl);
|
||||||
|
}
|
||||||
|
pub fn emit_with_code(&self,
|
||||||
|
cmsp: Option<(&codemap::CodeMap, Span)>,
|
||||||
|
msg: &str,
|
||||||
|
code: &str,
|
||||||
|
lvl: Level) {
|
||||||
|
self.emit.borrow_mut().emit(cmsp, msg, Some(code), lvl);
|
||||||
}
|
}
|
||||||
pub fn custom_emit(&self, cm: &codemap::CodeMap,
|
pub fn custom_emit(&self, cm: &codemap::CodeMap,
|
||||||
sp: RenderSpan, msg: &str, lvl: Level) {
|
sp: RenderSpan, msg: &str, lvl: Level) {
|
||||||
@@ -184,8 +196,9 @@ pub fn mk_span_handler(handler: Handler, cm: codemap::CodeMap) -> SpanHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_handler(color_config: ColorConfig) -> Handler {
|
pub fn default_handler(color_config: ColorConfig,
|
||||||
mk_handler(box EmitterWriter::stderr(color_config))
|
registry: Option<diagnostics::registry::Registry>) -> Handler {
|
||||||
|
mk_handler(box EmitterWriter::stderr(color_config, registry))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mk_handler(e: Box<Emitter + Send>) -> Handler {
|
pub fn mk_handler(e: Box<Emitter + Send>) -> Handler {
|
||||||
@@ -262,8 +275,8 @@ fn print_maybe_styled(w: &mut EmitterWriter,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_diagnostic(dst: &mut EmitterWriter,
|
fn print_diagnostic(dst: &mut EmitterWriter, topic: &str, lvl: Level,
|
||||||
topic: &str, lvl: Level, msg: &str) -> io::IoResult<()> {
|
msg: &str, code: Option<&str>) -> io::IoResult<()> {
|
||||||
if !topic.is_empty() {
|
if !topic.is_empty() {
|
||||||
try!(write!(&mut dst.dst, "{} ", topic));
|
try!(write!(&mut dst.dst, "{} ", topic));
|
||||||
}
|
}
|
||||||
@@ -272,13 +285,32 @@ fn print_diagnostic(dst: &mut EmitterWriter,
|
|||||||
format!("{}: ", lvl.to_string()).as_slice(),
|
format!("{}: ", lvl.to_string()).as_slice(),
|
||||||
term::attr::ForegroundColor(lvl.color())));
|
term::attr::ForegroundColor(lvl.color())));
|
||||||
try!(print_maybe_styled(dst,
|
try!(print_maybe_styled(dst,
|
||||||
format!("{}\n", msg).as_slice(),
|
format!("{}", msg).as_slice(),
|
||||||
term::attr::Bold));
|
term::attr::Bold));
|
||||||
|
|
||||||
|
match code {
|
||||||
|
Some(code) => {
|
||||||
|
let style = term::attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
|
||||||
|
try!(print_maybe_styled(dst, format!(" [{}]", code.clone()).as_slice(), style));
|
||||||
|
match dst.registry.as_ref().and_then(|registry| registry.find_description(code)) {
|
||||||
|
Some(_) => {
|
||||||
|
try!(write!(&mut dst.dst,
|
||||||
|
" (pass `--explain {}` to see a detailed explanation)",
|
||||||
|
code
|
||||||
|
));
|
||||||
|
}
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
try!(dst.dst.write_char('\n'));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EmitterWriter {
|
pub struct EmitterWriter {
|
||||||
dst: Destination,
|
dst: Destination,
|
||||||
|
registry: Option<diagnostics::registry::Registry>
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Destination {
|
enum Destination {
|
||||||
@@ -287,7 +319,8 @@ enum Destination {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EmitterWriter {
|
impl EmitterWriter {
|
||||||
pub fn stderr(color_config: ColorConfig) -> EmitterWriter {
|
pub fn stderr(color_config: ColorConfig,
|
||||||
|
registry: Option<diagnostics::registry::Registry>) -> EmitterWriter {
|
||||||
let stderr = io::stderr();
|
let stderr = io::stderr();
|
||||||
|
|
||||||
let use_color = match color_config {
|
let use_color = match color_config {
|
||||||
@@ -301,14 +334,15 @@ impl EmitterWriter {
|
|||||||
Some(t) => Terminal(t),
|
Some(t) => Terminal(t),
|
||||||
None => Raw(box stderr),
|
None => Raw(box stderr),
|
||||||
};
|
};
|
||||||
EmitterWriter { dst: dst }
|
EmitterWriter { dst: dst, registry: registry }
|
||||||
} else {
|
} else {
|
||||||
EmitterWriter { dst: Raw(box stderr) }
|
EmitterWriter { dst: Raw(box stderr), registry: registry }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(dst: Box<Writer + Send>) -> EmitterWriter {
|
pub fn new(dst: Box<Writer + Send>,
|
||||||
EmitterWriter { dst: Raw(dst) }
|
registry: Option<diagnostics::registry::Registry>) -> EmitterWriter {
|
||||||
|
EmitterWriter { dst: Raw(dst), registry: registry }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,11 +358,10 @@ impl Writer for Destination {
|
|||||||
impl Emitter for EmitterWriter {
|
impl Emitter for EmitterWriter {
|
||||||
fn emit(&mut self,
|
fn emit(&mut self,
|
||||||
cmsp: Option<(&codemap::CodeMap, Span)>,
|
cmsp: Option<(&codemap::CodeMap, Span)>,
|
||||||
msg: &str,
|
msg: &str, code: Option<&str>, lvl: Level) {
|
||||||
lvl: Level) {
|
|
||||||
let error = match cmsp {
|
let error = match cmsp {
|
||||||
Some((cm, sp)) => emit(self, cm, FullSpan(sp), msg, lvl, false),
|
Some((cm, sp)) => emit(self, cm, FullSpan(sp), msg, code, lvl, false),
|
||||||
None => print_diagnostic(self, "", lvl, msg),
|
None => print_diagnostic(self, "", lvl, msg, code),
|
||||||
};
|
};
|
||||||
|
|
||||||
match error {
|
match error {
|
||||||
@@ -339,7 +372,7 @@ impl Emitter for EmitterWriter {
|
|||||||
|
|
||||||
fn custom_emit(&mut self, cm: &codemap::CodeMap,
|
fn custom_emit(&mut self, cm: &codemap::CodeMap,
|
||||||
sp: RenderSpan, msg: &str, lvl: Level) {
|
sp: RenderSpan, msg: &str, lvl: Level) {
|
||||||
match emit(self, cm, sp, msg, lvl, true) {
|
match emit(self, cm, sp, msg, None, lvl, true) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(e) => fail!("failed to print diagnostics: {}", e),
|
Err(e) => fail!("failed to print diagnostics: {}", e),
|
||||||
}
|
}
|
||||||
@@ -347,7 +380,7 @@ impl Emitter for EmitterWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
|
fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
|
||||||
msg: &str, lvl: Level, custom: bool) -> io::IoResult<()> {
|
msg: &str, code: Option<&str>, lvl: Level, custom: bool) -> io::IoResult<()> {
|
||||||
let sp = rsp.span();
|
let sp = rsp.span();
|
||||||
let ss = cm.span_to_string(sp);
|
let ss = cm.span_to_string(sp);
|
||||||
let lines = cm.span_to_lines(sp);
|
let lines = cm.span_to_lines(sp);
|
||||||
@@ -357,12 +390,12 @@ fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
|
|||||||
// the span)
|
// the span)
|
||||||
let span_end = Span { lo: sp.hi, hi: sp.hi, expn_info: sp.expn_info};
|
let span_end = Span { lo: sp.hi, hi: sp.hi, expn_info: sp.expn_info};
|
||||||
let ses = cm.span_to_string(span_end);
|
let ses = cm.span_to_string(span_end);
|
||||||
try!(print_diagnostic(dst, ses.as_slice(), lvl, msg));
|
try!(print_diagnostic(dst, ses.as_slice(), lvl, msg, code));
|
||||||
if rsp.is_full_span() {
|
if rsp.is_full_span() {
|
||||||
try!(custom_highlight_lines(dst, cm, sp, lvl, lines));
|
try!(custom_highlight_lines(dst, cm, sp, lvl, lines));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try!(print_diagnostic(dst, ss.as_slice(), lvl, msg));
|
try!(print_diagnostic(dst, ss.as_slice(), lvl, msg, code));
|
||||||
if rsp.is_full_span() {
|
if rsp.is_full_span() {
|
||||||
try!(highlight_lines(dst, cm, sp, lvl, lines));
|
try!(highlight_lines(dst, cm, sp, lvl, lines));
|
||||||
}
|
}
|
||||||
@@ -501,9 +534,9 @@ fn print_macro_backtrace(w: &mut EmitterWriter,
|
|||||||
try!(print_diagnostic(w, ss.as_slice(), Note,
|
try!(print_diagnostic(w, ss.as_slice(), Note,
|
||||||
format!("in expansion of {}{}{}", pre,
|
format!("in expansion of {}{}{}", pre,
|
||||||
ei.callee.name,
|
ei.callee.name,
|
||||||
post).as_slice()));
|
post).as_slice(), None));
|
||||||
let ss = cm.span_to_string(ei.call_site);
|
let ss = cm.span_to_string(ei.call_site);
|
||||||
try!(print_diagnostic(w, ss.as_slice(), Note, "expansion site"));
|
try!(print_diagnostic(w, ss.as_slice(), Note, "expansion site", None));
|
||||||
try!(print_macro_backtrace(w, cm, ei.call_site));
|
try!(print_macro_backtrace(w, cm, ei.call_site));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
51
src/libsyntax/diagnostics/macros.rs
Normal file
51
src/libsyntax/diagnostics/macros.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
#![macro_escape]
|
||||||
|
|
||||||
|
// NOTE: remove after next snapshot
|
||||||
|
#[cfg(stage0)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! __register_diagnostic(
|
||||||
|
($code:tt, $description:tt) => ();
|
||||||
|
($code:tt) => ()
|
||||||
|
)
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! register_diagnostic(
|
||||||
|
($code:tt, $description:tt) => (__register_diagnostic!($code, $description));
|
||||||
|
($code:tt) => (__register_diagnostic!($code))
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: remove after next snapshot
|
||||||
|
#[cfg(stage0)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! __build_diagnostic_array(
|
||||||
|
($name:ident) => {
|
||||||
|
pub static $name: [(&'static str, &'static str), ..0] = [];
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: remove after next snapshot
|
||||||
|
#[cfg(stage0)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! __diagnostic_used(
|
||||||
|
($code:ident) => {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! span_err(
|
||||||
|
($session:expr, $span:expr, $code:ident, $($arg:expr),*) => ({
|
||||||
|
__diagnostic_used!($code);
|
||||||
|
($session).span_err_with_code($span, format!($($arg),*).as_slice(), stringify!($code))
|
||||||
|
})
|
||||||
|
)
|
||||||
132
src/libsyntax/diagnostics/plugin.rs
Normal file
132
src/libsyntax/diagnostics/plugin.rs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::gc::Gc;
|
||||||
|
use ast;
|
||||||
|
use ast::{Ident, Name, TokenTree};
|
||||||
|
use codemap::Span;
|
||||||
|
use ext::base::{ExtCtxt, MacExpr, MacItem, MacResult};
|
||||||
|
use ext::build::AstBuilder;
|
||||||
|
use parse::token;
|
||||||
|
|
||||||
|
local_data_key!(registered_diagnostics: RefCell<HashMap<Name, Option<Name>>>)
|
||||||
|
local_data_key!(used_diagnostics: RefCell<HashMap<Name, Span>>)
|
||||||
|
|
||||||
|
fn with_registered_diagnostics<T>(f: |&mut HashMap<Name, Option<Name>>| -> T) -> T {
|
||||||
|
match registered_diagnostics.get() {
|
||||||
|
Some(cell) => f(cell.borrow_mut().deref_mut()),
|
||||||
|
None => {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
let value = f(&mut map);
|
||||||
|
registered_diagnostics.replace(Some(RefCell::new(map)));
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_used_diagnostics<T>(f: |&mut HashMap<Name, Span>| -> T) -> T {
|
||||||
|
match used_diagnostics.get() {
|
||||||
|
Some(cell) => f(cell.borrow_mut().deref_mut()),
|
||||||
|
None => {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
let value = f(&mut map);
|
||||||
|
used_diagnostics.replace(Some(RefCell::new(map)));
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_diagnostic_used(ecx: &mut ExtCtxt, span: Span,
|
||||||
|
token_tree: &[TokenTree]) -> Box<MacResult> {
|
||||||
|
let code = match token_tree {
|
||||||
|
[ast::TTTok(_, token::IDENT(code, _))] => code,
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
with_registered_diagnostics(|diagnostics| {
|
||||||
|
if !diagnostics.contains_key(&code.name) {
|
||||||
|
ecx.span_err(span, format!(
|
||||||
|
"unknown diagnostic code {}", token::get_ident(code).get()
|
||||||
|
).as_slice());
|
||||||
|
}
|
||||||
|
()
|
||||||
|
});
|
||||||
|
with_used_diagnostics(|diagnostics| {
|
||||||
|
match diagnostics.swap(code.name, span) {
|
||||||
|
Some(previous_span) => {
|
||||||
|
ecx.span_warn(span, format!(
|
||||||
|
"diagnostic code {} already used", token::get_ident(code).get()
|
||||||
|
).as_slice());
|
||||||
|
ecx.span_note(previous_span, "previous invocation");
|
||||||
|
},
|
||||||
|
None => ()
|
||||||
|
}
|
||||||
|
()
|
||||||
|
});
|
||||||
|
MacExpr::new(quote_expr!(ecx, ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_register_diagnostic(ecx: &mut ExtCtxt, span: Span,
|
||||||
|
token_tree: &[TokenTree]) -> Box<MacResult> {
|
||||||
|
let (code, description) = match token_tree {
|
||||||
|
[ast::TTTok(_, token::IDENT(ref code, _))] => {
|
||||||
|
(code, None)
|
||||||
|
},
|
||||||
|
[ast::TTTok(_, token::IDENT(ref code, _)),
|
||||||
|
ast::TTTok(_, token::COMMA),
|
||||||
|
ast::TTTok(_, token::LIT_STR_RAW(description, _))] => {
|
||||||
|
(code, Some(description))
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
with_registered_diagnostics(|diagnostics| {
|
||||||
|
if !diagnostics.insert(code.name, description) {
|
||||||
|
ecx.span_err(span, format!(
|
||||||
|
"diagnostic code {} already registered", token::get_ident(*code).get()
|
||||||
|
).as_slice());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let sym = Ident::new(token::gensym((
|
||||||
|
"__register_diagnostic_".to_string() + token::get_ident(*code).get()
|
||||||
|
).as_slice()));
|
||||||
|
MacItem::new(quote_item!(ecx, mod $sym {}).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_build_diagnostic_array(ecx: &mut ExtCtxt, span: Span,
|
||||||
|
token_tree: &[TokenTree]) -> Box<MacResult> {
|
||||||
|
let name = match token_tree {
|
||||||
|
[ast::TTTok(_, token::IDENT(ref name, _))] => name,
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let (count, expr) = with_used_diagnostics(|diagnostics_in_use| {
|
||||||
|
with_registered_diagnostics(|diagnostics| {
|
||||||
|
let descriptions: Vec<Gc<ast::Expr>> = diagnostics
|
||||||
|
.iter().filter_map(|(code, description)| {
|
||||||
|
if !diagnostics_in_use.contains_key(code) {
|
||||||
|
ecx.span_warn(span, format!(
|
||||||
|
"diagnostic code {} never used", token::get_name(*code).get()
|
||||||
|
).as_slice());
|
||||||
|
}
|
||||||
|
description.map(|description| {
|
||||||
|
ecx.expr_tuple(span, vec![
|
||||||
|
ecx.expr_str(span, token::get_name(*code)),
|
||||||
|
ecx.expr_str(span, token::get_name(description))
|
||||||
|
])
|
||||||
|
})
|
||||||
|
}).collect();
|
||||||
|
(descriptions.len(), ecx.expr_vec(span, descriptions))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
MacItem::new(quote_item!(ecx,
|
||||||
|
pub static $name: [(&'static str, &'static str), ..$count] = $expr;
|
||||||
|
).unwrap())
|
||||||
|
}
|
||||||
25
src/libsyntax/diagnostics/registry.rs
Normal file
25
src/libsyntax/diagnostics/registry.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct Registry {
|
||||||
|
descriptions: HashMap<&'static str, &'static str>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Registry {
|
||||||
|
pub fn new(descriptions: &[(&'static str, &'static str)]) -> Registry {
|
||||||
|
Registry { descriptions: descriptions.iter().map(|&tuple| tuple).collect() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_description(&self, code: &str) -> Option<&'static str> {
|
||||||
|
self.descriptions.find_equiv(&code).map(|desc| *desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -148,6 +148,8 @@ pub trait AstBuilder {
|
|||||||
fn expr_some(&self, sp: Span, expr: Gc<ast::Expr>) -> Gc<ast::Expr>;
|
fn expr_some(&self, sp: Span, expr: Gc<ast::Expr>) -> Gc<ast::Expr>;
|
||||||
fn expr_none(&self, sp: Span) -> Gc<ast::Expr>;
|
fn expr_none(&self, sp: Span) -> Gc<ast::Expr>;
|
||||||
|
|
||||||
|
fn expr_tuple(&self, sp: Span, exprs: Vec<Gc<ast::Expr>>) -> Gc<ast::Expr>;
|
||||||
|
|
||||||
fn expr_fail(&self, span: Span, msg: InternedString) -> Gc<ast::Expr>;
|
fn expr_fail(&self, span: Span, msg: InternedString) -> Gc<ast::Expr>;
|
||||||
fn expr_unreachable(&self, span: Span) -> Gc<ast::Expr>;
|
fn expr_unreachable(&self, span: Span) -> Gc<ast::Expr>;
|
||||||
|
|
||||||
@@ -674,6 +676,10 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
|||||||
self.expr_path(none)
|
self.expr_path(none)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expr_tuple(&self, sp: Span, exprs: Vec<Gc<ast::Expr>>) -> Gc<ast::Expr> {
|
||||||
|
self.expr(sp, ast::ExprTup(exprs))
|
||||||
|
}
|
||||||
|
|
||||||
fn expr_fail(&self, span: Span, msg: InternedString) -> Gc<ast::Expr> {
|
fn expr_fail(&self, span: Span, msg: InternedString) -> Gc<ast::Expr> {
|
||||||
let loc = self.codemap().lookup_char_pos(span.lo);
|
let loc = self.codemap().lookup_char_pos(span.lo);
|
||||||
self.expr_call_global(
|
self.expr_call_global(
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ pub fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
|
|||||||
None => {
|
None => {
|
||||||
fld.cx.span_err(
|
fld.cx.span_err(
|
||||||
pth.span,
|
pth.span,
|
||||||
format!("macro undefined: '{}'",
|
format!("macro undefined: '{}!'",
|
||||||
extnamestr.get()).as_slice());
|
extnamestr.get()).as_slice());
|
||||||
|
|
||||||
// let compilation continue
|
// let compilation continue
|
||||||
@@ -567,7 +567,7 @@ fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<Gc<Stmt>> {
|
|||||||
let marked_after = match fld.extsbox.find(&extname.name) {
|
let marked_after = match fld.extsbox.find(&extname.name) {
|
||||||
None => {
|
None => {
|
||||||
fld.cx.span_err(pth.span,
|
fld.cx.span_err(pth.span,
|
||||||
format!("macro undefined: '{}'",
|
format!("macro undefined: '{}!'",
|
||||||
extnamestr).as_slice());
|
extnamestr).as_slice());
|
||||||
return SmallVector::zero();
|
return SmallVector::zero();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,11 @@
|
|||||||
#![feature(quote, unsafe_destructor)]
|
#![feature(quote, unsafe_destructor)]
|
||||||
#![allow(deprecated)]
|
#![allow(deprecated)]
|
||||||
|
|
||||||
extern crate serialize;
|
|
||||||
extern crate term;
|
|
||||||
#[phase(plugin, link)] extern crate log;
|
|
||||||
|
|
||||||
extern crate fmt_macros;
|
extern crate fmt_macros;
|
||||||
extern crate debug;
|
extern crate debug;
|
||||||
|
#[phase(plugin, link)] extern crate log;
|
||||||
|
extern crate serialize;
|
||||||
|
extern crate term;
|
||||||
|
|
||||||
pub mod util {
|
pub mod util {
|
||||||
pub mod interner;
|
pub mod interner;
|
||||||
@@ -41,26 +40,30 @@ pub mod util {
|
|||||||
pub mod small_vector;
|
pub mod small_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod diagnostics {
|
||||||
|
pub mod macros;
|
||||||
|
pub mod plugin;
|
||||||
|
pub mod registry;
|
||||||
|
}
|
||||||
|
|
||||||
pub mod syntax {
|
pub mod syntax {
|
||||||
pub use ext;
|
pub use ext;
|
||||||
pub use parse;
|
pub use parse;
|
||||||
pub use ast;
|
pub use ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod owned_slice;
|
|
||||||
pub mod attr;
|
|
||||||
pub mod diagnostic;
|
|
||||||
pub mod codemap;
|
|
||||||
pub mod abi;
|
pub mod abi;
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod ast_util;
|
|
||||||
pub mod ast_map;
|
pub mod ast_map;
|
||||||
pub mod visit;
|
pub mod ast_util;
|
||||||
pub mod fold;
|
pub mod attr;
|
||||||
|
pub mod codemap;
|
||||||
|
|
||||||
pub mod parse;
|
|
||||||
pub mod crateid;
|
pub mod crateid;
|
||||||
|
pub mod diagnostic;
|
||||||
|
pub mod fold;
|
||||||
|
pub mod owned_slice;
|
||||||
|
pub mod parse;
|
||||||
|
pub mod visit;
|
||||||
|
|
||||||
pub mod print {
|
pub mod print {
|
||||||
pub mod pp;
|
pub mod pp;
|
||||||
@@ -70,31 +73,25 @@ pub mod print {
|
|||||||
pub mod ext {
|
pub mod ext {
|
||||||
pub mod asm;
|
pub mod asm;
|
||||||
pub mod base;
|
pub mod base;
|
||||||
pub mod expand;
|
|
||||||
|
|
||||||
pub mod quote;
|
|
||||||
|
|
||||||
pub mod deriving;
|
|
||||||
|
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
pub mod bytes;
|
||||||
|
pub mod cfg;
|
||||||
|
pub mod concat;
|
||||||
|
pub mod concat_idents;
|
||||||
|
pub mod deriving;
|
||||||
|
pub mod env;
|
||||||
|
pub mod expand;
|
||||||
|
pub mod fmt;
|
||||||
|
pub mod format;
|
||||||
|
pub mod log_syntax;
|
||||||
|
pub mod mtwt;
|
||||||
|
pub mod quote;
|
||||||
|
pub mod source_util;
|
||||||
|
pub mod trace_macros;
|
||||||
|
|
||||||
pub mod tt {
|
pub mod tt {
|
||||||
pub mod transcribe;
|
pub mod transcribe;
|
||||||
pub mod macro_parser;
|
pub mod macro_parser;
|
||||||
pub mod macro_rules;
|
pub mod macro_rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod mtwt;
|
|
||||||
|
|
||||||
pub mod cfg;
|
|
||||||
pub mod fmt;
|
|
||||||
pub mod format;
|
|
||||||
pub mod env;
|
|
||||||
pub mod bytes;
|
|
||||||
pub mod concat;
|
|
||||||
pub mod concat_idents;
|
|
||||||
pub mod log_syntax;
|
|
||||||
pub mod source_util;
|
|
||||||
|
|
||||||
pub mod trace_macros;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1308,7 +1308,7 @@ mod test {
|
|||||||
use std::io::util;
|
use std::io::util;
|
||||||
|
|
||||||
fn mk_sh() -> diagnostic::SpanHandler {
|
fn mk_sh() -> diagnostic::SpanHandler {
|
||||||
let emitter = diagnostic::EmitterWriter::new(box util::NullWriter);
|
let emitter = diagnostic::EmitterWriter::new(box util::NullWriter, None);
|
||||||
let handler = diagnostic::mk_handler(box emitter);
|
let handler = diagnostic::mk_handler(box emitter);
|
||||||
diagnostic::mk_span_handler(handler, CodeMap::new())
|
diagnostic::mk_span_handler(handler, CodeMap::new())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub struct ParseSess {
|
|||||||
|
|
||||||
pub fn new_parse_sess() -> ParseSess {
|
pub fn new_parse_sess() -> ParseSess {
|
||||||
ParseSess {
|
ParseSess {
|
||||||
span_diagnostic: mk_span_handler(default_handler(Auto), CodeMap::new()),
|
span_diagnostic: mk_span_handler(default_handler(Auto, None), CodeMap::new()),
|
||||||
included_mod_stack: RefCell::new(Vec::new()),
|
included_mod_stack: RefCell::new(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,5 @@
|
|||||||
extern crate macro_crate_test;
|
extern crate macro_crate_test;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
assert_eq!(3, unexported_macro!()); //~ ERROR macro undefined: 'unexported_macro'
|
assert_eq!(3, unexported_macro!()); //~ ERROR macro undefined: 'unexported_macro!'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
print!(test!());
|
print!(test!());
|
||||||
//~^ ERROR: macro undefined: 'test'
|
//~^ ERROR: macro undefined: 'test!'
|
||||||
//~^^ ERROR: format argument must be a string literal
|
//~^^ ERROR: format argument must be a string literal
|
||||||
|
|
||||||
concat!(test!());
|
concat!(test!());
|
||||||
//~^ ERROR: macro undefined: 'test'
|
//~^ ERROR: macro undefined: 'test!'
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/test/compile-fail/rustc-diagnostics-1.rs
Normal file
28
src/test/compile-fail/rustc-diagnostics-1.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
#![feature(rustc_diagnostic_macros)]
|
||||||
|
|
||||||
|
__register_diagnostic!(E0001)
|
||||||
|
__register_diagnostic!(E0003)
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
__diagnostic_used!(E0002);
|
||||||
|
//~^ ERROR unknown diagnostic code E0002
|
||||||
|
|
||||||
|
__diagnostic_used!(E0001);
|
||||||
|
//~^ NOTE previous invocation
|
||||||
|
|
||||||
|
__diagnostic_used!(E0001);
|
||||||
|
//~^ WARNING diagnostic code E0001 already used
|
||||||
|
}
|
||||||
|
|
||||||
|
__build_diagnostic_array!(DIAGNOSTICS)
|
||||||
|
//~^ WARN diagnostic code E0003 never used
|
||||||
20
src/test/compile-fail/rustc-diagnostics-2.rs
Normal file
20
src/test/compile-fail/rustc-diagnostics-2.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
#![feature(rustc_diagnostic_macros)]
|
||||||
|
|
||||||
|
__register_diagnostic!(E0001)
|
||||||
|
__register_diagnostic!(E0001)
|
||||||
|
//~^ ERROR diagnostic code E0001 already registered
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
||||||
|
|
||||||
|
__build_diagnostic_array!(DIAGNOSTICS)
|
||||||
20
src/test/compile-fail/rustc-diagnostics-3.rs
Normal file
20
src/test/compile-fail/rustc-diagnostics-3.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
__register_diagnostic!(E0001)
|
||||||
|
//~^ ERROR macro undefined: '__register_diagnostic!'
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
__diagnostic_used!(E0001);
|
||||||
|
//~^ ERROR macro undefined: '__diagnostic_used!'
|
||||||
|
}
|
||||||
|
|
||||||
|
__build_diagnostic_array!(DIAGNOSTICS)
|
||||||
|
//~^ ERROR macro undefined: '__build_diagnostic_array!'
|
||||||
Reference in New Issue
Block a user