Heavily rework lint infrastructure. Split it into two passes: one that builds the table and one that does the checks. Build the table early and make session know about it fo reasy use.

This commit is contained in:
Michael Sullivan 2012-06-04 16:07:54 -07:00
parent 2d0e7cd272
commit 5a4e53487f
3 changed files with 131 additions and 110 deletions

View File

@ -157,6 +157,9 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
time(time_passes, "core injection", time(time_passes, "core injection",
bind front::core_inject::maybe_inject_libcore_ref(sess, crate)); 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 = let ast_map =
time(time_passes, "ast indexing", time(time_passes, "ast indexing",
bind syntax::ast_map::map_crate(sess.diagnostic(), *crate)); 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)); bind middle::alias::check_crate(ty_cx, crate));
time(time_passes, "kind checking", time(time_passes, "kind checking",
bind kind::check_crate(ty_cx, method_map, last_use_map, crate)); bind kind::check_crate(ty_cx, method_map, last_use_map, crate));
let _warning_settings =
time(time_passes, "lint checking", time(time_passes, "lint checking",
bind lint::check_crate(ty_cx, crate, sess.opts.lint_opts)); bind lint::check_crate(ty_cx, crate));
if upto == cu_no_trans { ret {crate: crate, tcx: some(ty_cx)}; } if upto == cu_no_trans { ret {crate: crate, tcx: some(ty_cx)}; }
let outputs = option::get(outputs); let outputs = option::get(outputs);
@ -528,6 +529,7 @@ fn build_session_(
sopts.maybe_sysroot, sopts.maybe_sysroot,
sopts.target_triple, sopts.target_triple,
sopts.addl_lib_search_paths); sopts.addl_lib_search_paths);
let warning_settings = lint::mk_warning_settings();
@{targ_cfg: target_cfg, @{targ_cfg: target_cfg,
opts: sopts, opts: sopts,
cstore: cstore, cstore: cstore,
@ -544,7 +546,8 @@ fn build_session_(
span_diagnostic: span_diagnostic_handler, span_diagnostic: span_diagnostic_handler,
filesearch: filesearch, filesearch: filesearch,
mut building_library: false, 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 { fn parse_pretty(sess: session, &&name: str) -> pp_mode {

View File

@ -9,6 +9,7 @@ import back::target_strs;
import back::link; import back::link;
import middle::lint; import middle::lint;
enum os { os_win32, os_macos, os_linux, os_freebsd, } enum os { os_win32, os_macos, os_linux, os_freebsd, }
enum arch { arch_x86, arch_x86_64, arch_arm, } enum arch { arch_x86, arch_x86_64, arch_arm, }
@ -82,7 +83,8 @@ type session = @{targ_cfg: @config,
span_diagnostic: diagnostic::span_handler, span_diagnostic: diagnostic::span_handler,
filesearch: filesearch::filesearch, filesearch: filesearch::filesearch,
mut building_library: bool, mut building_library: bool,
working_dir: str}; working_dir: str,
warning_settings: lint::warning_settings};
impl session for session { impl session for session {
fn span_fatal(sp: span, msg: str) -> ! { fn span_fatal(sp: span, msg: str) -> ! {
@ -127,6 +129,21 @@ impl session for session {
fn unimpl(msg: str) -> ! { fn unimpl(msg: str) -> ! {
self.span_diagnostic.handler().unimpl(msg) 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 { fn next_node_id() -> ast::node_id {
ret syntax::parse::next_node_id(self.parse_sess); ret syntax::parse::next_node_id(self.parse_sess);
} }

View File

@ -1,3 +1,4 @@
import driver::session;
import driver::session::session; import driver::session::session;
import middle::ty; import middle::ty;
import syntax::{ast, visit}; import syntax::{ast, visit};
@ -10,8 +11,9 @@ import syntax::print::pprust::expr_to_str;
export lint, ctypes, unused_imports; export lint, ctypes, unused_imports;
export level, ignore, warn, error; export level, ignore, warn, error;
export lookup_lint, lint_dict, get_lint_dict, check_crate; export lookup_lint, lint_dict, get_lint_dict, get_warning_settings_level;
export warning_settings, warning_methods; export check_crate, build_settings_crate, mk_warning_settings;
export warning_settings;
#[doc=" #[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 having entries for expressions that do not share their enclosing items
settings. 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 { enum lint {
@ -115,6 +121,11 @@ type warning_settings = {
settings_map: lint_mode_map 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 { fn get_warning_level(modes: lint_modes, lint: lint) -> level {
alt modes.find(lint as uint) { alt modes.find(lint as uint) {
some(c) { c } 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) { fn get_warning_settings_level(settings: warning_settings,
alt level { lint_mode: lint,
ignore { } _expr_id: ast::node_id,
warn { tcx.sess.span_warn(span, msg); } item_id: ast::node_id) -> level {
error { tcx.sess.span_err(span, msg); } alt settings.settings_map.find(item_id) {
}
}
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) } some(modes) { get_warning_level(modes, lint_mode) }
none { get_warning_level(self.default_settings, lint_mode) } none { get_warning_level(settings.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 // This is kind of unfortunate. It should be somewhere else, or we should use
// a persistent data structure... // a persistent data structure...
fn clone_lint_modes(modes: lint_modes) -> lint_modes { 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, type ctxt = {dict: lint_dict,
curr: lint_modes, curr: lint_modes,
is_default: bool, is_default: bool,
lint_mode_map: lint_mode_map, sess: session};
tcx: ty::ctxt};
impl methods for ctxt { impl methods for ctxt {
@ -175,7 +169,7 @@ impl methods for ctxt {
} }
fn span_lint(level: level, span: span, msg: str) { 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=" #[doc="
@ -213,7 +207,7 @@ impl methods for ctxt {
} }
} }
_ { _ {
self.tcx.sess.span_err( self.sess.span_err(
meta.span, meta.span,
"malformed warning attribute"); "malformed warning attribute");
} }
@ -221,7 +215,7 @@ impl methods for ctxt {
} }
} }
_ { _ {
self.tcx.sess.span_err(meta.span, self.sess.span_err(meta.span,
"malformed warning attribute"); "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<ctxt>) { fn build_settings_item(i: @ast::item, &&cx: ctxt, v: visit::vt<ctxt>) {
cx.with_warn_attrs(i.attrs) {|cx| 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 { 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); 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. // 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 // This is used to make the simple visitors used for the lint passes
// not traverse into subitems, since that is handled by the outer // not traverse into subitems, since that is handled by the outer
@ -276,15 +301,16 @@ fn item_stopping_visitor<E>(v: visit::vt<E>) -> visit::vt<E> {
visit::mk_vt(@{visit_item: {|_i, _e, _v| } with **v}) 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(@{ let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
visit_expr: fn@(e: @ast::expr) { visit_expr: fn@(e: @ast::expr) {
alt e.node { alt e.node {
ast::expr_while(cond, _) { ast::expr_while(cond, _) {
alt cond.node { alt cond.node {
ast::expr_lit(@{node: ast::lit_bool(true),_}) { ast::expr_lit(@{node: ast::lit_bool(true),_}) {
cx.span_lint( cx.sess.span_lint(
level, e.span, while_true, it.id, e.id,
e.span,
"denote infinite loops with loop { ... }"); "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); visit::visit_item(it, (), visit);
} }
fn check_item_unused_imports(_cx: ctxt, _level: level, _it: @ast::item) { fn check_item_ctypes(cx: ty::ctxt, 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: ctxt, level: level, it: @ast::item) { fn check_native_fn(cx: ty::ctxt, fn_id: ast::node_id,
decl: ast::fn_decl) {
fn check_native_fn(cx: ctxt, level: level, decl: ast::fn_decl) {
let tys = vec::map(decl.inputs) {|a| a.ty }; let tys = vec::map(decl.inputs) {|a| a.ty };
for vec::each(tys + [decl.output]) {|ty| for vec::each(tys + [decl.output]) {|ty|
alt ty.node { alt ty.node {
ast::ty_path(_, id) { 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)) { ast::def_prim_ty(ast::ty_int(ast::ty_i)) {
cx.span_lint( cx.sess.span_lint(
level, ty.span, ctypes, fn_id, id,
ty.span,
"found rust type `int` in native module, while \ "found rust type `int` in native module, while \
libc::c_int or libc::c_long should be used"); libc::c_int or libc::c_long should be used");
} }
ast::def_prim_ty(ast::ty_uint(ast::ty_u)) { ast::def_prim_ty(ast::ty_uint(ast::ty_u)) {
cx.span_lint( cx.sess.span_lint(
level, ty.span, ctypes, fn_id, id,
ty.span,
"found rust type `uint` in native module, while \ "found rust type `uint` in native module, while \
libc::c_uint or libc::c_ulong should be used"); 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| for nmod.items.each {|ni|
alt ni.node { alt ni.node {
ast::native_item_fn(decl, tps) { 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(@{ let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
visit_stmt: fn@(s: @ast::stmt) { visit_stmt: fn@(s: @ast::stmt) {
alt s.node { alt s.node {
ast::stmt_semi(@{id: _, ast::stmt_semi(@{id: id,
node: ast::expr_path(@path), node: ast::expr_path(@path),
span: _}, _) { span: _}, _) {
cx.span_lint( cx.sess.span_lint(
level, s.span, path_statement, it.id, id,
s.span,
"path statement with no effect"); "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); 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 uses_vstore = int_hash();
let visit = item_stopping_visitor(visit::mk_simple_visitor(@{ 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_vec(_, _) |
ast::expr_lit(@{node: ast::lit_str(_), span:_}) ast::expr_lit(@{node: ast::lit_str(_), span:_})
if ! uses_vstore.contains_key(e.id) { 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, _) { ast::expr_vstore(@inner, _) {
uses_vstore.insert(inner.id, true); 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 { alt t.node {
ast::ty_vec(_) ast::ty_vec(_)
if ! uses_vstore.contains_key(t.id) { 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, ast::ty_path(@{span: _, global: _, idents: ids,
rp: none, types: _}, _) rp: none, types: _}, _)
if ids == ["str"] && (! uses_vstore.contains_key(t.id)) { 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, _) { 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); visit::visit_item(it, (), visit);
} }
fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
fn check_crate(tcx: ty::ctxt, crate: @ast::crate, let v = visit::mk_simple_visitor(@{
lint_opts: [(lint, level)]) -> warning_settings { visit_item: fn@(it: @ast::item) { check_item(it, tcx); }
with *visit::default_simple_visitor()
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); visit::visit_crate(*crate, (), v);
}
tcx.sess.abort_if_errors(); tcx.sess.abort_if_errors();
ret {default_settings: default_settings,
settings_map: cx.lint_mode_map};
} }
// //