]> git.lizzy.rs Git - rust.git/commitdiff
Add scaffolding for assigning alpha-numeric codes to rustc diagnostics
authorJakub Wieczorek <jakub@jakub.cc>
Tue, 1 Jul 2014 16:39:41 +0000 (18:39 +0200)
committerJakub Wieczorek <jakub@jakub.cc>
Thu, 10 Jul 2014 22:32:00 +0000 (00:32 +0200)
25 files changed:
src/librustc/diagnostics.rs [new file with mode: 0644]
src/librustc/driver/config.rs
src/librustc/driver/driver.rs
src/librustc/driver/mod.rs
src/librustc/driver/session.rs
src/librustc/front/feature_gate.rs
src/librustc/lib.rs
src/librustc/middle/check_match.rs
src/librustc/middle/typeck/infer/test.rs
src/librustdoc/core.rs
src/librustdoc/test.rs
src/libsyntax/diagnostic.rs
src/libsyntax/diagnostics/macros.rs [new file with mode: 0644]
src/libsyntax/diagnostics/plugin.rs [new file with mode: 0644]
src/libsyntax/diagnostics/registry.rs [new file with mode: 0644]
src/libsyntax/ext/build.rs
src/libsyntax/ext/expand.rs
src/libsyntax/lib.rs
src/libsyntax/parse/lexer/mod.rs
src/libsyntax/parse/mod.rs
src/test/compile-fail-fulldeps/macro-crate-unexported-macro.rs
src/test/compile-fail/issue-11692.rs
src/test/compile-fail/rustc-diagnostics-1.rs [new file with mode: 0644]
src/test/compile-fail/rustc-diagnostics-2.rs [new file with mode: 0644]
src/test/compile-fail/rustc-diagnostics-3.rs [new file with mode: 0644]

diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
new file mode 100644 (file)
index 0000000..0499795
--- /dev/null
@@ -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.
+"##)
index 9daaa2792ebad0e0140f55234769fc236645fc7d..b726c50afe9d1202850cbfdc5b7b73fb20a2d0a6 100644 (file)
@@ -532,6 +532,7 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
         optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
         optopt( "",  "out-dir", "Write output to compiler-chosen filename in <dir>", "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 @@ fn test_switch_implies_cfg_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 @@ fn test_switch_implies_cfg_test_unless_cfg_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());
index 6a016edcd2868fce87671e901d207f62464ec2b5..8173e2ab582a043315a3882cd0e561f0c799284c 100644 (file)
@@ -16,6 +16,7 @@
 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 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 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 &registrar in registrars.iter() {
             registrar(&mut registry);
         }
index ad0d8cac1e3578fa8aebf0ec4d13d56b2a2aea3d..2614c9ebf98b003fa74d2fb3678bd519afb603cd 100644 (file)
@@ -26,6 +26,7 @@
 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::<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
                 // 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)
                     }
                 }
index 50f61f8f06a5be795e5df197238e7ca01b473876..8669a733f61345a7f7fb5d4f9d908036afa86189 100644 (file)
@@ -20,6 +20,7 @@
 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 @@ pub fn fatal(&self, msg: &str) -> ! {
     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 @@ pub fn host_filesearch<'a>(&'a self) -> filesearch::FileSearch<'a> {
 }
 
 pub fn build_session(sopts: config::Options,
-                     local_crate_source_file: Option<Path>)
+                     local_crate_source_file: Option<Path>,
+                     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);
 
index 931f0312f579c809686a3e7d76c223c06e7acbd1..8b92166388d8608aadec35fbca6e4934948c3794 100644 (file)
@@ -66,6 +66,8 @@
 
     ("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<bool>,
     pub issue_5723_bootstrap: Cell<bool>,
     pub overloaded_calls: Cell<bool>,
+    pub rustc_diagnostic_macros: Cell<bool>
 }
 
 impl Features {
@@ -101,6 +104,7 @@ pub fn new() -> 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"));
 }
index 6e28d0cce3220762a64e526fb65347ce5a2056ba..97ea425df9970ed5ff60ff8196ebb1585743effd 100644 (file)
@@ -32,6 +32,9 @@
 #![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;
 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.
 //
index 599f5f4024f28ab2d98c3a29156790f22a7a5e80..f31c247abd38c3c83935d0d5bad13221db1ffddf 100644 (file)
@@ -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!()
             }
index e66dcd118c92892186cb85857d2e6a4b4ef801c7..c06e40fce14f915e410208e5393bb57eccbf885f 100644 (file)
@@ -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);
index 9ef671ef2fcbbddc79e6fc125b6492fd330b29f0..e62c8b63a294070b0c873f5cccecc9775b22a24f 100644 (file)
@@ -101,7 +101,7 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
 
 
     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);
 
index 18f823317808156b88ac86cf49d0af4be814d089..92983e5d31bc40f849fe8688a9e08049494e002a 100644 (file)
@@ -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<Path>, 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();
index e469f327ae8ba403a3620ad65d0189978095f4a0..9bb5eae2ed2d66c893378ee316e419c01e53f90b 100644 (file)
@@ -12,6 +12,7 @@
 
 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 @@ pub fn span_err(&self, sp: Span, msg: &str) {
         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 @@ pub fn abort_if_errors(&self) {
         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 @@ pub fn emit(&self,
                 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<diagnostics::registry::Registry>) -> Handler {
+    mk_handler(box EmitterWriter::stderr(color_config, registry))
 }
 
 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,
-                    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<diagnostics::registry::Registry>
 }
 
 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<diagnostics::registry::Registry>) -> EmitterWriter {
         let stderr = io::stderr();
 
         let use_color = match color_config {
@@ -301,14 +334,15 @@ pub fn stderr(color_config: ColorConfig) -> 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<Writer + Send>) -> EmitterWriter {
-        EmitterWriter { dst: Raw(dst) }
+    pub fn new(dst: Box<Writer + Send>,
+               registry: Option<diagnostics::registry::Registry>) -> EmitterWriter {
+        EmitterWriter { dst: Raw(dst), registry: registry }
     }
 }
 
@@ -324,11 +358,10 @@ fn write(&mut self, bytes: &[u8]) -> io::IoResult<()> {
 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 @@ fn emit(&mut self,
 
     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 @@ fn custom_emit(&mut self, cm: &codemap::CodeMap,
 }
 
 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 (file)
index 0000000..b0260e1
--- /dev/null
@@ -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))
+    })
+)
diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs
new file mode 100644 (file)
index 0000000..6582d2e
--- /dev/null
@@ -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())
+}
diff --git a/src/libsyntax/diagnostics/registry.rs b/src/libsyntax/diagnostics/registry.rs
new file mode 100644 (file)
index 0000000..7bc191d
--- /dev/null
@@ -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)
+    }
+}
index 4d79ff3257a9a20cd3b58cd5e330ff923a019e97..4d3913da3656a2c90e048c6276665be2a045f05b 100644 (file)
@@ -148,6 +148,8 @@ fn expr_struct_ident(&self, span: Span, id: ast::Ident,
     fn expr_some(&self, sp: Span, expr: Gc<ast::Expr>) -> 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_unreachable(&self, span: Span) -> Gc<ast::Expr>;
 
@@ -674,6 +676,10 @@ fn expr_none(&self, sp: Span) -> Gc<ast::Expr> {
         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> {
         let loc = self.codemap().lookup_char_pos(span.lo);
         self.expr_call_global(
index b7d72ae4debc130342fe3d5dc3e2a8577b4a4d0d..b5f7005c2a3e005f2fdca7153e0260596456701d 100644 (file)
@@ -58,7 +58,7 @@ pub fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
                         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<Gc<Stmt>> {
     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();
         }
index 1ef376c6615ec393c751e40b3f41d5609302d335..184ce39aaf2a27749285a8d46174ab770176d638 100644 (file)
 #![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;
 }
index 0aaddacfab624783179153e274e848263b6ac381..5c5943f0cd47c10e5fd461800b0ffbae3024a978 100644 (file)
@@ -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())
     }
index 37c84c95af654e3f98ce63060c94b1bd7161c4a1..a3e169cd5116d53c0c22834e70ea6c85c8ca33d1 100644 (file)
@@ -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()),
     }
 }
index 39c2accaddf3327dd0259ac7b5344720b4ab4ad6..6a3b0b91ffe297ed9d24f03a8d685f687c0c344e 100644 (file)
@@ -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!'
 }
index 848deac4d55c04822b94ba8d10d6b77e8dab8ed6..09cf9739614040679a39ddb73be474e7b7d50b71 100644 (file)
@@ -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 (file)
index 0000000..55d8360
--- /dev/null
@@ -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
diff --git a/src/test/compile-fail/rustc-diagnostics-2.rs b/src/test/compile-fail/rustc-diagnostics-2.rs
new file mode 100644 (file)
index 0000000..c4e011b
--- /dev/null
@@ -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)
diff --git a/src/test/compile-fail/rustc-diagnostics-3.rs b/src/test/compile-fail/rustc-diagnostics-3.rs
new file mode 100644 (file)
index 0000000..d160664
--- /dev/null
@@ -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!'