Add scaffolding for assigning alpha-numeric codes to rustc diagnostics

This commit is contained in:
Jakub Wieczorek 2014-07-01 18:39:41 +02:00
parent 7ab9bfab4e
commit 9b9cce2316
25 changed files with 468 additions and 86 deletions

View File

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

View 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 @@ 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());

View File

@ -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);
}

View File

@ -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)
}
}

View File

@ -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);

View File

@ -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"));
}

View File

@ -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.
//

View 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!()
}

View 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);

View 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);

View 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();

View File

@ -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<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 @@ 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<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 @@ 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(())

View File

@ -0,0 +1,51 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![macro_escape]
// NOTE: remove after next snapshot
#[cfg(stage0)]
#[macro_export]
macro_rules! __register_diagnostic(
($code:tt, $description:tt) => ();
($code:tt) => ()
)
#[macro_export]
macro_rules! register_diagnostic(
($code:tt, $description:tt) => (__register_diagnostic!($code, $description));
($code:tt) => (__register_diagnostic!($code))
)
// NOTE: remove after next snapshot
#[cfg(stage0)]
#[macro_export]
macro_rules! __build_diagnostic_array(
($name:ident) => {
pub static $name: [(&'static str, &'static str), ..0] = [];
}
)
// NOTE: remove after next snapshot
#[cfg(stage0)]
#[macro_export]
macro_rules! __diagnostic_used(
($code:ident) => {
()
}
)
#[macro_export]
macro_rules! span_err(
($session:expr, $span:expr, $code:ident, $($arg:expr),*) => ({
__diagnostic_used!($code);
($session).span_err_with_code($span, format!($($arg),*).as_slice(), stringify!($code))
})
)

View File

@ -0,0 +1,132 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::cell::RefCell;
use std::collections::HashMap;
use std::gc::Gc;
use ast;
use ast::{Ident, Name, TokenTree};
use codemap::Span;
use ext::base::{ExtCtxt, MacExpr, MacItem, MacResult};
use ext::build::AstBuilder;
use parse::token;
local_data_key!(registered_diagnostics: RefCell<HashMap<Name, Option<Name>>>)
local_data_key!(used_diagnostics: RefCell<HashMap<Name, Span>>)
fn with_registered_diagnostics<T>(f: |&mut HashMap<Name, Option<Name>>| -> T) -> T {
match registered_diagnostics.get() {
Some(cell) => f(cell.borrow_mut().deref_mut()),
None => {
let mut map = HashMap::new();
let value = f(&mut map);
registered_diagnostics.replace(Some(RefCell::new(map)));
value
}
}
}
fn with_used_diagnostics<T>(f: |&mut HashMap<Name, Span>| -> T) -> T {
match used_diagnostics.get() {
Some(cell) => f(cell.borrow_mut().deref_mut()),
None => {
let mut map = HashMap::new();
let value = f(&mut map);
used_diagnostics.replace(Some(RefCell::new(map)));
value
}
}
}
pub fn expand_diagnostic_used(ecx: &mut ExtCtxt, span: Span,
token_tree: &[TokenTree]) -> Box<MacResult> {
let code = match token_tree {
[ast::TTTok(_, token::IDENT(code, _))] => code,
_ => unreachable!()
};
with_registered_diagnostics(|diagnostics| {
if !diagnostics.contains_key(&code.name) {
ecx.span_err(span, format!(
"unknown diagnostic code {}", token::get_ident(code).get()
).as_slice());
}
()
});
with_used_diagnostics(|diagnostics| {
match diagnostics.swap(code.name, span) {
Some(previous_span) => {
ecx.span_warn(span, format!(
"diagnostic code {} already used", token::get_ident(code).get()
).as_slice());
ecx.span_note(previous_span, "previous invocation");
},
None => ()
}
()
});
MacExpr::new(quote_expr!(ecx, ()))
}
pub fn expand_register_diagnostic(ecx: &mut ExtCtxt, span: Span,
token_tree: &[TokenTree]) -> Box<MacResult> {
let (code, description) = match token_tree {
[ast::TTTok(_, token::IDENT(ref code, _))] => {
(code, None)
},
[ast::TTTok(_, token::IDENT(ref code, _)),
ast::TTTok(_, token::COMMA),
ast::TTTok(_, token::LIT_STR_RAW(description, _))] => {
(code, Some(description))
}
_ => unreachable!()
};
with_registered_diagnostics(|diagnostics| {
if !diagnostics.insert(code.name, description) {
ecx.span_err(span, format!(
"diagnostic code {} already registered", token::get_ident(*code).get()
).as_slice());
}
});
let sym = Ident::new(token::gensym((
"__register_diagnostic_".to_string() + token::get_ident(*code).get()
).as_slice()));
MacItem::new(quote_item!(ecx, mod $sym {}).unwrap())
}
pub fn expand_build_diagnostic_array(ecx: &mut ExtCtxt, span: Span,
token_tree: &[TokenTree]) -> Box<MacResult> {
let name = match token_tree {
[ast::TTTok(_, token::IDENT(ref name, _))] => name,
_ => unreachable!()
};
let (count, expr) = with_used_diagnostics(|diagnostics_in_use| {
with_registered_diagnostics(|diagnostics| {
let descriptions: Vec<Gc<ast::Expr>> = diagnostics
.iter().filter_map(|(code, description)| {
if !diagnostics_in_use.contains_key(code) {
ecx.span_warn(span, format!(
"diagnostic code {} never used", token::get_name(*code).get()
).as_slice());
}
description.map(|description| {
ecx.expr_tuple(span, vec![
ecx.expr_str(span, token::get_name(*code)),
ecx.expr_str(span, token::get_name(description))
])
})
}).collect();
(descriptions.len(), ecx.expr_vec(span, descriptions))
})
});
MacItem::new(quote_item!(ecx,
pub static $name: [(&'static str, &'static str), ..$count] = $expr;
).unwrap())
}

View File

@ -0,0 +1,25 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::collections::HashMap;
pub struct Registry {
descriptions: HashMap<&'static str, &'static str>
}
impl Registry {
pub fn new(descriptions: &[(&'static str, &'static str)]) -> Registry {
Registry { descriptions: descriptions.iter().map(|&tuple| tuple).collect() }
}
pub fn find_description(&self, code: &str) -> Option<&'static str> {
self.descriptions.find_equiv(&code).map(|desc| *desc)
}
}

View File

@ -148,6 +148,8 @@ pub trait AstBuilder {
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 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
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(

View 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();
}

View File

@ -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 fold;
pub mod parse;
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 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 expand;
pub mod quote;
pub mod deriving;
pub mod build;
pub mod bytes;
pub mod cfg;
pub mod concat;
pub mod concat_idents;
pub mod deriving;
pub mod env;
pub mod expand;
pub mod fmt;
pub mod format;
pub mod log_syntax;
pub mod mtwt;
pub mod quote;
pub mod source_util;
pub mod trace_macros;
pub mod tt {
pub mod 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;
}

View 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())
}

View 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()),
}
}

View 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!'
}

View 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!'
}

View File

@ -0,0 +1,28 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_diagnostic_macros)]
__register_diagnostic!(E0001)
__register_diagnostic!(E0003)
fn main() {
__diagnostic_used!(E0002);
//~^ ERROR unknown diagnostic code E0002
__diagnostic_used!(E0001);
//~^ NOTE previous invocation
__diagnostic_used!(E0001);
//~^ WARNING diagnostic code E0001 already used
}
__build_diagnostic_array!(DIAGNOSTICS)
//~^ WARN diagnostic code E0003 never used

View File

@ -0,0 +1,20 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_diagnostic_macros)]
__register_diagnostic!(E0001)
__register_diagnostic!(E0001)
//~^ ERROR diagnostic code E0001 already registered
fn main() {
}
__build_diagnostic_array!(DIAGNOSTICS)

View File

@ -0,0 +1,20 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
__register_diagnostic!(E0001)
//~^ ERROR macro undefined: '__register_diagnostic!'
fn main() {
__diagnostic_used!(E0001);
//~^ ERROR macro undefined: '__diagnostic_used!'
}
__build_diagnostic_array!(DIAGNOSTICS)
//~^ ERROR macro undefined: '__build_diagnostic_array!'