about summary refs log tree commit diff
path: root/src/librustc
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-07-10 23:26:39 +0000
committerbors <bors@rust-lang.org>2014-07-10 23:26:39 +0000
commit0e80dbe59ea986ea53cc3caabffd40b2eaee4dc6 (patch)
tree3044cbdb7fe356d52a07c9048bc431aac3de5c4a /src/librustc
parenta672456c40d28f051ecbdb2caf5bf6733371d494 (diff)
parent9b9cce2316119a2ffdc9556d410e464b7542d64d (diff)
downloadrust-0e80dbe59ea986ea53cc3caabffd40b2eaee4dc6.tar.gz
rust-0e80dbe59ea986ea53cc3caabffd40b2eaee4dc6.zip
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`.
Diffstat (limited to 'src/librustc')
-rw-r--r--src/librustc/diagnostics.rs18
-rw-r--r--src/librustc/driver/config.rs8
-rw-r--r--src/librustc/driver/driver.rs13
-rw-r--r--src/librustc/driver/mod.rs35
-rw-r--r--src/librustc/driver/session.rs9
-rw-r--r--src/librustc/front/feature_gate.rs5
-rw-r--r--src/librustc/lib.rs9
-rw-r--r--src/librustc/middle/check_match.rs2
-rw-r--r--src/librustc/middle/typeck/infer/test.rs1
9 files changed, 85 insertions, 15 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
new file mode 100644
index 00000000000..0499795f890
--- /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 <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.
+"##)
diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs
index 9daaa2792eb..b726c50afe9 100644
--- a/src/librustc/driver/config.rs
+++ b/src/librustc/driver/config.rs
@@ -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 @@ 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 6a016edcd28..8173e2ab582 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 &registrar in registrars.iter() {
             registrar(&mut registry);
         }
diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs
index ad0d8cac1e3..2614c9ebf98 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::<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)
                     }
                 }
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index 50f61f8f06a..8669a733f61 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<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);
 
diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs
index 931f0312f57..8b92166388d 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<bool>,
     pub issue_5723_bootstrap: Cell<bool>,
     pub overloaded_calls: Cell<bool>,
+    pub rustc_diagnostic_macros: Cell<bool>
 }
 
 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 6e28d0cce32..97ea425df99 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 599f5f4024f..f31c247abd3 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 e66dcd118c9..c06e40fce14 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);