diff --git a/src/librustc/diag_db.md b/src/librustc/diag_db.md new file mode 100644 index 0000000000000..16afe333b0f96 --- /dev/null +++ b/src/librustc/diag_db.md @@ -0,0 +1,5 @@ +desc_diag!(A0003, " + +Your borrowed pointer doesn't live long enough, dude. + +"), \ No newline at end of file diff --git a/src/librustc/diag_db.rs b/src/librustc/diag_db.rs new file mode 100644 index 0000000000000..a3c20178c74ae --- /dev/null +++ b/src/librustc/diag_db.rs @@ -0,0 +1,27 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The diagnostic database. +//! +//! Extended information about Rust diagnostics is included in the +//! diag_db.md file and can be loaded at runtime with the `load` +//! function. + +use syntax::diag_db::DiagnosticDb; + +/// Load the database of extended diagnostic descriptions +pub fn load() -> DiagnosticDb { + DiagnosticDb::new(~[load_raw, ::syntax::diag_db::load_raw]) +} + +pub fn load_raw() -> ~[(&'static str, &'static str, &'static str)] { + ~[include!("diag_db.md")] +} + diff --git a/src/librustc/diag_index.rs b/src/librustc/diag_index.rs new file mode 100644 index 0000000000000..c089e58aa9e2a --- /dev/null +++ b/src/librustc/diag_index.rs @@ -0,0 +1,36 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The diagnostic registry +//! +//! All diagnostic codes must be registered here. To add a new +//! diagnostic code just go to the end of the file and add a new +//! line with a code that is one greater than the previous. + +reg_diag!(A0000) +reg_diag!(A0001) +reg_diag!(A0002) +reg_diag!(A0003) +reg_diag!(A0004) +reg_diag!(A0005) +reg_diag!(A0006) +reg_diag!(A0007) +reg_diag!(A0008) +reg_diag!(A0009) +reg_diag!(A0010) +reg_diag!(A0011) +reg_diag!(A0012) +reg_diag!(A0013) +reg_diag!(A0014) +reg_diag!(A0015) +reg_diag!(A0016) +reg_diag!(A0017) +reg_diag!(A0018) +reg_diag!(A0019) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 4657d69732354..a4a828c779b30 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -11,6 +11,7 @@ use back::link; use back::{arm, x86, x86_64, mips}; +use diag_db; use driver::session::{Aggressive, OutputExecutable}; use driver::session::{Session, Session_, No, Less, Default}; use driver::session; @@ -126,7 +127,7 @@ pub fn build_configuration(sess: Session) -> fn parse_cfgspecs(cfgspecs: ~[~str], demitter: @diagnostic::Emitter) -> ast::CrateConfig { cfgspecs.move_iter().map(|s| { - let sess = parse::new_parse_sess(Some(demitter)); + let sess = parse::new_parse_sess(Some(demitter), diag_db::load()); parse::parse_meta_from_source_str(@"cfgspec", s.to_managed(), ~[], sess) }).collect::() } @@ -884,7 +885,7 @@ pub fn build_session(sopts: @session::options, demitter: @diagnostic::Emitter) -> Session { let codemap = @codemap::CodeMap::new(); let diagnostic_handler = - diagnostic::mk_handler(Some(demitter)); + diagnostic::mk_handler(Some(demitter), diag_db::load()); let span_diagnostic_handler = diagnostic::mk_span_handler(diagnostic_handler, codemap); build_session_(sopts, codemap, demitter, span_diagnostic_handler) @@ -983,6 +984,8 @@ pub fn optgroups() -> ~[getopts::groups::OptGroup] { in ", "DIR"), optflag("", "parse-only", "Parse only; do not compile, assemble, or link"), + optopt("", "explain", + "Provide a detailed explanation of an error message", "ERRCODE"), optflagopt("", "pretty", "Pretty-print the input instead of compiling; valid types are: normal (un-annotated source), diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 0176c3fa1e06f..e48fc1dbc14cd 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -225,15 +225,27 @@ impl Session_ { pub fn span_fatal(&self, sp: Span, msg: &str) -> ! { self.span_diagnostic.span_fatal(sp, msg) } + pub fn span_fatal_with_diagnostic_code(&self, sp: Span, code: &str, msg: &str) -> ! { + self.span_diagnostic.span_fatal_with_diagnostic_code(sp, code, msg) + } pub fn fatal(&self, msg: &str) -> ! { self.span_diagnostic.handler().fatal(msg) } + pub fn fatal_with_diagnostic_code(&self, code: &str, msg: &str) -> ! { + self.span_diagnostic.handler().fatal_with_diagnostic_code(code, msg) + } pub fn span_err(&self, sp: Span, msg: &str) { self.span_diagnostic.span_err(sp, msg) } + pub fn span_err_with_diagnostic_code(&self, sp: Span, code: &str, msg: &str) { + self.span_diagnostic.span_err_with_diagnostic_code(sp, code, msg) + } pub fn err(&self, msg: &str) { self.span_diagnostic.handler().err(msg) } + pub fn err_with_diagnostic_code(&self, code: &str, msg: &str) { + self.span_diagnostic.handler().err_with_diagnostic_code(code, msg) + } pub fn err_count(&self) -> uint { self.span_diagnostic.handler().err_count() } @@ -246,9 +258,15 @@ impl Session_ { pub fn span_warn(&self, sp: Span, msg: &str) { self.span_diagnostic.span_warn(sp, msg) } + pub fn span_warn_with_diagnostic_code(&self, sp: Span, code: &str, msg: &str) { + self.span_diagnostic.span_warn_with_diagnostic_code(sp, code, msg) + } pub fn warn(&self, msg: &str) { self.span_diagnostic.handler().warn(msg) } + pub fn warn_with_diagnostic_code(&self, code: &str, msg: &str) { + self.span_diagnostic.handler().warn_with_diagnostic_code(code, msg) + } pub fn span_note(&self, sp: Span, msg: &str) { self.span_diagnostic.span_note(sp, msg) } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 82fed27b56521..812678d5dfb4a 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -56,6 +56,14 @@ use syntax::codemap; use syntax::diagnostic::Emitter; use syntax::diagnostic; use syntax::parse; +use syntax::diag_db::{explain_diagnostic, explain_diag_help}; + +// Define the diagnostic macros +#[path = "../libsyntax/diag_macros.rs"] +pub mod diag_macros; +// The index of all diagnostic codes used by this crate. This must be defined +// lexically before any diagnostics are used. +pub mod diag_index; pub mod middle { pub mod trans; @@ -227,6 +235,21 @@ pub fn run_compiler(args: &[~str], demitter: @diagnostic::Emitter) { return; } + match matches.opt_str("explain") { + Some(ref code) if code == &~"help" => { + explain_diag_help(); + return; + }, + Some(code) => { + if !explain_diagnostic(&diag_db::load(), code) { + d::early_error(demitter, + format!("no extended information about code {}", code)); + } + return; + } + None => () + } + // Display the available lint options if "-W help" or only "-W" is given. let lint_flags = vec::append(matches.opt_strs("W"), matches.opt_strs("warn")); @@ -464,3 +487,7 @@ pub fn main_args(args: &[~str]) -> int { monitor(proc(demitter) run_compiler(owned_args, demitter)); 0 } + +// The database of extended diagnostic descriptions. Must come lexically +// after all uses of diagnostics. See `diag_macros` for why. +pub mod diag_db; diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 9541afe719b06..a60dd7defa274 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -1460,7 +1460,8 @@ impl fake_ext_ctxt for fake_session { #[cfg(test)] fn mk_ctxt() -> @fake_ext_ctxt { - @parse::new_parse_sess(None) as @fake_ext_ctxt + use diag_db; + @parse::new_parse_sess(None, diag_db::load()) as @fake_ext_ctxt } #[cfg(test)] diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index ea7979b855e3a..cd5e44882ac2d 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -226,11 +226,10 @@ impl<'a> CheckLoanCtxt<'a> { match (new_loan.mutbl, old_loan.mutbl) { (MutableMutability, MutableMutability) => { - self.bccx.span_err( - new_loan.span, - format!("cannot borrow `{}` as mutable \ + span_err!(self.bccx, new_loan.span, A0012, + "cannot borrow `{}` as mutable \ more than once at a time", - self.bccx.loan_path_to_str(new_loan.loan_path))); + self.bccx.loan_path_to_str(new_loan.loan_path)); self.bccx.span_note( old_loan.span, format!("previous borrow of `{}` as mutable occurs here", @@ -239,13 +238,12 @@ impl<'a> CheckLoanCtxt<'a> { } _ => { - self.bccx.span_err( - new_loan.span, - format!("cannot borrow `{}` as {} because \ + span_err!(self.bccx, new_loan.span, A0013, + "cannot borrow `{}` as {} because \ it is also borrowed as {}", - self.bccx.loan_path_to_str(new_loan.loan_path), - self.bccx.mut_to_str(new_loan.mutbl), - self.bccx.mut_to_str(old_loan.mutbl))); + self.bccx.loan_path_to_str(new_loan.loan_path), + self.bccx.mut_to_str(new_loan.mutbl), + self.bccx.mut_to_str(old_loan.mutbl)); self.bccx.span_note( old_loan.span, format!("previous borrow of `{}` occurs here", @@ -332,11 +330,10 @@ impl<'a> CheckLoanCtxt<'a> { } // Otherwise, just a plain error. - self.bccx.span_err( - expr.span, - format!("cannot assign to {} {}", - cmt.mutbl.to_user_str(), - self.bccx.cmt_to_str(cmt))); + span_err!(self.bccx, expr.span, A0014, + "cannot assign to {} {}", + cmt.mutbl.to_user_str(), + self.bccx.cmt_to_str(cmt)); return; fn mark_variable_as_used_mut(this: &CheckLoanCtxt, @@ -602,10 +599,9 @@ impl<'a> CheckLoanCtxt<'a> { expr: &ast::Expr, loan_path: &LoanPath, loan: &Loan) { - self.bccx.span_err( - expr.span, - format!("cannot assign to `{}` because it is borrowed", - self.bccx.loan_path_to_str(loan_path))); + span_err!(self.bccx, expr.span, A0015, + "cannot assign to `{}` because it is borrowed", + self.bccx.loan_path_to_str(loan_path)); self.bccx.span_note( loan.span, format!("borrow of `{}` occurs here", @@ -630,11 +626,10 @@ impl<'a> CheckLoanCtxt<'a> { match self.analyze_move_out_from(id, move_path) { MoveOk => {} MoveWhileBorrowed(loan_path, loan_span) => { - self.bccx.span_err( - span, - format!("cannot move out of `{}` \ + span_err!(self.bccx, span, A0016, + "cannot move out of `{}` \ because it is borrowed", - self.bccx.loan_path_to_str(move_path))); + self.bccx.loan_path_to_str(move_path)); self.bccx.span_note( loan_span, format!("borrow of `{}` occurs here", @@ -727,11 +722,10 @@ fn check_loans_in_fn<'a>(this: &mut CheckLoanCtxt<'a>, match move_err { MoveOk => {} MoveWhileBorrowed(loan_path, loan_span) => { - this.bccx.span_err( - cap_var.span, - format!("cannot move `{}` into closure \ + span_err!(this.bccx, cap_var.span, A0017, + "cannot move `{}` into closure \ because it is borrowed", - this.bccx.loan_path_to_str(move_path))); + this.bccx.loan_path_to_str(move_path)); this.bccx.span_note( loan_span, format!("borrow of `{}` occurs here", diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs index 9ba8e00dc8ecb..bc3ebc92afff7 100644 --- a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs @@ -104,10 +104,9 @@ fn check_is_legal_to_move_from(bccx: &BorrowckCtxt, mc::cat_deref(_, _, mc::unsafe_ptr(..)) | mc::cat_stack_upvar(..) | mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, .. }) => { - bccx.span_err( - cmt0.span, - format!("cannot move out of {}", - bccx.cmt_to_str(cmt))); + span_err!(bccx, cmt0.span, A0018, + "cannot move out of {}", + bccx.cmt_to_str(cmt)); false } @@ -143,11 +142,10 @@ fn check_is_legal_to_move_from(bccx: &BorrowckCtxt, match ty::get(b.ty).sty { ty::ty_struct(did, _) | ty::ty_enum(did, _) => { if ty::has_dtor(bccx.tcx, did) { - bccx.span_err( - cmt0.span, - format!("cannot move out of type `{}`, \ + span_err!(bccx, cmt0.span, A0019, + "cannot move out of type `{}`, \ which defines the `Drop` trait", - b.ty.user_string(bccx.tcx))); + b.ty.user_string(bccx.tcx)); false } else { check_is_legal_to_move_from(bccx, cmt0, b) diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 5160233ecbf13..0a041a79d4856 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -510,9 +510,7 @@ impl BorrowckCtxt { } pub fn report(&self, err: BckError) { - self.span_err( - err.span, - self.bckerr_to_str(err)); + self.raise_bck_err(err); self.note_and_explain_bckerr(err); } @@ -529,20 +527,18 @@ impl BorrowckCtxt { match move.kind { move_data::Declared => { - self.tcx.sess.span_err( - use_span, - format!("{} of possibly uninitialized value: `{}`", - verb, - self.loan_path_to_str(lp))); + span_err!(self.tcx.sess, use_span, A0006, + "{} of possibly uninitialized value: `{}`", + verb, + self.loan_path_to_str(lp)); } _ => { let partially = if lp == moved_lp {""} else {"partially "}; - self.tcx.sess.span_err( - use_span, - format!("{} of {}moved value: `{}`", - verb, - partially, - self.loan_path_to_str(lp))); + span_err!(self.tcx.sess, use_span, A0007, + "{} of {}moved value: `{}`", + verb, + partially, + self.loan_path_to_str(lp)); } } @@ -616,43 +612,46 @@ impl BorrowckCtxt { lp: &LoanPath, assign: &move_data::Assignment) { - self.tcx.sess.span_err( - span, - format!("re-assignment of immutable variable `{}`", - self.loan_path_to_str(lp))); + span_err!(self.tcx.sess, span, A0008, + "re-assignment of immutable variable `{}`", + self.loan_path_to_str(lp)); self.tcx.sess.span_note( assign.span, format!("prior assignment occurs here")); } - pub fn span_err(&self, s: Span, m: &str) { - self.tcx.sess.span_err(s, m); + pub fn span_err_with_diagnostic_code(&self, s: Span, c: &str, m: &str) { + self.tcx.sess.span_err_with_diagnostic_code(s, c, m); } pub fn span_note(&self, s: Span, m: &str) { self.tcx.sess.span_note(s, m); } - pub fn bckerr_to_str(&self, err: BckError) -> ~str { + pub fn raise_bck_err(&self, err: BckError) { match err.code { err_mutbl(lk) => { - format!("cannot borrow {} {} as {}", - err.cmt.mutbl.to_user_str(), - self.cmt_to_str(err.cmt), - self.mut_to_str(lk)) + span_err!(self.tcx.sess, err.span, A0001, + "cannot borrow {} {} as {}", + err.cmt.mutbl.to_user_str(), + self.cmt_to_str(err.cmt), + self.mut_to_str(lk)); } err_out_of_root_scope(..) => { - format!("cannot root managed value long enough") + span_err!(self.tcx.sess, err.span, A0002, + "cannot root managed value long enough"); } err_out_of_scope(..) => { - format!("borrowed value does not live long enough") + span_err!(self.tcx.sess, err.span, A0003, + "borrowed value does not live long enough"); } err_freeze_aliasable_const => { // Means that the user borrowed a ~T or enum value // residing in &const or @const pointer. Terrible // error message, but then &const and @const are // supposed to be going away. - format!("unsafe borrow of aliasable, const value") + span_err!(self.tcx.sess, err.span, A0004, + "unsafe borrow of aliasable, const value"); } err_borrowed_pointer_too_short(..) => { let descr = match opt_loan_path(err.cmt) { @@ -660,9 +659,10 @@ impl BorrowckCtxt { None => self.cmt_to_str(err.cmt), }; - format!("lifetime of {} is too short to guarantee \ - its contents can be safely reborrowed", - descr) + span_err!(self.tcx.sess, err.span, A0005, + "lifetime of {} is too short to guarantee \ + its contents can be safely reborrowed", + descr); } } } @@ -678,21 +678,18 @@ impl BorrowckCtxt { match cause { mc::AliasableOther => { - self.tcx.sess.span_err( - span, - format!("{} in an aliasable location", prefix)); + span_err!(self.tcx.sess, span, A0009, + "{} in an aliasable location", prefix); } mc::AliasableManaged => { - self.tcx.sess.span_err(span, format!("{} in a `@` pointer", - prefix)) + span_err!(self.tcx.sess, span, A0010, + "{} in a `@` pointer", prefix); } mc::AliasableBorrowed(m) => { - self.tcx.sess.span_err( - span, - format!("{} in a `&{}` pointer; \ - try an `&mut` instead", - prefix, - self.mut_to_keyword(m))); + span_err!(self.tcx.sess, span, A0011, + "{} in a `&{}` pointer; try an `&mut` instead", + prefix, + self.mut_to_keyword(m)); } } } diff --git a/src/librustc/middle/typeck/infer/test.rs b/src/librustc/middle/typeck/infer/test.rs index dc8e090596d0c..7e5ea6d380034 100644 --- a/src/librustc/middle/typeck/infer/test.rs +++ b/src/librustc/middle/typeck/infer/test.rs @@ -16,6 +16,7 @@ Note: This module is only compiled when doing unit testing. */ +use diag_db; use driver::diagnostic; use driver::driver::{optgroups, build_session_options, build_session}; use driver::driver::{str_input, build_configuration}; @@ -61,7 +62,7 @@ fn setup_env(test_name: &str, source_string: &str) -> Env { let region_map = HashMap(); let lang_items = LanguageItems::new(); - let parse_sess = parse::new_parse_sess(None); + let parse_sess = parse::new_parse_sess(None, diag_db::load()); let crate = parse_crate_from_source_str( test_name.to_str(), @source_string.to_str(), cfg, parse_sess); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 947038bc8d85f..d0c3b784eaca8 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -11,6 +11,7 @@ use rustc; use rustc::{driver, middle}; use rustc::middle::privacy; +use rustc::diag_db; use syntax::ast; use syntax::diagnostic; @@ -46,7 +47,7 @@ fn get_ast_and_resolve(cpath: &Path, phase_2_configure_and_expand, phase_3_run_analysis_passes}; - let parsesess = parse::new_parse_sess(None); + let parsesess = parse::new_parse_sess(None, diag_db::load()); let input = file_input(cpath.clone()); let sessopts = @driver::session::options { @@ -58,7 +59,7 @@ fn get_ast_and_resolve(cpath: &Path, }; - let diagnostic_handler = syntax::diagnostic::mk_handler(None); + let diagnostic_handler = syntax::diagnostic::mk_handler(None, diag_db::load()); let span_diagnostic_handler = syntax::diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 31a500718eed9..1a065cb2ee926 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -20,6 +20,7 @@ use extra::getopts; use extra::test; use rustc::driver::driver; use rustc::driver::session; +use rustc::diag_db; use syntax::diagnostic; use syntax::parse; @@ -32,7 +33,7 @@ use passes; use visit_ast::RustdocVisitor; pub fn run(input: &str, matches: &getopts::Matches) -> int { - let parsesess = parse::new_parse_sess(None); + let parsesess = parse::new_parse_sess(None, diag_db::load()); let input = driver::file_input(Path::new(input)); let libs = matches.opt_strs("L").map(|s| Path::new(s.as_slice())); let libs = @RefCell::new(libs.move_iter().collect()); @@ -46,7 +47,7 @@ pub fn run(input: &str, matches: &getopts::Matches) -> int { }; - let diagnostic_handler = diagnostic::mk_handler(None); + let diagnostic_handler = diagnostic::mk_handler(None, diag_db::load()); let span_diagnostic_handler = diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm); @@ -94,7 +95,7 @@ pub fn run(input: &str, matches: &getopts::Matches) -> int { fn runtest(test: &str, cratename: &str, libs: HashSet) { let test = maketest(test, cratename); - let parsesess = parse::new_parse_sess(None); + let parsesess = parse::new_parse_sess(None, diag_db::load()); let input = driver::str_input(test); let sessopts = @session::options { @@ -106,7 +107,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet) { .. (*session::basic_options()).clone() }; - let diagnostic_handler = diagnostic::mk_handler(None); + let diagnostic_handler = diagnostic::mk_handler(None, diag_db::load()); let span_diagnostic_handler = diagnostic::mk_span_handler(diagnostic_handler, parsesess.cm); diff --git a/src/libsyntax/diag_db.md b/src/libsyntax/diag_db.md new file mode 100644 index 0000000000000..36c13b6da5271 --- /dev/null +++ b/src/libsyntax/diag_db.md @@ -0,0 +1 @@ +("", "", "") /* placeholder */ \ No newline at end of file diff --git a/src/libsyntax/diag_db.rs b/src/libsyntax/diag_db.rs new file mode 100644 index 0000000000000..a3cb7d51ee12d --- /dev/null +++ b/src/libsyntax/diag_db.rs @@ -0,0 +1,150 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The diagnostic database. +//! +//! Extended information about Rust diagnostics is included in the +//! diag_db.md file and can be loaded at runtime with the `load` +//! function. + +use std::cell::RefCell; +use std::hashmap::HashMap; + +/// Load the database of extended diagnostic descriptions +pub fn load() -> DiagnosticDb { + DiagnosticDb::new(~[load_raw]) +} + +pub fn load_raw() -> ~[RawInfo] { + ~[include!("diag_db.md")] +} + +pub type RawInfo = (&'static str, &'static str, &'static str); +pub type Initializer = fn() -> ~[RawInfo]; + +#[deriving(Clone)] +pub struct DiagnosticInfo { + code: &'static str, + msg: &'static str, + desc: &'static str +} + +pub struct DiagnosticDb { + state: RefCell +} + +pub enum DiagnosticDbState { + Uninitialized(~[Initializer]), + Initialized(HashMap<&'static str, DiagnosticInfo>) +} + +impl DiagnosticDb { + pub fn new(initializers: ~[Initializer]) -> DiagnosticDb { + DiagnosticDb { + state: RefCell::new(Uninitialized(initializers)) + } + } + + fn get_map(&self, f: |&mut HashMap<&'static str, DiagnosticInfo>| -> T) -> T { + let mut new_map; + { + let mut state = self.state.borrow_mut(); + match *state.get() { + Uninitialized(ref initializers) => { + let mut map = HashMap::new(); + for initr in initializers.iter() { + let raw_info = (*initr)(); + for &(code, msg, desc) in raw_info.iter() { + let info = DiagnosticInfo { code: code, msg: msg, desc: desc }; + map.insert(code, info); + } + } + new_map = Some(map); + } + Initialized(ref mut map) => { + return f(map); + } + } + } + + match new_map { + Some(new_map) => { + self.state.set(Initialized(new_map)); + return self.get_map(f); + } + None => unreachable!() + } + } + + pub fn get_info(&self, code: &str) -> Option { + self.get_map(|map| { + match map.find_equiv(&code) { + Some(&info) => Some(info), + None => None + } + }) + } +} + +impl DiagnosticInfo { + /// Returns a markdown-formatted explanation of the diagnostic + pub fn format(&self) -> ~str { + format!("\\# {}: {}\n\n{}", self.code, self.msg, self.desc.trim()) + } +} + +/// Print extended information about a single diagnostic code to the console. +/// Returns false if the DB contains no information about the code. +pub fn explain_diagnostic(db: &DiagnosticDb, code: &str) -> bool { + match db.get_info(code) { + Some(info) => { + println!("\n{}\n", info.format()) + true + } + None => false + } +} + +pub fn explain_diag_help() { + println!("\nRust includes extended documentation about some compiler errors\n\ + that explain in greater depth what the errors means, present examples,\n\ + and suggestions for how to fix them.\n\ + \n\ + Each Rust error message has a corresponding code. When emitted by\n\ + a tool the code will be included in square brackets, like `[A0001]`. If\n\ + the error has additional documentation the code will be appended with\n\ + an asterisk, as in `[A0002*]`.\n\ + \n\ + To view the extended documentation, run `rustc --explain A0002`, relpacing\n\ + 'A0002' with your error code.\n\ + \n\ + The extend and quality of extended error documentation depends on user\n\ + contributions. To learn how to improve Rust's error documentation visit\n\ + http://github.com/mozilla/rust/wiki/Note-extended-diagnostics.\n"); +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn smoke_test() { + fn load() -> ~[RawInfo] { + ~[("A0000", "message", "description")] + } + + let db = DiagnosticDb::new(~[load]); + let info = db.get_info("A0000"); + let text = info.unwrap().format(); + assert!(text.contains("A0000")); + assert!(text.contains("message")); + assert!(text.contains("description")); + } +} diff --git a/src/libsyntax/diag_index.rs b/src/libsyntax/diag_index.rs new file mode 100644 index 0000000000000..e6c760257380c --- /dev/null +++ b/src/libsyntax/diag_index.rs @@ -0,0 +1,9 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. diff --git a/src/libsyntax/diag_macros.rs b/src/libsyntax/diag_macros.rs new file mode 100644 index 0000000000000..210dcfba0684b --- /dev/null +++ b/src/libsyntax/diag_macros.rs @@ -0,0 +1,177 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The macros for managing the Rust compiler's diagnostic codes. + +#[macro_escape]; + +/// Register a diagnostic code at compile time +/// +/// Registers a diagnostic code at compile time so it can be used later +/// for reporting a diagnostic. All diagnostic codes must be registered once +/// and only once before use (lexically) in `alert_*!` or `desc_diag!` +/// macros. +/// +/// Registration of diagnostics ensures that only known diagnostic codes are +/// used in diagnostic messages and only known diagnostic codes have extended +/// descriptions. It also creates a central place where all existing +/// diagnostic codes (both in use and not) are stored so that it is easy +/// to pick the next available diagnostic code when creating a new one. +#[cfg(not(stage0))] +macro_rules! reg_diag ( + ($name: tt) => { + __tt_map_insert!(DIAGNOSTIC_REGISTRY, $name, $name) + } +) + +#[cfg(stage0)] +macro_rules! reg_diag ( + ($name: tt) => { + } +) + +#[cfg(not(stage0))] +macro_rules! reg_diag_msg ( + ($name: tt, $msg: tt) => { { + // Validate that the diagnostic code is registered + let _ = stringify!(__tt_map_get_expr!(DIAGNOSTIC_REGISTRY, $name)); + // Insert the diagnostic message into the DIAGNOSTIC_MSG table + // so that desc_diag can retrieve it later. This also prevents the + // same diagnostic from being raised in two different places. + mod insert { __tt_map_insert!(DIAGNOSTIC_MSG, $name, $msg) } + } } +) + +#[cfg(stage0)] +macro_rules! reg_diag_msg ( + ($name: tt, $msg: tt) => { { + } } +) + +macro_rules! report_diag ( + ($f: expr, $name: tt, $msg: expr, $($arg: expr), *) => { { + reg_diag_msg!($name, $msg); + let msg: &str = format!($msg, $($arg), *); + $f(stringify!($name), msg); + } }; + ($f: expr, $name: tt, $msg: expr) => { { + reg_diag_msg!($name, $msg); + $f(stringify!($name), $msg); + } } +) + +macro_rules! report_diag_sp ( + ($f: expr, $sp: expr, $name: tt, $msg: expr, $($arg: expr), *) => { { + reg_diag_msg!($name, $msg); + let msg: &str = format!($msg, $($arg), *); + $f(sp, stringify!($name), msg); + } }; + ($f: expr, $sp: expr, $name: tt, $msg: expr) => { { + reg_diag_msg!($name, $msg); + $f(sp, stringify!($name), $msg); + } } +) + +/// Raise a diagnostic at the 'fatal' level +/// +/// Report a diagnostic, registering its message literal at compile time so that +/// it can be retreived later (at compile time) by the `desc_diag!` macro. +/// +/// This must be called (lexically) before a `desc_diag` on the same diagnostic code. +macro_rules! alert_fatal ( + ($sess: expr, $name: tt, $msg: expr, $($arg: expr), *) => ( + report_diag!(|c, m| $sess.fatal_with_diagnostic_code(c, m), + $name, $msg, $($arg), *); + ); + ($sess: expr, $name: tt, $msg: expr) => ( + report_diag!(|c, m| $sess.fatal_with_diagnostic_code(c, m), + $name, $msg); + ) +) + +/// Raise a diagnostic at the 'error' level +macro_rules! alert_err ( + ($sess: expr, $name: tt, $msg: expr, $($arg: expr), *) => ( + report_diag!(|c, m| $sess.err_with_diagnostic_code(c, m), + $name, $msg, $($arg), *); + ); + ($sess: expr, $name: tt, $msg: expr) => ( + report_diag!(|c, m| $sess.err_with_diagnostic_code(c, m), + $name, $msg); + ) +) + +/// Raise a diagnostic at the 'error' level +macro_rules! alert_warn ( + ($sess: expr, $name: tt, $msg: expr, $($arg: expr), *) => ( + report_diag!(|c, m| $sess.warn_with_diagnostic_code(c, m), + $name, $msg, $($arg), *); + ); + ($sess: expr, $name: tt, $msg: expr) => ( + report_diag!(|c, m| $sess.warn_with_diagnostic_code(c, m), + $name, $msg); + ) +) + +/// Raise a diagnostic at the 'fatal' level +macro_rules! span_fatal ( + ($sess: expr, $sp: expr, $name: tt, $msg: expr, $($arg: expr), *) => ( + report_diag!(|c, m| $sess.span_fatal_with_diagnostic_code($sp, c, m), + $name, $msg, $($arg), *); + ); + ($sess: expr, $sp: expr, $name: tt, $msg: expr) => ( + report_diag!(|c, m| $sess.span_fatal_with_diagnostic_code($sp, c, m), + $name, $msg); + ) +) + +/// Raise a diagnostic at the 'error' level +macro_rules! span_err ( + ($sess: expr, $sp: expr, $name: tt, $msg: expr, $($arg: expr), *) => ( + report_diag!(|c, m| $sess.span_err_with_diagnostic_code($sp, c, m), + $name, $msg, $($arg), *); + ); + ($sess: expr, $sp: expr, $name: tt, $msg: expr) => ( + report_diag!(|c, m| $sess.span_err_with_diagnostic_code($sp, c, m), + $name, $msg); + ) +) + +/// Raise a diagnostic at the 'warning' level +macro_rules! span_warn ( + ($sess: expr, $sp: expr, $name: tt, $msg: expr, $($arg: expr), *) => ( + report_diag!(|c, m| $sess.span_warn_with_diagnostic_code($sp, c, m), + $name, $msg, $($arg), *); + ); + ($sess: expr, $sp: expr, $name: tt, $msg: expr) => ( + report_diag!(|c, m| $sess.span_warn_with_diagnostic_code($sp, c, m), + $name, $msg); + ) +) + +/// Describe a diagnostic code and return info about it at runtime +/// +/// Returns a tuple of strings containing (the diagnostic code, the diagnostic +/// message reported for the code, the extended description of the diagnostic). +/// Repated calls to this macro can be used to provide extended documentation about +/// errors and to build up a database of diagnostic information. +#[cfg(not(stage0))] +macro_rules! desc_diag ( + ($name: tt, $desc: expr) => { + (stringify!($name), __tt_map_get_expr!(DIAGNOSTIC_MSG, $name), $desc) + } +) + +#[cfg(stage0)] +macro_rules! desc_diag ( + ($name: tt, $desc: expr) => { + (stringify!($name), "unknown", $desc) + } +) diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs index 18479d4ef414d..8201007feb4ac 100644 --- a/src/libsyntax/diagnostic.rs +++ b/src/libsyntax/diagnostic.rs @@ -10,6 +10,7 @@ use codemap::{Pos, Span}; use codemap; +use diag_db::DiagnosticDb; use std::cell::Cell; use std::io; @@ -40,13 +41,24 @@ impl SpanHandler { self.handler.emit(Some((&*self.cm, sp)), msg, Fatal); fail!(); } + pub fn span_fatal_with_diagnostic_code(@self, sp: Span, code: &str, msg: &str) -> ! { + self.handler.emit_with_code(Some((&*self.cm, sp)), code, msg, Fatal); + fail!(); + } 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_diagnostic_code(@self, sp: Span, code: &str, msg: &str) { + self.handler.emit_with_code(Some((&*self.cm, sp)), code, msg, Error); + self.handler.bump_err_count(); + } pub fn span_warn(@self, sp: Span, msg: &str) { self.handler.emit(Some((&*self.cm, sp)), msg, Warning); } + pub fn span_warn_with_diagnostic_code(@self, sp: Span, code: &str, msg: &str) { + self.handler.emit_with_code(Some((&*self.cm, sp)), code, msg, Warning); + } pub fn span_note(@self, sp: Span, msg: &str) { self.handler.emit(Some((&*self.cm, sp)), msg, Note); } @@ -67,15 +79,28 @@ impl SpanHandler { pub struct Handler { err_count: Cell, emit: @Emitter, + diag_db: DiagnosticDb, + /// Indicates that we've emitted a diagnostic with extended info + saw_extended_info: Cell, } impl Handler { pub fn fatal(@self, msg: &str) -> ! { - self.emit.emit(None, msg, Fatal); + self.emit(None, msg, Fatal); + self.emit_extended_info_explainer(); + fail!(); + } + pub fn fatal_with_diagnostic_code(@self, code: &str, msg: &str) -> ! { + self.emit_with_code(None, code, msg, Fatal); + self.emit_extended_info_explainer(); fail!(); } pub fn err(@self, msg: &str) { - self.emit.emit(None, msg, Error); + self.emit(None, msg, Error); + self.bump_err_count(); + } + pub fn err_with_diagnostic_code(@self, code: &str, msg: &str) { + self.emit_with_code(None, code, msg, Error); self.bump_err_count(); } pub fn bump_err_count(@self) { @@ -100,10 +125,13 @@ impl Handler { self.fatal(s); } pub fn warn(@self, msg: &str) { - self.emit.emit(None, msg, Warning); + self.emit(None, msg, Warning); + } + pub fn warn_with_diagnostic_code(@self, code: &str, msg: &str) { + self.emit_with_code(None, code, msg, Warning); } pub fn note(@self, msg: &str) { - self.emit.emit(None, msg, Note); + self.emit(None, msg, Note); } pub fn bug(@self, msg: &str) -> ! { self.fatal(ice_msg(msg)); @@ -117,6 +145,34 @@ impl Handler { lvl: Level) { self.emit.emit(cmsp, msg, lvl); } + pub fn emit_with_code(@self, cmsp: Option<(&codemap::CodeMap, Span)>, + code: &str, msg: &str, lvl: Level) { + let msg = if self.have_extended_info_for_code(code) { + self.saw_extended_info.set(true); + format!("{} [{}*]", msg, code) + } else { + format!("{} [{}]", msg, code) + }; + self.emit.emit(cmsp, msg, lvl); + } + + fn have_extended_info_for_code(@self, code: &str) -> bool { + self.diag_db.get_info(code).is_some() + } + + fn emit_extended_info_explainer(@self) { + if self.saw_extended_info.get() { + self.note( + "some of these errors have extended documentation (indicated by the asterisk \ + next to the error code). Use `rustc --explain [code]` to get addional \ + information."); + } else { + self.note( + "none of these errors have extended documentation (indicated by the asterisk \ + next to the error code). Use 'rustc --explain help` to learn how to contribute \ + documentation for Rust errors."); + } + } } pub fn ice_msg(msg: &str) -> ~str { @@ -132,7 +188,7 @@ pub fn mk_span_handler(handler: @Handler, cm: @codemap::CodeMap) } } -pub fn mk_handler(emitter: Option<@Emitter>) -> @Handler { +pub fn mk_handler(emitter: Option<@Emitter>, db: DiagnosticDb) -> @Handler { let emit: @Emitter = match emitter { Some(e) => e, None => @DefaultEmitter as @Emitter @@ -141,6 +197,8 @@ pub fn mk_handler(emitter: Option<@Emitter>) -> @Handler { @Handler { err_count: Cell::new(0), emit: emit, + diag_db: db, + saw_extended_info: Cell::new(false) } } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 76135f31e31fe..3a1f938d4305d 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -277,6 +277,12 @@ pub fn syntax_expander_table() -> SyntaxEnv { syntax_expanders.insert(intern(&"trace_macros"), builtin_normal_tt_no_ctxt( ext::trace_macros::expand_trace_macros)); + syntax_expanders.insert(intern(&"__tt_map_insert"), + builtin_normal_tt_no_ctxt( + ext::tt_map::insert_expr)); + syntax_expanders.insert(intern(&"__tt_map_get_expr"), + builtin_normal_tt_no_ctxt( + ext::tt_map::get_expr)); syntax_expanders } @@ -289,7 +295,9 @@ pub struct ExtCtxt { backtrace: Option<@ExpnInfo>, mod_path: ~[ast::Ident], - trace_mac: bool + trace_mac: bool, + // State for the hacky __tt_map_* extensions + tt_maps: HashMap>, } impl ExtCtxt { @@ -300,7 +308,8 @@ impl ExtCtxt { cfg: cfg, backtrace: None, mod_path: ~[], - trace_mac: false + trace_mac: false, + tt_maps: HashMap::new() } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 75c0371f62579..6706b6e9a536c 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1080,6 +1080,7 @@ mod test { use util::parser_testing::{string_to_pat, string_to_tts, strs_to_idents}; use visit; use visit::Visitor; + use diag_db; // a visitor that extracts the paths // from a given thingy and puts them in a mutable @@ -1119,7 +1120,7 @@ mod test { // make sure that fail! is present #[test] fn fail_exists_test () { let src = @"fn main() { fail!(\"something appropriately gloomy\");}"; - let sess = parse::new_parse_sess(None); + let sess = parse::new_parse_sess(None, diag_db::load()); let crate_ast = parse::parse_crate_from_source_str( @"", src, @@ -1137,7 +1138,7 @@ mod test { #[test] fn macros_cant_escape_fns_test () { let src = @"fn bogus() {macro_rules! z (() => (3+4))}\ fn inty() -> int { z!() }"; - let sess = parse::new_parse_sess(None); + let sess = parse::new_parse_sess(None, diag_db::load()); let crate_ast = parse::parse_crate_from_source_str( @"", src, @@ -1151,7 +1152,7 @@ mod test { #[test] fn macros_cant_escape_mods_test () { let src = @"mod foo {macro_rules! z (() => (3+4))}\ fn inty() -> int { z!() }"; - let sess = parse::new_parse_sess(None); + let sess = parse::new_parse_sess(None, diag_db::load()); let crate_ast = parse::parse_crate_from_source_str( @"", src, @@ -1164,7 +1165,7 @@ mod test { #[test] fn macros_can_escape_flattened_mods_test () { let src = @"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\ fn inty() -> int { z!() }"; - let sess = parse::new_parse_sess(None); + let sess = parse::new_parse_sess(None, diag_db::load()); let crate_ast = parse::parse_crate_from_source_str( @"", src, @@ -1175,7 +1176,7 @@ mod test { #[test] fn std_macros_must_parse () { let src = super::std_macros(); - let sess = parse::new_parse_sess(None); + let sess = parse::new_parse_sess(None, diag_db::load()); let cfg = ~[]; let item_ast = parse::parse_item_from_source_str( @"", diff --git a/src/libsyntax/ext/tt_map.rs b/src/libsyntax/ext/tt_map.rs new file mode 100644 index 0000000000000..d4ce97e7f5d8c --- /dev/null +++ b/src/libsyntax/ext/tt_map.rs @@ -0,0 +1,112 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/* + * The token-tree map syntax extension maintains an arbitrary number + * of named maps from ident to token-tree. Token trees can be stored + * into a map with the `__tt_map_insert!` macro and retrieved as an + * expression with the `__tt_map_get_expr!` macro. + * + * This is a hack used to maintain the tables of constants used by + * the rustc error reporting system. In particular, it allows string + * literals to be reused in multiple places without duplication. + */ + +use ast; +use codemap::Span; +use ext::base::{ExtCtxt, MacResult, MRExpr, MRItem}; +use ext::build::AstBuilder; +use parse::token; +use parse::token::{gensym, interner_get}; +use parse::new_parser_from_tts; +use std::hashmap::HashMap; + +pub fn insert_expr(ecx: &mut ExtCtxt, sp: Span, + tts: &[ast::TokenTree]) -> MacResult { + + if tts.len() != 5 { + ecx.span_fatal(sp, "incorrect number of arguments"); + } + + let idxs = [1, 3]; + for i in idxs.iter() { + match &tts[*i] { + &ast::TTTok(_, token::COMMA) => (), + _ => ecx.span_fatal(sp, "expecting comma") + } + } + + let map_name = tree_2_name(ecx, &tts[0]); + let key_name = tree_2_name(ecx, &tts[2]); + let expr = tts[4].clone(); + + if !ecx.tt_maps.contains_key(&map_name) { + ecx.tt_maps.insert(map_name.clone(), HashMap::new()); + } + + let existed = { + let mut maybe_map = ecx.tt_maps.find_mut(&map_name); + let map = maybe_map.get_mut_ref(); + !map.insert(key_name, expr) + }; + + if existed { + let key_name = interner_get(key_name); + ecx.span_fatal(sp, format!("key {} already exists in map", key_name)); + } + + // This item isn't used + let dummy_ident = ast::Ident::new(gensym("dummy_name")); + let dummy_item = ecx.item_mod(sp, dummy_ident, ~[], ~[], ~[]); + return MRItem(dummy_item); +} + +pub fn get_expr(ecx: &mut ExtCtxt, sp: Span, + tts: &[ast::TokenTree]) -> MacResult { + + if tts.len() != 3 { + ecx.span_fatal(sp, "incorrect number of arguments"); + } + + match &tts[1] { + &ast::TTTok(_, token::COMMA) => (), + _ => ecx.span_fatal(sp, "expecting comma") + } + + let map_name = tree_2_name(ecx, &tts[0]); + let key_name = tree_2_name(ecx, &tts[2]); + + match ecx.tt_maps.find(&map_name) { + Some(map) => { + match map.find(&key_name) { + Some(map_tree) => { + MRExpr(tree_2_expr(ecx, map_tree)) + } + None => { + let key_name = interner_get(key_name); + ecx.span_fatal(sp, format!("key {} does not exist in map", key_name)); + } + } + } + None => { + ecx.span_fatal(sp, "map does not exist"); + } + } +} + +fn tree_2_name(ecx: &ExtCtxt, tts: &ast::TokenTree) -> ast::Name { + let mut p = new_parser_from_tts(ecx.parse_sess(), ecx.cfg.clone(), ~[tts.clone()]); + return p.parse_ident().name; +} + +fn tree_2_expr(ecx: &ExtCtxt, tts: &ast::TokenTree) -> @ast::Expr { + let mut p = new_parser_from_tts(ecx.parse_sess(), ecx.cfg.clone(), ~[tts.clone()]); + return p.parse_expr(); +} diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 0fd4c5c193c2a..e629e5b3c02d4 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -32,6 +32,12 @@ This API is completely unstable and subject to change. extern mod extra; +// Define the diagnostic macros +pub mod diag_macros; +// The index of all diagnostic codes used by this crate. This must be defined +// lexically before any diagnostics are used. +pub mod diag_index; + pub mod util { pub mod interner; #[cfg(test)] @@ -91,6 +97,11 @@ pub mod ext { pub mod concat_idents; pub mod log_syntax; pub mod source_util; + pub mod tt_map; pub mod trace_macros; } + +// The database of extended diagnostic descriptions. Must come lexically +// after all uses of diagnostics. See `diag_macros` for why. +pub mod diag_db; diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs index 9304b84229448..b6b8b2edfaa13 100644 --- a/src/libsyntax/parse/lexer.rs +++ b/src/libsyntax/parse/lexer.rs @@ -961,6 +961,7 @@ mod test { use codemap::{BytePos, CodeMap, Span}; use diagnostic; + use diag_db; use parse::token; use parse::token::{str_to_ident}; @@ -974,7 +975,7 @@ mod test { let cm = CodeMap::new(); let fm = cm.new_filemap(@"zebra.rs", teststr); let span_handler = - diagnostic::mk_span_handler(diagnostic::mk_handler(None),@cm); + diagnostic::mk_span_handler(diagnostic::mk_handler(None, diag_db::load()),@cm); Env { string_reader: new_string_reader(span_handler,fm) } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index ea069c2ffe37d..8f6cef3011b79 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -17,6 +17,7 @@ use codemap; use diagnostic::{SpanHandler, mk_span_handler, mk_handler, Emitter}; use parse::attr::ParserAttr; use parse::parser::Parser; +use diag_db::DiagnosticDb; use std::cell::RefCell; use std::io; @@ -46,11 +47,11 @@ pub struct ParseSess { included_mod_stack: RefCell<~[Path]>, } -pub fn new_parse_sess(demitter: Option<@Emitter>) -> @ParseSess { +pub fn new_parse_sess(demitter: Option<@Emitter>, db: DiagnosticDb) -> @ParseSess { let cm = @CodeMap::new(); @ParseSess { cm: cm, - span_diagnostic: mk_span_handler(mk_handler(demitter), cm), + span_diagnostic: mk_span_handler(mk_handler(demitter, db), cm), included_mod_stack: RefCell::new(~[]), } } diff --git a/src/libsyntax/util/parser_testing.rs b/src/libsyntax/util/parser_testing.rs index 5153ddf1c7d17..2103c9e9773f3 100644 --- a/src/libsyntax/util/parser_testing.rs +++ b/src/libsyntax/util/parser_testing.rs @@ -14,11 +14,12 @@ use parse::{ParseSess,string_to_filemap,filemap_to_tts}; use parse::{new_parser_from_source_str}; use parse::parser::Parser; use parse::token; +use diag_db; // map a string to tts, using a made-up filename: return both the TokenTree's // and the ParseSess pub fn string_to_tts_and_sess (source_str : @str) -> (~[ast::TokenTree], @ParseSess) { - let ps = new_parse_sess(None); + let ps = new_parse_sess(None, diag_db::load()); (filemap_to_tts(ps,string_to_filemap(ps,source_str,@"bogofile")),ps) } @@ -29,7 +30,7 @@ pub fn string_to_tts(source_str : @str) -> ~[ast::TokenTree] { } pub fn string_to_parser_and_sess(source_str: @str) -> (Parser,@ParseSess) { - let ps = new_parse_sess(None); + let ps = new_parse_sess(None, diag_db::load()); (new_parser_from_source_str(ps,~[],@"bogofile",source_str),ps) } diff --git a/src/test/run-pass/tt_map.rs b/src/test/run-pass/tt_map.rs new file mode 100644 index 0000000000000..24137a88be2c2 --- /dev/null +++ b/src/test/run-pass/tt_map.rs @@ -0,0 +1,18 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + + let j = 2; + mod m { __tt_map_insert!(my_tt_map, i, j) } + + let k = __tt_map_get_expr!(my_tt_map, i); + assert!(j == k); +}