Skip to content

Commit 0e80dbe

Browse files
committed
auto merge of #15336 : jakub-/rust/diagnostics, r=brson
This is a continuation of @brson's work from #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`.
2 parents a672456 + 9b9cce2 commit 0e80dbe

25 files changed

+466
-84
lines changed

src/librustc/diagnostics.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
register_diagnostic!(E0001, r##"
12+
This error suggests that the expression arm corresponding to the noted pattern
13+
will never be reached as for all possible values of the expression being matched,
14+
one of the preceeding patterns will match.
15+
16+
This means that perhaps some of the preceeding patterns are too general, this
17+
one is too specific or the ordering is incorrect.
18+
"##)

src/librustc/driver/config.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
532532
optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
533533
optopt( "", "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
534534
optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
535+
optopt("", "explain", "Provide a detailed explanation of an error message", "OPT"),
535536
optflagopt("", "pretty",
536537
"Pretty-print the input instead of compiling;
537538
valid types are: `normal` (un-annotated source),
@@ -807,6 +808,7 @@ mod test {
807808
use getopts::getopts;
808809
use syntax::attr;
809810
use syntax::attr::AttrMetaMethods;
811+
use syntax::diagnostics;
810812

811813
// When the user supplies --test we should implicitly supply --cfg test
812814
#[test]
@@ -816,8 +818,9 @@ mod test {
816818
Ok(m) => m,
817819
Err(f) => fail!("test_switch_implies_cfg_test: {}", f)
818820
};
821+
let registry = diagnostics::registry::Registry::new([]);
819822
let sessopts = build_session_options(matches);
820-
let sess = build_session(sessopts, None);
823+
let sess = build_session(sessopts, None, registry);
821824
let cfg = build_configuration(&sess);
822825
assert!((attr::contains_name(cfg.as_slice(), "test")));
823826
}
@@ -834,8 +837,9 @@ mod test {
834837
fail!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
835838
}
836839
};
840+
let registry = diagnostics::registry::Registry::new([]);
837841
let sessopts = build_session_options(matches);
838-
let sess = build_session(sessopts, None);
842+
let sess = build_session(sessopts, None, registry);
839843
let cfg = build_configuration(&sess);
840844
let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
841845
assert!(test_items.next().is_some());

src/librustc/driver/driver.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use driver::{PpmFlowGraph, PpmExpanded, PpmExpandedIdentified, PpmTyped};
1616
use driver::{PpmIdentified};
1717
use front;
1818
use lib::llvm::{ContextRef, ModuleRef};
19+
use lint;
1920
use metadata::common::LinkMeta;
2021
use metadata::creader;
2122
use middle::cfg;
@@ -26,7 +27,7 @@ use middle;
2627
use plugin::load::Plugins;
2728
use plugin::registry::Registry;
2829
use plugin;
29-
use lint;
30+
3031
use util::common::time;
3132
use util::ppaux;
3233
use util::nodemap::{NodeSet};
@@ -41,6 +42,7 @@ use std::io::MemReader;
4142
use syntax::ast;
4243
use syntax::attr;
4344
use syntax::attr::{AttrMetaMethods};
45+
use syntax::diagnostics;
4446
use syntax::parse;
4547
use syntax::parse::token;
4648
use syntax::print::{pp, pprust};
@@ -213,6 +215,15 @@ pub fn phase_2_configure_and_expand(sess: &Session,
213215
let mut registry = Registry::new(&krate);
214216

215217
time(time_passes, "plugin registration", (), |_| {
218+
if sess.features.rustc_diagnostic_macros.get() {
219+
registry.register_macro("__diagnostic_used",
220+
diagnostics::plugin::expand_diagnostic_used);
221+
registry.register_macro("__register_diagnostic",
222+
diagnostics::plugin::expand_register_diagnostic);
223+
registry.register_macro("__build_diagnostic_array",
224+
diagnostics::plugin::expand_build_diagnostic_array);
225+
}
226+
216227
for &registrar in registrars.iter() {
217228
registrar(&mut registry);
218229
}

src/librustc/driver/mod.rs

+27-8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use std::task::TaskBuilder;
2626
use syntax::ast;
2727
use syntax::parse;
2828
use syntax::diagnostic::Emitter;
29+
use syntax::diagnostics;
2930

3031
use getopts;
3132

@@ -49,8 +50,24 @@ fn run_compiler(args: &[String]) {
4950
Some(matches) => matches,
5051
None => return
5152
};
52-
let sopts = config::build_session_options(&matches);
5353

54+
let descriptions = diagnostics::registry::Registry::new(super::DIAGNOSTICS);
55+
match matches.opt_str("explain") {
56+
Some(ref code) => {
57+
match descriptions.find_description(code.as_slice()) {
58+
Some(ref description) => {
59+
println!("{}", description);
60+
}
61+
None => {
62+
early_error(format!("no extended information for {}", code).as_slice());
63+
}
64+
}
65+
return;
66+
},
67+
None => ()
68+
}
69+
70+
let sopts = config::build_session_options(&matches);
5471
let (input, input_file_path) = match matches.free.len() {
5572
0u => {
5673
if sopts.describe_lints {
@@ -75,7 +92,7 @@ fn run_compiler(args: &[String]) {
7592
_ => early_error("multiple input filenames provided")
7693
};
7794

78-
let sess = build_session(sopts, input_file_path);
95+
let sess = build_session(sopts, input_file_path, descriptions);
7996
let cfg = config::build_configuration(&sess);
8097
let odir = matches.opt_str("out-dir").map(|o| Path::new(o));
8198
let ofile = matches.opt_str("o").map(|o| Path::new(o));
@@ -383,14 +400,14 @@ fn parse_crate_attrs(sess: &Session, input: &Input) ->
383400
}
384401

385402
pub fn early_error(msg: &str) -> ! {
386-
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto);
387-
emitter.emit(None, msg, diagnostic::Fatal);
403+
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);
404+
emitter.emit(None, msg, None, diagnostic::Fatal);
388405
fail!(diagnostic::FatalError);
389406
}
390407

391408
pub fn early_warn(msg: &str) {
392-
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto);
393-
emitter.emit(None, msg, diagnostic::Warning);
409+
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);
410+
emitter.emit(None, msg, None, diagnostic::Warning);
394411
}
395412

396413
pub fn list_metadata(sess: &Session, path: &Path,
@@ -429,14 +446,15 @@ fn monitor(f: proc():Send) {
429446
Err(value) => {
430447
// Task failed without emitting a fatal diagnostic
431448
if !value.is::<diagnostic::FatalError>() {
432-
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto);
449+
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);
433450

434451
// a .span_bug or .bug call has already printed what
435452
// it wants to print.
436453
if !value.is::<diagnostic::ExplicitBug>() {
437454
emitter.emit(
438455
None,
439456
"unexpected failure",
457+
None,
440458
diagnostic::Bug);
441459
}
442460

@@ -447,7 +465,7 @@ fn monitor(f: proc():Send) {
447465
"run with `RUST_BACKTRACE=1` for a backtrace".to_string(),
448466
];
449467
for note in xs.iter() {
450-
emitter.emit(None, note.as_slice(), diagnostic::Note)
468+
emitter.emit(None, note.as_slice(), None, diagnostic::Note)
451469
}
452470

453471
match r.read_to_string() {
@@ -457,6 +475,7 @@ fn monitor(f: proc():Send) {
457475
format!("failed to read internal \
458476
stderr: {}",
459477
e).as_slice(),
478+
None,
460479
diagnostic::Error)
461480
}
462481
}

src/librustc/driver/session.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use util::nodemap::NodeMap;
2020
use syntax::ast::NodeId;
2121
use syntax::codemap::Span;
2222
use syntax::diagnostic;
23+
use syntax::diagnostics;
2324
use syntax::parse;
2425
use syntax::parse::token;
2526
use syntax::parse::ParseSess;
@@ -65,6 +66,9 @@ impl Session {
6566
pub fn span_err(&self, sp: Span, msg: &str) {
6667
self.diagnostic().span_err(sp, msg)
6768
}
69+
pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
70+
self.diagnostic().span_err_with_code(sp, msg, code)
71+
}
6872
pub fn err(&self, msg: &str) {
6973
self.diagnostic().handler().err(msg)
7074
}
@@ -197,11 +201,12 @@ impl Session {
197201
}
198202

199203
pub fn build_session(sopts: config::Options,
200-
local_crate_source_file: Option<Path>)
204+
local_crate_source_file: Option<Path>,
205+
registry: diagnostics::registry::Registry)
201206
-> Session {
202207
let codemap = codemap::CodeMap::new();
203208
let diagnostic_handler =
204-
diagnostic::default_handler(sopts.color);
209+
diagnostic::default_handler(sopts.color, Some(registry));
205210
let span_diagnostic_handler =
206211
diagnostic::mk_span_handler(diagnostic_handler, codemap);
207212

src/librustc/front/feature_gate.rs

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
6666

6767
("quad_precision_float", Removed),
6868

69+
("rustc_diagnostic_macros", Active),
70+
6971
// A temporary feature gate used to enable parser extensions needed
7072
// to bootstrap fix for #5723.
7173
("issue_5723_bootstrap", Active),
@@ -93,6 +95,7 @@ pub struct Features {
9395
pub default_type_params: Cell<bool>,
9496
pub issue_5723_bootstrap: Cell<bool>,
9597
pub overloaded_calls: Cell<bool>,
98+
pub rustc_diagnostic_macros: Cell<bool>
9699
}
97100

98101
impl Features {
@@ -101,6 +104,7 @@ impl Features {
101104
default_type_params: Cell::new(false),
102105
issue_5723_bootstrap: Cell::new(false),
103106
overloaded_calls: Cell::new(false),
107+
rustc_diagnostic_macros: Cell::new(false)
104108
}
105109
}
106110
}
@@ -425,4 +429,5 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) {
425429
sess.features.default_type_params.set(cx.has_feature("default_type_params"));
426430
sess.features.issue_5723_bootstrap.set(cx.has_feature("issue_5723_bootstrap"));
427431
sess.features.overloaded_calls.set(cx.has_feature("overloaded_calls"));
432+
sess.features.rustc_diagnostic_macros.set(cx.has_feature("rustc_diagnostic_macros"));
428433
}

src/librustc/lib.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,21 @@ This API is completely unstable and subject to change.
3232
#![feature(macro_rules, globs, struct_variant, managed_boxes, quote)]
3333
#![feature(default_type_params, phase, unsafe_destructor)]
3434

35+
#![allow(unknown_features)] // NOTE: Remove after next snapshot
36+
#![feature(rustc_diagnostic_macros)]
37+
3538
extern crate arena;
3639
extern crate debug;
3740
extern crate flate;
3841
extern crate getopts;
3942
extern crate graphviz;
4043
extern crate libc;
4144
extern crate serialize;
42-
extern crate syntax;
4345
extern crate time;
4446
#[phase(plugin, link)] extern crate log;
47+
#[phase(plugin, link)] extern crate syntax;
48+
49+
mod diagnostics;
4550

4651
pub mod middle {
4752
pub mod def;
@@ -127,6 +132,8 @@ pub mod lib {
127132
pub mod llvmdeps;
128133
}
129134

135+
__build_diagnostic_array!(DIAGNOSTICS)
136+
130137
// A private module so that macro-expanded idents like
131138
// `::rustc::lint::Lint` will also work in `rustc` itself.
132139
//

src/librustc/middle/check_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
194194

195195
let v = vec!(*pat);
196196
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
197-
NotUseful => cx.tcx.sess.span_err(pat.span, "unreachable pattern"),
197+
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
198198
Useful => (),
199199
UsefulWithWitness(_) => unreachable!()
200200
}

src/librustc/middle/typeck/infer/test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ impl Emitter for ExpectErrorEmitter {
7878
fn emit(&mut self,
7979
_cmsp: Option<(&codemap::CodeMap, Span)>,
8080
msg: &str,
81+
_: Option<&str>,
8182
lvl: Level)
8283
{
8384
remove_message(self, msg, lvl);

src/librustdoc/core.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
101101

102102

103103
let codemap = syntax::codemap::CodeMap::new();
104-
let diagnostic_handler = syntax::diagnostic::default_handler(syntax::diagnostic::Auto);
104+
let diagnostic_handler = syntax::diagnostic::default_handler(syntax::diagnostic::Auto, None);
105105
let span_diagnostic_handler =
106106
syntax::diagnostic::mk_span_handler(diagnostic_handler, codemap);
107107

src/librustdoc/test.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub fn run(input: &str,
5454

5555

5656
let codemap = CodeMap::new();
57-
let diagnostic_handler = diagnostic::default_handler(diagnostic::Auto);
57+
let diagnostic_handler = diagnostic::default_handler(diagnostic::Auto, None);
5858
let span_diagnostic_handler =
5959
diagnostic::mk_span_handler(diagnostic_handler, codemap);
6060

@@ -150,7 +150,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool,
150150
};
151151
io::util::copy(&mut p, &mut err).unwrap();
152152
});
153-
let emitter = diagnostic::EmitterWriter::new(box w2);
153+
let emitter = diagnostic::EmitterWriter::new(box w2, None);
154154

155155
// Compile the code
156156
let codemap = CodeMap::new();

0 commit comments

Comments
 (0)