diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs new file mode 100644 index 0000000000000..0499795f890fd --- /dev/null +++ b/src/librustc/diagnostics.rs @@ -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 or the MIT license +// , 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. +"##) diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index 9daaa2792ebad..b726c50afe9d1 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -532,6 +532,7 @@ pub fn optgroups() -> Vec { optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"), optopt( "", "out-dir", "Write output to compiler-chosen filename in ", "DIR"), optflag("", "parse-only", "Parse only; do not compile, assemble, or link"), + optopt("", "explain", "Provide a detailed explanation of an error message", "OPT"), optflagopt("", "pretty", "Pretty-print the input instead of compiling; valid types are: `normal` (un-annotated source), @@ -807,6 +808,7 @@ mod test { use getopts::getopts; use syntax::attr; use syntax::attr::AttrMetaMethods; + use syntax::diagnostics; // When the user supplies --test we should implicitly supply --cfg test #[test] @@ -816,8 +818,9 @@ mod test { Ok(m) => m, Err(f) => fail!("test_switch_implies_cfg_test: {}", f) }; + let registry = diagnostics::registry::Registry::new([]); let sessopts = build_session_options(matches); - let sess = build_session(sessopts, None); + let sess = build_session(sessopts, None, registry); let cfg = build_configuration(&sess); assert!((attr::contains_name(cfg.as_slice(), "test"))); } @@ -834,8 +837,9 @@ mod test { fail!("test_switch_implies_cfg_test_unless_cfg_test: {}", f) } }; + let registry = diagnostics::registry::Registry::new([]); 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 mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test"))); assert!(test_items.next().is_some()); diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 6a016edcd2868..8173e2ab582a0 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -16,6 +16,7 @@ use driver::{PpmFlowGraph, PpmExpanded, PpmExpandedIdentified, PpmTyped}; use driver::{PpmIdentified}; use front; use lib::llvm::{ContextRef, ModuleRef}; +use lint; use metadata::common::LinkMeta; use metadata::creader; use middle::cfg; @@ -26,7 +27,7 @@ use middle; use plugin::load::Plugins; use plugin::registry::Registry; use plugin; -use lint; + use util::common::time; use util::ppaux; use util::nodemap::{NodeSet}; @@ -41,6 +42,7 @@ use std::io::MemReader; use syntax::ast; use syntax::attr; use syntax::attr::{AttrMetaMethods}; +use syntax::diagnostics; use syntax::parse; use syntax::parse::token; use syntax::print::{pp, pprust}; @@ -213,6 +215,15 @@ pub fn phase_2_configure_and_expand(sess: &Session, let mut registry = Registry::new(&krate); 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() { registrar(&mut registry); } diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs index ad0d8cac1e357..2614c9ebf98b0 100644 --- a/src/librustc/driver/mod.rs +++ b/src/librustc/driver/mod.rs @@ -26,6 +26,7 @@ use std::task::TaskBuilder; use syntax::ast; use syntax::parse; use syntax::diagnostic::Emitter; +use syntax::diagnostics; use getopts; @@ -49,8 +50,24 @@ fn run_compiler(args: &[String]) { Some(matches) => matches, 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() { 0u => { if sopts.describe_lints { @@ -75,7 +92,7 @@ fn run_compiler(args: &[String]) { _ => 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 odir = matches.opt_str("out-dir").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) -> ! { - let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto); - emitter.emit(None, msg, diagnostic::Fatal); + let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); + emitter.emit(None, msg, None, diagnostic::Fatal); fail!(diagnostic::FatalError); } pub fn early_warn(msg: &str) { - let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto); - emitter.emit(None, msg, diagnostic::Warning); + let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None); + emitter.emit(None, msg, None, diagnostic::Warning); } pub fn list_metadata(sess: &Session, path: &Path, @@ -429,7 +446,7 @@ fn monitor(f: proc():Send) { Err(value) => { // Task failed without emitting a fatal diagnostic if !value.is::() { - 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 // it wants to print. @@ -437,6 +454,7 @@ fn monitor(f: proc():Send) { emitter.emit( None, "unexpected failure", + None, diagnostic::Bug); } @@ -447,7 +465,7 @@ fn monitor(f: proc():Send) { "run with `RUST_BACKTRACE=1` for a backtrace".to_string(), ]; 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() { @@ -457,6 +475,7 @@ fn monitor(f: proc():Send) { format!("failed to read internal \ stderr: {}", e).as_slice(), + None, diagnostic::Error) } } diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 50f61f8f06a5b..8669a733f6134 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -20,6 +20,7 @@ use util::nodemap::NodeMap; use syntax::ast::NodeId; use syntax::codemap::Span; use syntax::diagnostic; +use syntax::diagnostics; use syntax::parse; use syntax::parse::token; use syntax::parse::ParseSess; @@ -65,6 +66,9 @@ impl Session { pub fn span_err(&self, sp: Span, msg: &str) { 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) { self.diagnostic().handler().err(msg) } @@ -197,11 +201,12 @@ impl Session { } pub fn build_session(sopts: config::Options, - local_crate_source_file: Option) + local_crate_source_file: Option, + registry: diagnostics::registry::Registry) -> Session { let codemap = codemap::CodeMap::new(); let diagnostic_handler = - diagnostic::default_handler(sopts.color); + diagnostic::default_handler(sopts.color, Some(registry)); let span_diagnostic_handler = diagnostic::mk_span_handler(diagnostic_handler, codemap); diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 931f0312f579c..8b92166388d86 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -66,6 +66,8 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("quad_precision_float", Removed), + ("rustc_diagnostic_macros", Active), + // A temporary feature gate used to enable parser extensions needed // to bootstrap fix for #5723. ("issue_5723_bootstrap", Active), @@ -93,6 +95,7 @@ pub struct Features { pub default_type_params: Cell, pub issue_5723_bootstrap: Cell, pub overloaded_calls: Cell, + pub rustc_diagnostic_macros: Cell } impl Features { @@ -101,6 +104,7 @@ impl Features { default_type_params: Cell::new(false), issue_5723_bootstrap: 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.issue_5723_bootstrap.set(cx.has_feature("issue_5723_bootstrap")); sess.features.overloaded_calls.set(cx.has_feature("overloaded_calls")); + sess.features.rustc_diagnostic_macros.set(cx.has_feature("rustc_diagnostic_macros")); } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 6e28d0cce3220..97ea425df9970 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -32,6 +32,9 @@ This API is completely unstable and subject to change. #![feature(macro_rules, globs, struct_variant, managed_boxes, quote)] #![feature(default_type_params, phase, unsafe_destructor)] +#![allow(unknown_features)] // NOTE: Remove after next snapshot +#![feature(rustc_diagnostic_macros)] + extern crate arena; extern crate debug; extern crate flate; @@ -39,9 +42,11 @@ extern crate getopts; extern crate graphviz; extern crate libc; extern crate serialize; -extern crate syntax; extern crate time; #[phase(plugin, link)] extern crate log; +#[phase(plugin, link)] extern crate syntax; + +mod diagnostics; pub mod middle { pub mod def; @@ -127,6 +132,8 @@ pub mod lib { pub mod llvmdeps; } +__build_diagnostic_array!(DIAGNOSTICS) + // A private module so that macro-expanded idents like // `::rustc::lint::Lint` will also work in `rustc` itself. // diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 599f5f4024f28..f31c247abd38c 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -194,7 +194,7 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) { let v = vec!(*pat); 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 => (), UsefulWithWitness(_) => unreachable!() } diff --git a/src/librustc/middle/typeck/infer/test.rs b/src/librustc/middle/typeck/infer/test.rs index e66dcd118c928..c06e40fce14f9 100644 --- a/src/librustc/middle/typeck/infer/test.rs +++ b/src/librustc/middle/typeck/infer/test.rs @@ -78,6 +78,7 @@ impl Emitter for ExpectErrorEmitter { fn emit(&mut self, _cmsp: Option<(&codemap::CodeMap, Span)>, msg: &str, + _: Option<&str>, lvl: Level) { remove_message(self, msg, lvl); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 9ef671ef2fcbb..e62c8b63a2940 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -101,7 +101,7 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet, cfgs: Vec) 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 = syntax::diagnostic::mk_span_handler(diagnostic_handler, codemap); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 18f8233178081..92983e5d31bc4 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -54,7 +54,7 @@ pub fn run(input: &str, 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 = diagnostic::mk_span_handler(diagnostic_handler, codemap); @@ -150,7 +150,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet, should_fail: bool, }; 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 let codemap = CodeMap::new(); diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs index e469f327ae8ba..9bb5eae2ed2d6 100644 --- a/src/libsyntax/diagnostic.rs +++ b/src/libsyntax/diagnostic.rs @@ -12,6 +12,7 @@ extern crate libc; use codemap::{Pos, Span}; use codemap; +use diagnostics; use std::cell::{RefCell, Cell}; use std::fmt; @@ -59,7 +60,7 @@ pub enum ColorConfig { pub trait Emitter { 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, sp: RenderSpan, msg: &str, lvl: Level); } @@ -90,6 +91,10 @@ impl SpanHandler { self.handler.emit(Some((&self.cm, sp)), msg, Error); 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) { self.handler.emit(Some((&self.cm, sp)), msg, Warning); } @@ -124,11 +129,11 @@ pub struct Handler { impl Handler { 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); } 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(); } pub fn bump_err_count(&self) { @@ -153,13 +158,13 @@ impl Handler { self.fatal(s.as_slice()); } 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) { - self.emit.borrow_mut().emit(None, msg, Note); + self.emit.borrow_mut().emit(None, msg, None, Note); } 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); } pub fn unimpl(&self, msg: &str) -> ! { @@ -169,7 +174,14 @@ impl Handler { cmsp: Option<(&codemap::CodeMap, Span)>, msg: &str, 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, 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 { - mk_handler(box EmitterWriter::stderr(color_config)) +pub fn default_handler(color_config: ColorConfig, + registry: Option) -> Handler { + mk_handler(box EmitterWriter::stderr(color_config, registry)) } pub fn mk_handler(e: Box) -> Handler { @@ -262,8 +275,8 @@ fn print_maybe_styled(w: &mut EmitterWriter, } } -fn print_diagnostic(dst: &mut EmitterWriter, - topic: &str, lvl: Level, msg: &str) -> io::IoResult<()> { +fn print_diagnostic(dst: &mut EmitterWriter, topic: &str, lvl: Level, + msg: &str, code: Option<&str>) -> io::IoResult<()> { if !topic.is_empty() { try!(write!(&mut dst.dst, "{} ", topic)); } @@ -272,13 +285,32 @@ fn print_diagnostic(dst: &mut EmitterWriter, format!("{}: ", lvl.to_string()).as_slice(), term::attr::ForegroundColor(lvl.color()))); try!(print_maybe_styled(dst, - format!("{}\n", msg).as_slice(), + format!("{}", msg).as_slice(), 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(()) } pub struct EmitterWriter { dst: Destination, + registry: Option } enum Destination { @@ -287,7 +319,8 @@ enum Destination { } impl EmitterWriter { - pub fn stderr(color_config: ColorConfig) -> EmitterWriter { + pub fn stderr(color_config: ColorConfig, + registry: Option) -> EmitterWriter { let stderr = io::stderr(); let use_color = match color_config { @@ -301,14 +334,15 @@ impl EmitterWriter { Some(t) => Terminal(t), None => Raw(box stderr), }; - EmitterWriter { dst: dst } + EmitterWriter { dst: dst, registry: registry } } else { - EmitterWriter { dst: Raw(box stderr) } + EmitterWriter { dst: Raw(box stderr), registry: registry } } } - pub fn new(dst: Box) -> EmitterWriter { - EmitterWriter { dst: Raw(dst) } + pub fn new(dst: Box, + registry: Option) -> EmitterWriter { + EmitterWriter { dst: Raw(dst), registry: registry } } } @@ -324,11 +358,10 @@ impl Writer for Destination { impl Emitter for EmitterWriter { fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, Span)>, - msg: &str, - lvl: Level) { + msg: &str, code: Option<&str>, lvl: Level) { let error = match cmsp { - Some((cm, sp)) => emit(self, cm, FullSpan(sp), msg, lvl, false), - None => print_diagnostic(self, "", lvl, msg), + Some((cm, sp)) => emit(self, cm, FullSpan(sp), msg, code, lvl, false), + None => print_diagnostic(self, "", lvl, msg, code), }; match error { @@ -339,7 +372,7 @@ impl Emitter for EmitterWriter { fn custom_emit(&mut self, cm: &codemap::CodeMap, sp: RenderSpan, msg: &str, lvl: Level) { - match emit(self, cm, sp, msg, lvl, true) { + match emit(self, cm, sp, msg, None, lvl, true) { Ok(()) => {} 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, - msg: &str, lvl: Level, custom: bool) -> io::IoResult<()> { + msg: &str, code: Option<&str>, lvl: Level, custom: bool) -> io::IoResult<()> { let sp = rsp.span(); let ss = cm.span_to_string(sp); let lines = cm.span_to_lines(sp); @@ -357,12 +390,12 @@ fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan, // the span) let span_end = Span { lo: sp.hi, hi: sp.hi, expn_info: sp.expn_info}; 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() { try!(custom_highlight_lines(dst, cm, sp, lvl, lines)); } } 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() { 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, format!("in expansion of {}{}{}", pre, ei.callee.name, - post).as_slice())); + post).as_slice(), None)); 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)); } Ok(()) diff --git a/src/libsyntax/diagnostics/macros.rs b/src/libsyntax/diagnostics/macros.rs new file mode 100644 index 0000000000000..b0260e1180f8f --- /dev/null +++ b/src/libsyntax/diagnostics/macros.rs @@ -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 or the MIT license +// , 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)) + }) +) diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs new file mode 100644 index 0000000000000..6582d2e44c8e3 --- /dev/null +++ b/src/libsyntax/diagnostics/plugin.rs @@ -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 or the MIT license +// , 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>>) +local_data_key!(used_diagnostics: RefCell>) + +fn with_registered_diagnostics(f: |&mut HashMap>| -> 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(f: |&mut HashMap| -> 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 { + 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 { + 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 { + 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> = 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()) +} diff --git a/src/libsyntax/diagnostics/registry.rs b/src/libsyntax/diagnostics/registry.rs new file mode 100644 index 0000000000000..7bc191df55af3 --- /dev/null +++ b/src/libsyntax/diagnostics/registry.rs @@ -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 or the MIT license +// , 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) + } +} diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 4d79ff3257a9a..4d3913da3656a 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -148,6 +148,8 @@ pub trait AstBuilder { fn expr_some(&self, sp: Span, expr: Gc) -> Gc; fn expr_none(&self, sp: Span) -> Gc; + fn expr_tuple(&self, sp: Span, exprs: Vec>) -> Gc; + fn expr_fail(&self, span: Span, msg: InternedString) -> Gc; fn expr_unreachable(&self, span: Span) -> Gc; @@ -674,6 +676,10 @@ impl<'a> AstBuilder for ExtCtxt<'a> { self.expr_path(none) } + fn expr_tuple(&self, sp: Span, exprs: Vec>) -> Gc { + self.expr(sp, ast::ExprTup(exprs)) + } + fn expr_fail(&self, span: Span, msg: InternedString) -> Gc { let loc = self.codemap().lookup_char_pos(span.lo); self.expr_call_global( diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index b7d72ae4debc1..b5f7005c2a3e0 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -58,7 +58,7 @@ pub fn expand_expr(e: Gc, fld: &mut MacroExpander) -> Gc { None => { fld.cx.span_err( pth.span, - format!("macro undefined: '{}'", + format!("macro undefined: '{}!'", extnamestr.get()).as_slice()); // let compilation continue @@ -567,7 +567,7 @@ fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector> { let marked_after = match fld.extsbox.find(&extname.name) { None => { fld.cx.span_err(pth.span, - format!("macro undefined: '{}'", + format!("macro undefined: '{}!'", extnamestr).as_slice()); return SmallVector::zero(); } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 1ef376c6615ec..184ce39aaf2a2 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -27,12 +27,11 @@ #![feature(quote, unsafe_destructor)] #![allow(deprecated)] -extern crate serialize; -extern crate term; -#[phase(plugin, link)] extern crate log; - extern crate fmt_macros; extern crate debug; +#[phase(plugin, link)] extern crate log; +extern crate serialize; +extern crate term; pub mod util { pub mod interner; @@ -41,26 +40,30 @@ pub mod util { pub mod small_vector; } +pub mod diagnostics { + pub mod macros; + pub mod plugin; + pub mod registry; +} + pub mod syntax { pub use ext; pub use parse; pub use ast; } -pub mod owned_slice; -pub mod attr; -pub mod diagnostic; -pub mod codemap; pub mod abi; pub mod ast; -pub mod ast_util; pub mod ast_map; -pub mod visit; +pub mod ast_util; +pub mod attr; +pub mod codemap; +pub mod crateid; +pub mod diagnostic; pub mod fold; - - +pub mod owned_slice; pub mod parse; -pub mod crateid; +pub mod visit; pub mod print { pub mod pp; @@ -70,31 +73,25 @@ pub mod print { pub mod ext { pub mod asm; pub mod base; + 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 deriving; - - pub mod build; + pub mod source_util; + pub mod trace_macros; pub mod tt { pub mod transcribe; pub mod macro_parser; 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; } diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 0aaddacfab624..5c5943f0cd47c 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -1308,7 +1308,7 @@ mod test { use std::io::util; 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); diagnostic::mk_span_handler(handler, CodeMap::new()) } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 37c84c95af654..a3e169cd5116d 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -40,7 +40,7 @@ pub struct ParseSess { pub fn new_parse_sess() -> 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()), } } diff --git a/src/test/compile-fail-fulldeps/macro-crate-unexported-macro.rs b/src/test/compile-fail-fulldeps/macro-crate-unexported-macro.rs index 39c2accaddf33..6a3b0b91ffe29 100644 --- a/src/test/compile-fail-fulldeps/macro-crate-unexported-macro.rs +++ b/src/test/compile-fail-fulldeps/macro-crate-unexported-macro.rs @@ -18,5 +18,5 @@ extern crate macro_crate_test; fn main() { - assert_eq!(3, unexported_macro!()); //~ ERROR macro undefined: 'unexported_macro' + assert_eq!(3, unexported_macro!()); //~ ERROR macro undefined: 'unexported_macro!' } diff --git a/src/test/compile-fail/issue-11692.rs b/src/test/compile-fail/issue-11692.rs index 848deac4d55c0..09cf973961404 100644 --- a/src/test/compile-fail/issue-11692.rs +++ b/src/test/compile-fail/issue-11692.rs @@ -10,9 +10,9 @@ fn main() { print!(test!()); - //~^ ERROR: macro undefined: 'test' + //~^ ERROR: macro undefined: 'test!' //~^^ ERROR: format argument must be a string literal concat!(test!()); - //~^ ERROR: macro undefined: 'test' + //~^ ERROR: macro undefined: 'test!' } diff --git a/src/test/compile-fail/rustc-diagnostics-1.rs b/src/test/compile-fail/rustc-diagnostics-1.rs new file mode 100644 index 0000000000000..55d836092fa71 --- /dev/null +++ b/src/test/compile-fail/rustc-diagnostics-1.rs @@ -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 or the MIT license +// , 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 diff --git a/src/test/compile-fail/rustc-diagnostics-2.rs b/src/test/compile-fail/rustc-diagnostics-2.rs new file mode 100644 index 0000000000000..c4e011bcea042 --- /dev/null +++ b/src/test/compile-fail/rustc-diagnostics-2.rs @@ -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 or the MIT license +// , 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) diff --git a/src/test/compile-fail/rustc-diagnostics-3.rs b/src/test/compile-fail/rustc-diagnostics-3.rs new file mode 100644 index 0000000000000..d160664a48c78 --- /dev/null +++ b/src/test/compile-fail/rustc-diagnostics-3.rs @@ -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 or the MIT license +// , 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!'