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",
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 {

View File

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

View File

@ -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<ctxt>) {
fn build_settings_item(i: @ast::item, &&cx: ctxt, v: visit::vt<ctxt>) {
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<E>(v: visit::vt<E>) -> visit::vt<E> {
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};
}
//