diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs index ebdccf59c530..6d4f48595d25 100644 --- a/src/rustc/driver/driver.rs +++ b/src/rustc/driver/driver.rs @@ -157,6 +157,9 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg, time(time_passes, "core injection", bind front::core_inject::maybe_inject_libcore_ref(sess, crate)); + time(time_passes, "building warning settings table", + bind lint::build_settings_crate(sess, crate)); + let ast_map = time(time_passes, "ast indexing", bind syntax::ast_map::map_crate(sess.diagnostic(), *crate)); @@ -204,10 +207,8 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg, bind middle::alias::check_crate(ty_cx, crate)); time(time_passes, "kind checking", bind kind::check_crate(ty_cx, method_map, last_use_map, crate)); - - let _warning_settings = - time(time_passes, "lint checking", - bind lint::check_crate(ty_cx, crate, sess.opts.lint_opts)); + time(time_passes, "lint checking", + bind lint::check_crate(ty_cx, crate)); if upto == cu_no_trans { ret {crate: crate, tcx: some(ty_cx)}; } let outputs = option::get(outputs); @@ -528,6 +529,7 @@ fn build_session_( sopts.maybe_sysroot, sopts.target_triple, sopts.addl_lib_search_paths); + let warning_settings = lint::mk_warning_settings(); @{targ_cfg: target_cfg, opts: sopts, cstore: cstore, @@ -544,7 +546,8 @@ fn build_session_( span_diagnostic: span_diagnostic_handler, filesearch: filesearch, mut building_library: false, - working_dir: os::getcwd()} + working_dir: os::getcwd(), + warning_settings: warning_settings} } fn parse_pretty(sess: session, &&name: str) -> pp_mode { diff --git a/src/rustc/driver/session.rs b/src/rustc/driver/session.rs index 362fa823460f..97e2d0ec4db7 100644 --- a/src/rustc/driver/session.rs +++ b/src/rustc/driver/session.rs @@ -9,6 +9,7 @@ import back::target_strs; import back::link; import middle::lint; + enum os { os_win32, os_macos, os_linux, os_freebsd, } enum arch { arch_x86, arch_x86_64, arch_arm, } @@ -82,7 +83,8 @@ type session = @{targ_cfg: @config, span_diagnostic: diagnostic::span_handler, filesearch: filesearch::filesearch, mut building_library: bool, - working_dir: str}; + working_dir: str, + warning_settings: lint::warning_settings}; impl session for session { fn span_fatal(sp: span, msg: str) -> ! { @@ -127,6 +129,21 @@ impl session for session { fn unimpl(msg: str) -> ! { self.span_diagnostic.handler().unimpl(msg) } + fn span_lint_level(level: lint::level, + sp: span, msg: str) { + alt level { + lint::ignore { } + lint::warn { self.span_warn(sp, msg); } + lint::error { self.span_err(sp, msg); } + } + } + fn span_lint(lint_mode: lint::lint, + expr_id: ast::node_id, item_id: ast::node_id, + span: span, msg: str) { + let level = lint::get_warning_settings_level( + self.warning_settings, lint_mode, expr_id, item_id); + self.span_lint_level(level, span, msg); + } fn next_node_id() -> ast::node_id { ret syntax::parse::next_node_id(self.parse_sess); } diff --git a/src/rustc/middle/lint.rs b/src/rustc/middle/lint.rs index 88444f9a0f14..dc31c75733e7 100644 --- a/src/rustc/middle/lint.rs +++ b/src/rustc/middle/lint.rs @@ -1,3 +1,4 @@ +import driver::session; import driver::session::session; import middle::ty; import syntax::{ast, visit}; @@ -10,8 +11,9 @@ import syntax::print::pprust::expr_to_str; export lint, ctypes, unused_imports; export level, ignore, warn, error; -export lookup_lint, lint_dict, get_lint_dict, check_crate; -export warning_settings, warning_methods; +export lookup_lint, lint_dict, get_lint_dict, get_warning_settings_level; +export check_crate, build_settings_crate, mk_warning_settings; +export warning_settings; #[doc=" @@ -29,6 +31,10 @@ omitted. If we start allowing warn attributes on expressions, we will start having entries for expressions that do not share their enclosing items settings. +This module then, exports two passes: one that populates the warning settings +table in the session and is run early in the compile process, and one that +does a variety of lint checks, and is run late in the compile process. + "] enum lint { @@ -115,6 +121,11 @@ type warning_settings = { settings_map: lint_mode_map }; +fn mk_warning_settings() -> warning_settings { + {default_settings: std::smallintmap::mk(), + settings_map: int_hash()} +} + fn get_warning_level(modes: lint_modes, lint: lint) -> level { alt modes.find(lint as uint) { some(c) { c } @@ -122,32 +133,16 @@ fn get_warning_level(modes: lint_modes, lint: lint) -> level { } } -fn span_lint(tcx: ty::ctxt, level: level, span: span, msg: str) { - alt level { - ignore { } - warn { tcx.sess.span_warn(span, msg); } - error { tcx.sess.span_err(span, msg); } +fn get_warning_settings_level(settings: warning_settings, + lint_mode: lint, + _expr_id: ast::node_id, + item_id: ast::node_id) -> level { + alt settings.settings_map.find(item_id) { + some(modes) { get_warning_level(modes, lint_mode) } + none { get_warning_level(settings.default_settings, lint_mode) } } } -impl warning_methods for warning_settings { - fn get_level(lint_mode: lint, - _expr_id: ast::node_id, item_id: ast::node_id) -> level { - alt self.settings_map.find(item_id) { - some(modes) { get_warning_level(modes, lint_mode) } - none { get_warning_level(self.default_settings, lint_mode) } - } - } - - fn span_lint(tcx: ty::ctxt, lint_mode: lint, - expr_id: ast::node_id, item_id: ast::node_id, - span: span, msg: str) { - let level = self.get_level(lint_mode, expr_id, item_id); - span_lint(tcx, level, span, msg); - } - -} - // This is kind of unfortunate. It should be somewhere else, or we should use // a persistent data structure... fn clone_lint_modes(modes: lint_modes) -> lint_modes { @@ -157,8 +152,7 @@ fn clone_lint_modes(modes: lint_modes) -> lint_modes { type ctxt = {dict: lint_dict, curr: lint_modes, is_default: bool, - lint_mode_map: lint_mode_map, - tcx: ty::ctxt}; + sess: session}; impl methods for ctxt { @@ -175,7 +169,7 @@ impl methods for ctxt { } fn span_lint(level: level, span: span, msg: str) { - span_lint(self.tcx, level, span, msg); + self.sess.span_lint_level(level, span, msg); } #[doc=" @@ -213,7 +207,7 @@ impl methods for ctxt { } } _ { - self.tcx.sess.span_err( + self.sess.span_err( meta.span, "malformed warning attribute"); } @@ -221,8 +215,8 @@ impl methods for ctxt { } } _ { - self.tcx.sess.span_err(meta.span, - "malformed warning attribute"); + self.sess.span_err(meta.span, + "malformed warning attribute"); } } } @@ -249,25 +243,56 @@ fn lookup_lint(dict: lint_dict, s: str) }) } -fn check_item(i: @ast::item, &&cx: ctxt, v: visit::vt) { +fn build_settings_item(i: @ast::item, &&cx: ctxt, v: visit::vt) { cx.with_warn_attrs(i.attrs) {|cx| - for cx.curr.each {|lint, level| - alt int_to_lint(lint as int) { - ctypes { check_item_ctypes(cx, level, i); } - unused_imports { check_item_unused_imports(cx, level, i); } - while_true { check_item_while_true(cx, level, i); } - path_statement { check_item_path_statement(cx, level, i); } - old_vecs { check_item_old_vecs(cx, level, i); } - unrecognized_warning { /* this is checked elsewhere */ } - } - } if !cx.is_default { - cx.lint_mode_map.insert(i.id, cx.curr); + cx.sess.warning_settings.settings_map.insert(i.id, cx.curr); } visit::visit_item(i, cx, v); } } +fn build_settings_crate(sess: session::session, crate: @ast::crate) { + + let cx = {dict: get_lint_dict(), + curr: std::smallintmap::mk(), + is_default: true, + sess: sess}; + + // Install defaults. + for cx.dict.each {|_k, spec| cx.set_level(spec.lint, spec.default); } + + // Install command-line options, overriding defaults. + for sess.opts.lint_opts.each {|pair| + let (lint,level) = pair; + cx.set_level(lint, level); + } + + cx.with_warn_attrs(crate.node.attrs) {|cx| + // Copy out the default settings + for cx.curr.each {|k, v| + sess.warning_settings.default_settings.insert(k, v); + } + + let cx = {is_default: true with cx}; + + let visit = visit::mk_vt(@{ + visit_item: build_settings_item + with *visit::default_visitor() + }); + visit::visit_crate(*crate, cx, visit); + } + + sess.abort_if_errors(); +} + +fn check_item(i: @ast::item, cx: ty::ctxt) { + check_item_ctypes(cx, i); + check_item_while_true(cx, i); + check_item_path_statement(cx, i); + check_item_old_vecs(cx, i); +} + // Take a visitor, and modify it so that it will not proceed past subitems. // This is used to make the simple visitors used for the lint passes // not traverse into subitems, since that is handled by the outer @@ -276,16 +301,17 @@ fn item_stopping_visitor(v: visit::vt) -> visit::vt { visit::mk_vt(@{visit_item: {|_i, _e, _v| } with **v}) } -fn check_item_while_true(cx: ctxt, level: level, it: @ast::item) { +fn check_item_while_true(cx: ty::ctxt, it: @ast::item) { let visit = item_stopping_visitor(visit::mk_simple_visitor(@{ visit_expr: fn@(e: @ast::expr) { alt e.node { ast::expr_while(cond, _) { alt cond.node { ast::expr_lit(@{node: ast::lit_bool(true),_}) { - cx.span_lint( - level, e.span, - "denote infinite loops with loop { ... }"); + cx.sess.span_lint( + while_true, it.id, e.id, + e.span, + "denote infinite loops with loop { ... }"); } _ {} } @@ -298,28 +324,26 @@ fn check_item_while_true(cx: ctxt, level: level, it: @ast::item) { visit::visit_item(it, (), visit); } -fn check_item_unused_imports(_cx: ctxt, _level: level, _it: @ast::item) { - // FIXME: Don't know how to check this in lint yet, it's currently being - // done over in resolve. When resolve is rewritten, do it here instead. -} +fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) { -fn check_item_ctypes(cx: ctxt, level: level, it: @ast::item) { - - fn check_native_fn(cx: ctxt, level: level, decl: ast::fn_decl) { + fn check_native_fn(cx: ty::ctxt, fn_id: ast::node_id, + decl: ast::fn_decl) { let tys = vec::map(decl.inputs) {|a| a.ty }; for vec::each(tys + [decl.output]) {|ty| alt ty.node { ast::ty_path(_, id) { - alt cx.tcx.def_map.get(id) { + alt cx.def_map.get(id) { ast::def_prim_ty(ast::ty_int(ast::ty_i)) { - cx.span_lint( - level, ty.span, + cx.sess.span_lint( + ctypes, fn_id, id, + ty.span, "found rust type `int` in native module, while \ libc::c_int or libc::c_long should be used"); } ast::def_prim_ty(ast::ty_uint(ast::ty_u)) { - cx.span_lint( - level, ty.span, + cx.sess.span_lint( + ctypes, fn_id, id, + ty.span, "found rust type `uint` in native module, while \ libc::c_uint or libc::c_ulong should be used"); } @@ -337,7 +361,7 @@ fn check_item_ctypes(cx: ctxt, level: level, it: @ast::item) { for nmod.items.each {|ni| alt ni.node { ast::native_item_fn(decl, tps) { - check_native_fn(cx, level, decl); + check_native_fn(cx, it.id, decl); } } } @@ -346,15 +370,16 @@ fn check_item_ctypes(cx: ctxt, level: level, it: @ast::item) { } } -fn check_item_path_statement(cx: ctxt, level: level, it: @ast::item) { +fn check_item_path_statement(cx: ty::ctxt, it: @ast::item) { let visit = item_stopping_visitor(visit::mk_simple_visitor(@{ visit_stmt: fn@(s: @ast::stmt) { alt s.node { - ast::stmt_semi(@{id: _, + ast::stmt_semi(@{id: id, node: ast::expr_path(@path), span: _}, _) { - cx.span_lint( - level, s.span, + cx.sess.span_lint( + path_statement, it.id, id, + s.span, "path statement with no effect"); } _ {} @@ -365,7 +390,7 @@ fn check_item_path_statement(cx: ctxt, level: level, it: @ast::item) { visit::visit_item(it, (), visit); } -fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) { +fn check_item_old_vecs(cx: ty::ctxt, it: @ast::item) { let uses_vstore = int_hash(); let visit = item_stopping_visitor(visit::mk_simple_visitor(@{ @@ -375,7 +400,9 @@ fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) { ast::expr_vec(_, _) | ast::expr_lit(@{node: ast::lit_str(_), span:_}) if ! uses_vstore.contains_key(e.id) { - cx.span_lint(level, e.span, "deprecated vec/str expr"); + cx.sess.span_lint( + old_vecs, it.id, e.id, + e.span, "deprecated vec/str expr"); } ast::expr_vstore(@inner, _) { uses_vstore.insert(inner.id, true); @@ -388,13 +415,17 @@ fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) { alt t.node { ast::ty_vec(_) if ! uses_vstore.contains_key(t.id) { - cx.span_lint(level, t.span, "deprecated vec type"); + cx.sess.span_lint( + old_vecs, it.id, t.id, + t.span, "deprecated vec type"); } ast::ty_path(@{span: _, global: _, idents: ids, rp: none, types: _}, _) if ids == ["str"] && (! uses_vstore.contains_key(t.id)) { - cx.span_lint(level, t.span, "deprecated str type"); + cx.sess.span_lint( + old_vecs, it.id, t.id, + t.span, "deprecated str type"); } ast::ty_vstore(inner, _) { @@ -409,45 +440,15 @@ fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) { visit::visit_item(it, (), visit); } +fn check_crate(tcx: ty::ctxt, crate: @ast::crate) { -fn check_crate(tcx: ty::ctxt, crate: @ast::crate, - lint_opts: [(lint, level)]) -> warning_settings { - - fn hash_lint(&&lint: lint) -> uint { lint as uint } - fn eq_lint(&&a: lint, &&b: lint) -> bool { a == b } - - let cx = {dict: get_lint_dict(), - curr: std::smallintmap::mk(), - is_default: true, - lint_mode_map: int_hash(), - tcx: tcx}; - - let mut default_settings = cx.curr; // dummy value - - // Install defaults. - for cx.dict.each {|_k, spec| cx.set_level(spec.lint, spec.default); } - - // Install command-line options, overriding defaults. - for lint_opts.each {|pair| - let (lint,level) = pair; - cx.set_level(lint, level); - } - - cx.with_warn_attrs(crate.node.attrs) {|cx| - default_settings = cx.curr; - let cx = {is_default: true with cx}; - - let visit = visit::mk_vt(@{ - visit_item: check_item - with *visit::default_visitor() - }); - visit::visit_crate(*crate, cx, visit); - } + let v = visit::mk_simple_visitor(@{ + visit_item: fn@(it: @ast::item) { check_item(it, tcx); } + with *visit::default_simple_visitor() + }); + visit::visit_crate(*crate, (), v); tcx.sess.abort_if_errors(); - - ret {default_settings: default_settings, - settings_map: cx.lint_mode_map}; } //