Re-implement lint with less emphasis on item ids

This way it's much easier to add lints throughout compilation correctly, and
functions on impls can alter the way lints are emitted.
This commit is contained in:
Alex Crichton 2013-04-30 01:15:17 -04:00
parent 77c98f0815
commit 030c666cc1
16 changed files with 507 additions and 605 deletions

View file

@ -17,7 +17,6 @@ use container::{Container, Mutable, Map, Set};
use cmp::{Eq, Equiv};
use hash::Hash;
use old_iter::BaseIter;
use hash::Hash;
use old_iter;
use option::{None, Option, Some};
use rand::RngUtil;

View file

@ -171,7 +171,6 @@ pub mod write {
use back::link::{output_type_assembly, output_type_bitcode};
use back::link::{output_type_exe, output_type_llvm_assembly};
use back::link::{output_type_object};
use back::link::output_type;
use driver::session::Session;
use driver::session;
use lib::llvm::llvm;

View file

@ -22,6 +22,7 @@ use middle;
use util::common::time;
use util::ppaux;
use core::hashmap::HashMap;
use core::int;
use core::io;
use core::os;
@ -200,9 +201,6 @@ pub fn compile_rest(sess: Session,
crate = time(time_passes, ~"core injection", ||
front::core_inject::maybe_inject_libcore_ref(sess, crate));
time(time_passes, ~"building lint settings table", ||
lint::build_settings_crate(sess, crate));
let ast_map = time(time_passes, ~"ast indexing", ||
syntax::ast_map::map_crate(sess.diagnostic(), crate));
@ -709,7 +707,6 @@ pub fn build_session_(sopts: @session::options,
&sopts.maybe_sysroot,
sopts.target_triple,
/*bad*/copy sopts.addl_lib_search_paths);
let lint_settings = lint::mk_lint_settings();
@Session_ {
targ_cfg: target_cfg,
opts: sopts,
@ -723,7 +720,7 @@ pub fn build_session_(sopts: @session::options,
filesearch: filesearch,
building_library: @mut false,
working_dir: os::getcwd(),
lint_settings: lint_settings
lints: @mut HashMap::new(),
}
}

View file

@ -26,6 +26,8 @@ use syntax::{ast, codemap};
use syntax::abi;
use syntax;
use core::hashmap::HashMap;
#[deriving(Eq)]
pub enum os { os_win32, os_macos, os_linux, os_android, os_freebsd, }
@ -170,7 +172,7 @@ pub struct Session_ {
filesearch: @filesearch::FileSearch,
building_library: @mut bool,
working_dir: Path,
lint_settings: lint::LintSettings
lints: @mut HashMap<ast::node_id, ~[(lint::lint, codemap::span, ~str)]>,
}
pub type Session = @Session_;
@ -230,15 +232,12 @@ pub impl Session_ {
}
}
}
fn span_lint(@self, lint_mode: lint::lint,
expr_id: ast::node_id,
item_id: ast::node_id,
span: span,
msg: &str) {
let level = lint::get_lint_settings_level(
self.lint_settings, lint_mode, expr_id, item_id);
let msg = fmt!("%s [-W %s]", msg, lint::get_lint_name(lint_mode));
self.span_lint_level(level, span, msg);
fn add_lint(@self, lint: lint::lint, id: ast::node_id, sp: span, msg: ~str) {
match self.lints.find_mut(&id) {
Some(arr) => { arr.push((lint, sp, msg)); return; }
None => {}
}
self.lints.insert(id, ~[(lint, sp, msg)]);
}
fn next_node_id(@self) -> ast::node_id {
return syntax::parse::next_node_id(self.parse_sess);

View file

@ -8,7 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use driver::session::Session;
use driver::session;
use middle::ty;
use middle::pat_util;
@ -19,7 +18,7 @@ use std::smallintmap::SmallIntMap;
use syntax::attr;
use syntax::codemap::span;
use syntax::codemap;
use syntax::{ast, visit};
use syntax::{ast, visit, ast_util};
/**
* A 'lint' check is a kind of miscellaneous constraint that a user _might_
@ -86,7 +85,13 @@ struct LintSpec {
default: level
}
pub type LintDict = @HashMap<~str, LintSpec>;
pub type LintDict = HashMap<~str, LintSpec>;
enum AttributedNode<'self> {
Item(@ast::item),
Method(&'self ast::method),
Crate(@ast::crate),
}
static lint_table: &'static [(&'static str, LintSpec)] = &[
("ctypes",
@ -225,7 +230,7 @@ pub fn get_lint_dict() -> LintDict {
for lint_table.each|&(k, v)| {
map.insert(k.to_str(), v);
}
return @map;
return map;
}
pub fn get_lint_name(lint_mode: lint) -> ~str {
@ -237,61 +242,31 @@ pub fn get_lint_name(lint_mode: lint) -> ~str {
fail!();
}
// This is a highly not-optimal set of data structure decisions.
type LintModes = @mut SmallIntMap<level>;
type LintModeMap = @mut HashMap<ast::node_id, LintModes>;
// settings_map maps node ids of items with non-default lint settings
// to their settings; default_settings contains the settings for everything
// not in the map.
pub struct LintSettings {
default_settings: LintModes,
settings_map: LintModeMap
}
pub fn mk_lint_settings() -> LintSettings {
LintSettings {
default_settings: @mut SmallIntMap::new(),
settings_map: @mut HashMap::new()
}
}
pub fn get_lint_level(modes: LintModes, lint: lint) -> level {
match modes.find(&(lint as uint)) {
Some(&c) => c,
None => allow
}
}
pub fn get_lint_settings_level(settings: LintSettings,
lint_mode: lint,
_expr_id: ast::node_id,
item_id: ast::node_id)
-> level {
match settings.settings_map.find(&item_id) {
Some(&modes) => get_lint_level(modes, lint_mode),
None => get_lint_level(settings.default_settings, lint_mode)
}
}
// This is kind of unfortunate. It should be somewhere else, or we should use
// a persistent data structure...
fn clone_lint_modes(modes: LintModes) -> LintModes {
@mut (copy *modes)
}
type LintModes = SmallIntMap<level>;
type LintModeMap = HashMap<ast::node_id, LintModes>;
struct Context {
dict: LintDict,
// All known lint modes (string versions)
dict: @LintDict,
// Current levels of each lint warning
curr: LintModes,
is_default: bool,
sess: Session
// context we're checking in (used to access fields like sess)
tcx: ty::ctxt,
// When recursing into an attributed node of the ast which modifies lint
// levels, this stack keeps track of the previous lint levels of whatever
// was modified.
lint_stack: ~[(lint, level)],
}
pub impl Context {
impl Context {
fn get_level(&self, lint: lint) -> level {
get_lint_level(self.curr, lint)
match self.curr.find(&(lint as uint)) {
Some(&c) => c,
None => allow
}
}
fn set_level(&self, lint: lint, level: level) {
fn set_level(&mut self, lint: lint, level: level) {
if level == allow {
self.curr.remove(&(lint as uint));
} else {
@ -299,8 +274,8 @@ pub impl Context {
}
}
fn span_lint(&self, level: level, span: span, msg: ~str) {
self.sess.span_lint_level(level, span, msg);
fn span_lint(&self, lint: lint, span: span, msg: &str) {
self.tcx.sess.span_lint_level(self.get_level(lint), span, msg);
}
/**
@ -308,41 +283,13 @@ pub impl Context {
* current lint context, call the provided function, then reset the
* lints in effect to their previous state.
*/
fn with_lint_attrs(&self, attrs: ~[ast::attribute], f: &fn(Context)) {
let mut new_ctxt = *self;
let mut triples = ~[];
for [allow, warn, deny, forbid].each |level| {
let level_name = level_to_str(*level);
let metas =
attr::attr_metas(attr::find_attrs_by_name(attrs, level_name));
for metas.each |meta| {
match meta.node {
ast::meta_list(_, ref metas) => {
for metas.each |meta| {
match meta.node {
ast::meta_word(ref lintname) => {
triples.push((*meta,
*level,
/*bad*/copy *lintname));
}
_ => {
self.sess.span_err(
meta.span,
"malformed lint attribute");
}
}
}
}
_ => {
self.sess.span_err(meta.span,
"malformed lint attribute");
}
}
}
}
fn with_lint_attrs(@mut self, attrs: &[ast::attribute], f: &fn()) {
// Parse all of the lint attributes, and then add them all to the
// current dictionary of lint information. Along the way, keep a history
// of what we changed so we can roll everything back after invoking the
// specified closure
let triples = extract_lints(self.tcx.sess, attrs);
let mut pushed = 0u;
for triples.each |triple| {
// FIXME(#3874): it would be nicer to write this...
// let (meta, level, lintname) = /*bad*/copy *pair;
@ -350,147 +297,144 @@ pub impl Context {
(ref meta, level, lintname) => (meta, level, lintname)
};
match self.dict.find(lintname) {
let lint = match self.dict.find(lintname) {
None => {
self.span_lint(
new_ctxt.get_level(unrecognized_lint),
unrecognized_lint,
meta.span,
fmt!("unknown `%s` attribute: `%s`",
level_to_str(level), *lintname));
loop
}
Some(lint) => {
Some(lint) => { lint.lint }
};
if new_ctxt.get_level(lint.lint) == forbid &&
level != forbid {
self.span_lint(
forbid,
meta.span,
fmt!("%s(%s) overruled by outer forbid(%s)",
level_to_str(level),
*lintname, *lintname));
let now = self.get_level(lint);
if now == forbid && level != forbid {
self.tcx.sess.span_err(meta.span,
fmt!("%s(%s) overruled by outer forbid(%s)",
level_to_str(level),
*lintname, *lintname));
loop
}
self.lint_stack.push((lint, now));
pushed += 1;
self.set_level(lint, level);
}
f();
// rollback
for pushed.times {
let (lint, level) = self.lint_stack.pop();
self.set_level(lint, level);
}
}
fn process(&self, n: AttributedNode, v: @visit::SimpleVisitor) {
self.process_visitor(n, visit::mk_simple_visitor(v));
}
fn process_visitor(&self, n: AttributedNode, v: visit::vt<()>) {
let v = item_stopping_visitor(v);
match n {
Item(it) => visit::visit_item(it, (), v),
Crate(c) => visit::visit_crate(c, (), v),
// Can't use visit::visit_method_helper because the
// item_stopping_visitor has overridden visit_fn(&fk_method(... ))
// to be a no-op, so manually invoke visit_fn.
Method(m) => visit::visit_fn(&visit::fk_method(copy m.ident,
&m.generics,
m),
&m.decl,
&m.body,
m.span,
m.id,
(),
v)
}
}
}
pub fn extract_lints(sess: session::Session,
attrs: &[ast::attribute])
-> ~[(@ast::meta_item, level, @~str)]
{
let mut triples = ~[];
for [allow, warn, deny, forbid].each |&level| {
let level_name = level_to_str(level);
let metas =
attr::attr_metas(attr::find_attrs_by_name(attrs, level_name));
for metas.each |meta| {
match meta.node {
ast::meta_list(_, ref metas) => {
for metas.each |meta| {
match meta.node {
ast::meta_word(lintname) => {
triples.push((*meta,
level,
lintname));
}
_ => {
sess.span_err(meta.span, ~"malformed lint attribute");
}
}
}
// we do multiple unneeded copies of the
// map if many attributes are set, but
// this shouldn't actually be a problem...
let c = clone_lint_modes(new_ctxt.curr);
new_ctxt = Context {
is_default: false,
curr: c,
.. new_ctxt
};
new_ctxt.set_level(lint.lint, level);
}
_ => {
sess.span_err(meta.span, ~"malformed lint attribute");
}
}
}
f(new_ctxt);
}
}
fn build_settings_item(i: @ast::item, cx: Context, v: visit::vt<Context>) {
do cx.with_lint_attrs(/*bad*/copy i.attrs) |cx| {
if !cx.is_default {
cx.sess.lint_settings.settings_map.insert(i.id, cx.curr);
}
visit::visit_item(i, cx, v);
}
}
pub fn build_settings_crate(sess: session::Session, crate: @ast::crate) {
let cx = Context {
dict: get_lint_dict(),
curr: @mut SmallIntMap::new(),
is_default: true,
sess: sess
};
// Install defaults.
for cx.dict.each_value |&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);
}
do cx.with_lint_attrs(/*bad*/copy crate.node.attrs) |cx| {
// Copy out the default settings
for cx.curr.each |&k, &v| {
sess.lint_settings.default_settings.insert(k, v);
}
let cx = Context {
is_default: true,
.. cx
};
let visit = visit::mk_vt(@visit::Visitor {
visit_item: build_settings_item,
.. *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_non_camel_case_types(cx, i);
check_item_heap(cx, i);
check_item_type_limits(cx, i);
check_item_default_methods(cx, i);
check_item_unused_unsafe(cx, i);
check_item_unused_mut(cx, i);
return triples;
}
// 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
// lint visitor.
fn item_stopping_visitor<E>(v: visit::vt<E>) -> visit::vt<E> {
visit::mk_vt(@visit::Visitor {visit_item: |_i, _e, _v| { },
.. **(ty_stopping_visitor(v))})
fn item_stopping_visitor<E: Copy>(v: visit::vt<E>) -> visit::vt<E> {
visit::mk_vt(@visit::Visitor {
visit_item: |_i, _e, _v| { },
visit_fn: |fk, fd, b, s, id, e, v| {
match *fk {
visit::fk_method(*) => {}
_ => visit::visit_fn(fk, fd, b, s, id, e, v)
}
},
.. **(ty_stopping_visitor(v))})
}
fn ty_stopping_visitor<E>(v: visit::vt<E>) -> visit::vt<E> {
visit::mk_vt(@visit::Visitor {visit_ty: |_t, _e, _v| { },.. **v})
}
fn check_item_while_true(cx: ty::ctxt, it: @ast::item) {
let visit = item_stopping_visitor(
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: |e: @ast::expr| {
match e.node {
ast::expr_while(cond, _) => {
match cond.node {
ast::expr_lit(@codemap::spanned {
node: ast::lit_bool(true), _}) =>
{
cx.sess.span_lint(
while_true, e.id, it.id,
e.span,
"denote infinite loops \
with loop { ... }");
}
_ => ()
fn check_item_while_true(cx: @mut Context, n: AttributedNode) {
cx.process(n, @visit::SimpleVisitor {
visit_expr: |e: @ast::expr| {
match e.node {
ast::expr_while(cond, _) => {
match cond.node {
ast::expr_lit(@codemap::spanned {
node: ast::lit_bool(true), _}) =>
{
cx.span_lint(while_true, e.span,
"denote infinite loops with \
loop { ... }");
}
_ => ()
}
_ => ()
}
},
.. *visit::default_simple_visitor()
}));
visit::visit_item(it, (), visit);
_ => ()
}
},
.. *visit::default_simple_visitor()
});
}
fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
fn check_item_type_limits(cx: @mut Context, n: AttributedNode) {
fn is_valid<T:cmp::Ord>(binop: ast::binop, v: T,
min: T, max: T) -> bool {
match binop {
@ -534,7 +478,7 @@ fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
}
}
fn check_limits(cx: ty::ctxt, binop: ast::binop, l: &ast::expr,
fn check_limits(cx: @mut Context, binop: ast::binop, l: &ast::expr,
r: &ast::expr) -> bool {
let (lit, expr, swap) = match (&l.node, &r.node) {
(&ast::expr_lit(_), _) => (l, r, true),
@ -543,12 +487,12 @@ fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
};
// Normalize the binop so that the literal is always on the RHS in
// the comparison
let norm_binop = if (swap) {
let norm_binop = if swap {
rev_binop(binop)
} else {
binop
};
match ty::get(ty::expr_ty(cx, @/*bad*/copy *expr)).sty {
match ty::get(ty::expr_ty(cx.tcx, @/*bad*/copy *expr)).sty {
ty::ty_int(int_ty) => {
let (min, max) = int_ty_range(int_ty);
let lit_val: i64 = match lit.node {
@ -592,36 +536,29 @@ fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
ast::expr_binary(ref binop, @ref l, @ref r) => {
if is_comparison(*binop)
&& !check_limits(cx, *binop, l, r) {
cx.sess.span_lint(
type_limits, e.id, it.id, e.span,
"comparison is useless due to type limits");
cx.span_lint(type_limits, e.span,
"comparison is useless due to type limits");
}
}
_ => ()
}
};
let visit = item_stopping_visitor(
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: visit_expr,
.. *visit::default_simple_visitor()
}));
visit::visit_item(it, (), visit);
cx.process(n, @visit::SimpleVisitor {
visit_expr: visit_expr,
.. *visit::default_simple_visitor()
});
}
fn check_item_default_methods(cx: ty::ctxt, item: @ast::item) {
fn check_item_default_methods(cx: @mut Context, item: @ast::item) {
match item.node {
ast::item_trait(_, _, ref methods) => {
for methods.each |method| {
match *method {
ast::required(*) => {}
ast::provided(*) => {
cx.sess.span_lint(
default_methods,
item.id,
item.id,
item.span,
"default methods are experimental");
cx.span_lint(default_methods, item.span,
"default methods are experimental");
}
}
}
@ -630,25 +567,21 @@ fn check_item_default_methods(cx: ty::ctxt, item: @ast::item) {
}
}
fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
fn check_foreign_fn(cx: ty::ctxt, fn_id: ast::node_id,
decl: &ast::fn_decl) {
fn check_item_ctypes(cx: @mut Context, it: @ast::item) {
fn check_foreign_fn(cx: @mut Context, decl: &ast::fn_decl) {
let tys = vec::map(decl.inputs, |a| a.ty );
for vec::each(vec::append_one(tys, decl.output)) |ty| {
match ty.node {
ast::ty_path(_, id) => {
match cx.def_map.get_copy(&id) {
match cx.tcx.def_map.get_copy(&id) {
ast::def_prim_ty(ast::ty_int(ast::ty_i)) => {
cx.sess.span_lint(
ctypes, id, fn_id,
ty.span,
cx.span_lint(ctypes, ty.span,
"found rust type `int` in foreign module, while \
libc::c_int or libc::c_long should be used");
}
ast::def_prim_ty(ast::ty_uint(ast::ty_u)) => {
cx.sess.span_lint(
ctypes, id, fn_id,
ty.span,
cx.span_lint(ctypes, ty.span,
"found rust type `uint` in foreign module, while \
libc::c_uint or libc::c_ulong should be used");
}
@ -665,7 +598,7 @@ fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
for nmod.items.each |ni| {
match ni.node {
ast::foreign_item_fn(ref decl, _, _) => {
check_foreign_fn(cx, it.id, decl);
check_foreign_fn(cx, decl);
}
// FIXME #4622: Not implemented.
ast::foreign_item_const(*) => {}
@ -676,106 +609,96 @@ fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
}
}
fn check_item_heap(cx: ty::ctxt, it: @ast::item) {
fn check_item_heap(cx: @mut Context, n: AttributedNode) {
fn check_type_for_lint(cx: ty::ctxt, lint: lint,
node: ast::node_id,
item: ast::node_id,
span: span, ty: ty::t) {
fn check_type_for_lint(cx: @mut Context, lint: lint, span: span, ty: ty::t) {
if cx.get_level(lint) == allow { return }
if get_lint_settings_level(cx.sess.lint_settings,
lint, node, item) != allow {
let mut n_box = 0;
let mut n_uniq = 0;
ty::fold_ty(cx, ty, |t| {
match ty::get(t).sty {
ty::ty_box(_) => n_box += 1,
ty::ty_uniq(_) => n_uniq += 1,
_ => ()
};
t
});
let mut n_box = 0;
let mut n_uniq = 0;
ty::fold_ty(cx.tcx, ty, |t| {
match ty::get(t).sty {
ty::ty_box(_) => n_box += 1,
ty::ty_uniq(_) => n_uniq += 1,
_ => ()
};
t
});
if (n_uniq > 0 && lint != managed_heap_memory) {
let s = ty_to_str(cx, ty);
let m = ~"type uses owned (~ type) pointers: " + s;
cx.sess.span_lint(lint, node, item, span, m);
}
if n_uniq > 0 && lint != managed_heap_memory {
let s = ty_to_str(cx.tcx, ty);
let m = ~"type uses owned (~ type) pointers: " + s;
cx.span_lint(lint, span, m);
}
if (n_box > 0 && lint != owned_heap_memory) {
let s = ty_to_str(cx, ty);
let m = ~"type uses managed (@ type) pointers: " + s;
cx.sess.span_lint(lint, node, item, span, m);
}
if n_box > 0 && lint != owned_heap_memory {
let s = ty_to_str(cx.tcx, ty);
let m = ~"type uses managed (@ type) pointers: " + s;
cx.span_lint(lint, span, m);
}
}
fn check_type(cx: ty::ctxt,
node: ast::node_id,
item: ast::node_id,
span: span, ty: ty::t) {
for [managed_heap_memory,
owned_heap_memory,
heap_memory].each |lint| {
check_type_for_lint(cx, *lint, node, item, span, ty);
fn check_type(cx: @mut Context, span: span, ty: ty::t) {
for [managed_heap_memory, owned_heap_memory, heap_memory].each |lint| {
check_type_for_lint(cx, *lint, span, ty);
}
}
match n {
Item(it) => {
match it.node {
ast::item_fn(*) |
ast::item_ty(*) |
ast::item_enum(*) |
ast::item_struct(*) => check_type(cx, it.span,
ty::node_id_to_type(cx.tcx,
it.id)),
_ => ()
}
}
match it.node {
ast::item_fn(*) |
ast::item_ty(*) |
ast::item_enum(*) |
ast::item_struct(*) => check_type(cx, it.id, it.id, it.span,
ty::node_id_to_type(cx, it.id)),
_ => ()
}
// If it's a struct, we also have to check the fields' types
match it.node {
ast::item_struct(struct_def, _) => {
for struct_def.fields.each |struct_field| {
check_type(cx, struct_field.node.id, it.id,
struct_field.span,
ty::node_id_to_type(cx, struct_field.node.id));
// If it's a struct, we also have to check the fields' types
match it.node {
ast::item_struct(struct_def, _) => {
for struct_def.fields.each |struct_field| {
check_type(cx, struct_field.span,
ty::node_id_to_type(cx.tcx,
struct_field.node.id));
}
}
_ => ()
}
}
_ => ()
}
let visit = item_stopping_visitor(
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: |e: @ast::expr| {
let ty = ty::expr_ty(cx, e);
check_type(cx, e.id, it.id, e.span, ty);
},
.. *visit::default_simple_visitor()
}));
visit::visit_item(it, (), visit);
cx.process(n, @visit::SimpleVisitor {
visit_expr: |e| {
let ty = ty::expr_ty(cx.tcx, e);
check_type(cx, e.span, ty);
},
.. *visit::default_simple_visitor()
});
}
fn check_item_path_statement(cx: ty::ctxt, it: @ast::item) {
let visit = item_stopping_visitor(
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_stmt: |s: @ast::stmt| {
match s.node {
ast::stmt_semi(
@ast::expr { id: id, node: ast::expr_path(_), _ },
_
) => {
cx.sess.span_lint(
path_statement, id, it.id,
s.span,
"path statement with no effect");
}
_ => ()
fn check_item_path_statement(cx: @mut Context, n: AttributedNode) {
cx.process(n, @visit::SimpleVisitor {
visit_stmt: |s| {
match s.node {
ast::stmt_semi(
@ast::expr { node: ast::expr_path(_), _ },
_
) => {
cx.span_lint(path_statement, s.span,
"path statement with no effect");
}
},
.. *visit::default_simple_visitor()
}));
visit::visit_item(it, (), visit);
_ => ()
}
},
.. *visit::default_simple_visitor()
});
}
fn check_item_non_camel_case_types(cx: ty::ctxt, it: @ast::item) {
fn check_item_non_camel_case_types(cx: @mut Context, it: @ast::item) {
fn is_camel_case(cx: ty::ctxt, ident: ast::ident) -> bool {
let ident = cx.sess.str_of(ident);
assert!(!ident.is_empty());
@ -799,61 +722,54 @@ fn check_item_non_camel_case_types(cx: ty::ctxt, it: @ast::item) {
}
}
fn check_case(cx: ty::ctxt, ident: ast::ident,
expr_id: ast::node_id, item_id: ast::node_id,
span: span) {
if !is_camel_case(cx, ident) {
cx.sess.span_lint(
non_camel_case_types, expr_id, item_id, span,
"type, variant, or trait should have \
a camel case identifier");
fn check_case(cx: @mut Context, ident: ast::ident, span: span) {
if !is_camel_case(cx.tcx, ident) {
cx.span_lint(non_camel_case_types, span,
"type, variant, or trait should have \
a camel case identifier");
}
}
match it.node {
ast::item_ty(*) | ast::item_struct(*) |
ast::item_trait(*) => {
check_case(cx, it.ident, it.id, it.id, it.span)
check_case(cx, it.ident, it.span)
}
ast::item_enum(ref enum_definition, _) => {
check_case(cx, it.ident, it.id, it.id, it.span);
check_case(cx, it.ident, it.span);
for enum_definition.variants.each |variant| {
check_case(cx, variant.node.name,
variant.node.id, it.id, variant.span);
check_case(cx, variant.node.name, variant.span);
}
}
_ => ()
}
}
fn check_item_unused_unsafe(cx: ty::ctxt, it: @ast::item) {
fn check_item_unused_unsafe(cx: @mut Context, n: AttributedNode) {
let visit_expr: @fn(@ast::expr) = |e| {
match e.node {
ast::expr_block(ref blk) if blk.node.rules == ast::unsafe_blk => {
if !cx.used_unsafe.contains(&blk.node.id) {
cx.sess.span_lint(unused_unsafe, blk.node.id, it.id,
blk.span,
"unnecessary `unsafe` block");
if !cx.tcx.used_unsafe.contains(&blk.node.id) {
cx.span_lint(unused_unsafe, blk.span,
"unnecessary `unsafe` block");
}
}
_ => ()
}
};
let visit = item_stopping_visitor(
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_expr: visit_expr,
.. *visit::default_simple_visitor()
}));
visit::visit_item(it, (), visit);
cx.process(n, @visit::SimpleVisitor {
visit_expr: visit_expr,
.. *visit::default_simple_visitor()
});
}
fn check_item_unused_mut(tcx: ty::ctxt, it: @ast::item) {
fn check_item_unused_mut(cx: @mut Context, n: AttributedNode) {
let check_pat: @fn(@ast::pat) = |p| {
let mut used = false;
let mut bindings = 0;
do pat_util::pat_bindings(tcx.def_map, p) |_, id, _, _| {
used = used || tcx.used_mut_nodes.contains(&id);
do pat_util::pat_bindings(cx.tcx.def_map, p) |_, id, _, _| {
used = used || cx.tcx.used_mut_nodes.contains(&id);
bindings += 1;
}
if !used {
@ -862,7 +778,7 @@ fn check_item_unused_mut(tcx: ty::ctxt, it: @ast::item) {
} else {
"variables do not need to be mutable"
};
tcx.sess.span_lint(unused_mut, p.id, it.id, p.span, msg);
cx.span_lint(unused_mut, p.span, msg);
}
};
@ -874,45 +790,113 @@ fn check_item_unused_mut(tcx: ty::ctxt, it: @ast::item) {
}
};
let visit = item_stopping_visitor(
visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_local: |l| {
if l.node.is_mutbl {
check_pat(l.node.pat);
}
},
visit_fn: |_, fd, _, _, _| visit_fn_decl(fd),
visit_ty_method: |tm| visit_fn_decl(&tm.decl),
visit_struct_method: |sm| visit_fn_decl(&sm.decl),
visit_trait_method: |tm| {
match *tm {
ast::required(ref tm) => visit_fn_decl(&tm.decl),
ast::provided(m) => visit_fn_decl(&m.decl),
}
},
.. *visit::default_simple_visitor()
}));
visit::visit_item(it, (), visit);
cx.process(n, @visit::SimpleVisitor {
visit_local: |l| {
if l.node.is_mutbl {
check_pat(l.node.pat);
}
},
visit_fn: |_, fd, _, _, _| visit_fn_decl(fd),
visit_ty_method: |tm| visit_fn_decl(&tm.decl),
visit_struct_method: |sm| visit_fn_decl(&sm.decl),
visit_trait_method: |tm| {
match *tm {
ast::required(ref tm) => visit_fn_decl(&tm.decl),
ast::provided(m) => visit_fn_decl(&m.decl),
}
},
.. *visit::default_simple_visitor()
});
}
fn check_fn(_: ty::ctxt,
fk: &visit::fn_kind,
_: &ast::fn_decl,
_: &ast::blk,
_: span,
id: ast::node_id) {
debug!("lint check_fn fk=%? id=%?", fk, id);
fn check_item_session_lints(cx: @mut Context, n: AttributedNode) {
cx.process_visitor(n, ast_util::id_visitor(|id| {
match cx.tcx.sess.lints.pop(&id) {
None => {},
Some(l) => {
info!("id %?", id);
do vec::consume(l) |_, (lint, span, msg)| {
cx.span_lint(lint, span, msg)
}
}
}
}));
}
fn check_attributed_node(cx: @mut Context, n: AttributedNode) {
check_item_while_true(cx, n);
check_item_path_statement(cx, n);
check_item_heap(cx, n);
check_item_type_limits(cx, n);
check_item_unused_unsafe(cx, n);
check_item_unused_mut(cx, n);
check_item_session_lints(cx, n);
}
pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
let v = visit::mk_simple_visitor(@visit::SimpleVisitor {
visit_item: |it|
check_item(it, tcx),
visit_fn: |fk, decl, body, span, id|
check_fn(tcx, fk, decl, body, span, id),
.. *visit::default_simple_visitor()
});
visit::visit_crate(crate, (), v);
let cx = @mut Context {
dict: @get_lint_dict(),
curr: SmallIntMap::new(),
tcx: tcx,
lint_stack: ~[],
};
// Install defaults.
for cx.dict.each_value |spec| {
cx.set_level(spec.lint, spec.default);
}
// Install command-line options, overriding defaults.
for tcx.sess.opts.lint_opts.each |&(lint, level)| {
cx.set_level(lint, level);
}
// type inference doesn't like this being declared below, we need to tell it
// what the type of this first function is...
let visit_item:
@fn(@ast::item, @mut Context, visit::vt<@mut Context>) =
|it, cx, vt| {
do cx.with_lint_attrs(it.attrs) {
check_item_ctypes(cx, it);
check_item_non_camel_case_types(cx, it);
check_item_default_methods(cx, it);
check_item_heap(cx, it);
check_attributed_node(cx, Item(it));
visit::visit_item(it, cx, vt);
}
};
do cx.with_lint_attrs(crate.node.attrs) {
check_item_session_lints(cx, Crate(crate));
visit::visit_crate(crate, cx, visit::mk_vt(@visit::Visitor {
visit_item: visit_item,
visit_fn: |fk, decl, body, span, id, cx, vt| {
match *fk {
visit::fk_method(_, _, m) => {
do cx.with_lint_attrs(m.attrs) {
check_attributed_node(cx, Method(m));
visit::visit_fn(fk, decl, body, span, id, cx, vt);
}
}
_ => {
visit::visit_fn(fk, decl, body, span, id, cx, vt);
}
}
},
.. *visit::default_visitor()
}));
}
for tcx.sess.lints.each |_, v| {
for v.each |t| {
match *t {
(lint, span, ref msg) =>
tcx.sess.span_bug(span, fmt!("unprocessed lint %?: %s",
lint, *msg))
}
}
}
tcx.sess.abort_if_errors();
}

View file

@ -153,15 +153,13 @@ pub fn check_crate(tcx: ty::ctxt,
visit_local: visit_local,
visit_expr: visit_expr,
visit_arm: visit_arm,
visit_item: visit_item,
.. *visit::default_visitor()
});
let initial_maps = @mut IrMaps(tcx,
method_map,
variable_moves_map,
capture_map,
0);
capture_map);
visit::visit_crate(crate, initial_maps, visitor);
tcx.sess.abort_if_errors();
}
@ -240,15 +238,12 @@ struct IrMaps {
capture_info_map: HashMap<node_id, @~[CaptureInfo]>,
var_kinds: ~[VarKind],
lnks: ~[LiveNodeKind],
cur_item: node_id,
}
fn IrMaps(tcx: ty::ctxt,
method_map: typeck::method_map,
variable_moves_map: moves::VariableMovesMap,
capture_map: moves::CaptureMap,
cur_item: node_id)
capture_map: moves::CaptureMap)
-> IrMaps {
IrMaps {
tcx: tcx,
@ -262,7 +257,6 @@ fn IrMaps(tcx: ty::ctxt,
capture_info_map: HashMap::new(),
var_kinds: ~[],
lnks: ~[],
cur_item: cur_item,
}
}
@ -341,13 +335,6 @@ pub impl IrMaps {
}
}
fn visit_item(item: @item, this: @mut IrMaps, v: vt<@mut IrMaps>) {
let old_cur_item = this.cur_item;
this.cur_item = item.id;
visit::visit_item(item, this, v);
this.cur_item = old_cur_item;
}
fn visit_fn(fk: &visit::fn_kind,
decl: &fn_decl,
body: &blk,
@ -362,8 +349,7 @@ fn visit_fn(fk: &visit::fn_kind,
let fn_maps = @mut IrMaps(this.tcx,
this.method_map,
this.variable_moves_map,
this.capture_map,
this.cur_item);
this.capture_map);
unsafe {
debug!("creating fn_maps: %x", transmute(&*fn_maps));
@ -1802,13 +1788,11 @@ pub impl Liveness {
};
if is_assigned {
self.tcx.sess.span_lint(unused_variable, id,
self.ir.cur_item, sp,
self.tcx.sess.add_lint(unused_variable, id, sp,
fmt!("variable `%s` is assigned to, \
but never used", **name));
} else {
self.tcx.sess.span_lint(unused_variable, id,
self.ir.cur_item, sp,
self.tcx.sess.add_lint(unused_variable, id, sp,
fmt!("unused variable: `%s`", **name));
}
}
@ -1821,8 +1805,7 @@ pub impl Liveness {
ln: LiveNode, var: Variable) {
if self.live_on_exit(ln, var).is_none() {
for self.should_warn(var).each |name| {
self.tcx.sess.span_lint(dead_assignment, id,
self.ir.cur_item, sp,
self.tcx.sess.add_lint(dead_assignment, id, sp,
fmt!("value assigned to `%s` is never read", **name));
}
}

View file

@ -16,47 +16,10 @@ use metadata::csearch::get_type_name_if_impl;
use metadata::cstore::find_extern_mod_stmt_cnum;
use metadata::decoder::{def_like, dl_def, dl_field, dl_impl};
use middle::lang_items::LanguageItems;
use middle::lint::{allow, level, unused_imports};
use middle::lint::{get_lint_level, get_lint_settings_level};
use middle::lint::unused_imports;
use middle::pat_util::pat_bindings;
use syntax::ast::{TyParamBound, ty_closure};
use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm};
use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk};
use syntax::ast::{bind_infer, bind_by_ref, bind_by_copy};
use syntax::ast::{crate, decl_item, def, def_arg, def_binding};
use syntax::ast::{def_const, def_foreign_mod, def_fn, def_id, def_label};
use syntax::ast::{def_local, def_mod, def_prim_ty, def_region, def_self};
use syntax::ast::{def_self_ty, def_static_method, def_struct, def_ty};
use syntax::ast::{def_ty_param, def_typaram_binder, def_trait};
use syntax::ast::{def_upvar, def_use, def_variant, explicit_self_, expr, expr_assign_op};
use syntax::ast::{expr_binary, expr_break, expr_field};
use syntax::ast::{expr_fn_block, expr_index, expr_method_call, expr_path};
use syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param};
use syntax::ast::{def_upvar, def_use, def_variant, div, eq};
use syntax::ast::{expr, expr_again, expr_assign_op};
use syntax::ast::{expr_index, expr_loop};
use syntax::ast::{expr_path, expr_self, expr_struct, expr_unary, fn_decl};
use syntax::ast::{foreign_item, foreign_item_const, foreign_item_fn, ge};
use syntax::ast::Generics;
use syntax::ast::{gt, ident, inherited, item, item_struct};
use syntax::ast::{item_const, item_enum, item_fn, item_foreign_mod};
use syntax::ast::{item_impl, item_mac, item_mod, item_trait, item_ty, le};
use syntax::ast::{local, local_crate, lt, method, mul};
use syntax::ast::{named_field, ne, neg, node_id, pat, pat_enum, pat_ident};
use syntax::ast::{Path, pat_lit, pat_range, pat_struct};
use syntax::ast::{prim_ty, private, provided};
use syntax::ast::{public, required, rem, shl, shr, stmt_decl};
use syntax::ast::{struct_field, struct_variant_kind};
use syntax::ast::{sty_static, subtract, trait_ref, tuple_variant_kind, Ty};
use syntax::ast::{ty_bool, ty_char, ty_f, ty_f32, ty_f64, ty_float, ty_i};
use syntax::ast::{ty_i16, ty_i32, ty_i64, ty_i8, ty_int, TyParam, ty_path};
use syntax::ast::{ty_str, ty_u, ty_u16, ty_u32, ty_u64, ty_u8, ty_uint};
use syntax::ast::unnamed_field;
use syntax::ast::{variant, view_item, view_item_extern_mod};
use syntax::ast::{view_item_use, view_path_glob, view_path_list};
use syntax::ast::{view_path_simple, anonymous, named, not};
use syntax::ast::{unsafe_fn};
use syntax::ast::*;
use syntax::ast_util::{def_id_of_def, local_def};
use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method};
use syntax::ast_util::{Privacy, Public, Private};
@ -66,6 +29,7 @@ use syntax::parse::token::ident_interner;
use syntax::parse::token::special_idents;
use syntax::print::pprust::path_to_str;
use syntax::codemap::{span, dummy_sp, BytePos};
use syntax::visit::{mk_simple_visitor, default_simple_visitor, SimpleVisitor};
use syntax::visit::{default_visitor, mk_vt, Visitor, visit_block};
use syntax::visit::{visit_crate, visit_expr, visit_expr_opt};
use syntax::visit::{visit_foreign_item, visit_item};
@ -346,18 +310,21 @@ pub struct ImportDirective {
module_path: ~[ident],
subclass: @ImportDirectiveSubclass,
span: span,
id: node_id,
}
pub fn ImportDirective(privacy: Privacy,
module_path: ~[ident],
subclass: @ImportDirectiveSubclass,
span: span)
span: span,
id: node_id)
-> ImportDirective {
ImportDirective {
privacy: privacy,
module_path: module_path,
subclass: subclass,
span: span
span: span,
id: id
}
}
@ -381,7 +348,7 @@ pub struct ImportResolution {
/// The privacy of this `use` directive (whether it's `use` or
/// `pub use`.
privacy: Privacy,
span: span,
id: node_id,
// The number of outstanding references to this name. When this reaches
// zero, outside modules can count on the targets being correct. Before
@ -393,21 +360,16 @@ pub struct ImportResolution {
value_target: Option<Target>,
/// The type that this `use` directive names, if there is one.
type_target: Option<Target>,
/// There exists one state per import statement
state: @mut ImportState,
}
pub fn ImportResolution(privacy: Privacy,
span: span,
state: @mut ImportState) -> ImportResolution {
id: node_id) -> ImportResolution {
ImportResolution {
privacy: privacy,
span: span,
id: id,
outstanding_references: 0,
value_target: None,
type_target: None,
state: state,
}
}
@ -420,15 +382,6 @@ pub impl ImportResolution {
}
}
pub struct ImportState {
used: bool,
warned: bool
}
pub fn ImportState() -> ImportState {
ImportState{ used: false, warned: false }
}
/// The link from a module up to its nearest parent node.
pub enum ParentLink {
NoParentLink,
@ -805,6 +758,7 @@ pub fn Resolver(session: Session,
def_map: @mut HashMap::new(),
export_map2: @mut HashMap::new(),
trait_map: HashMap::new(),
used_imports: HashSet::new(),
intr: session.intr()
};
@ -862,6 +816,8 @@ pub struct Resolver {
def_map: DefMap,
export_map2: ExportMap2,
trait_map: TraitMap,
used_imports: HashSet<node_id>,
}
pub impl Resolver {
@ -879,7 +835,7 @@ pub impl Resolver {
self.resolve_crate();
self.session.abort_if_errors();
self.check_for_unused_imports_if_necessary();
self.check_for_unused_imports();
}
//
@ -1424,7 +1380,7 @@ pub impl Resolver {
// Build up the import directives.
let module_ = self.get_module_from_parent(parent);
match view_path.node {
view_path_simple(binding, full_path, _) => {
view_path_simple(binding, full_path, id) => {
let source_ident = *full_path.idents.last();
let subclass = @SingleImport(binding,
source_ident);
@ -1432,7 +1388,8 @@ pub impl Resolver {
module_,
module_path,
subclass,
view_path.span);
view_path.span,
id);
}
view_path_list(_, ref source_idents, _) => {
for source_idents.each |source_ident| {
@ -1442,15 +1399,17 @@ pub impl Resolver {
module_,
copy module_path,
subclass,
source_ident.span);
source_ident.span,
source_ident.node.id);
}
}
view_path_glob(_, _) => {
view_path_glob(_, id) => {
self.build_import_directive(privacy,
module_,
module_path,
@GlobImport,
view_path.span);
view_path.span,
id);
}
}
}
@ -1575,10 +1534,7 @@ pub impl Resolver {
// avoid creating cycles in the
// module graph.
let resolution =
@mut ImportResolution(Public,
dummy_sp(),
@mut ImportState());
let resolution = @mut ImportResolution(Public, 0);
resolution.outstanding_references = 0;
match existing_module.parent_link {
@ -1840,9 +1796,10 @@ pub impl Resolver {
module_: @mut Module,
module_path: ~[ident],
subclass: @ImportDirectiveSubclass,
span: span) {
span: span,
id: node_id) {
let directive = @ImportDirective(privacy, module_path,
subclass, span);
subclass, span, id);
module_.imports.push(directive);
// Bump the reference count on the name. Or, if this is a glob, set
@ -1864,16 +1821,7 @@ pub impl Resolver {
}
None => {
debug!("(building import directive) creating new");
let state = @mut ImportState();
let resolution = @mut ImportResolution(privacy,
span,
state);
let name = self.idents_to_str(directive.module_path);
// Don't warn about unused intrinsics because they're
// automatically appended to all files
if name == ~"intrinsic::rusti" {
resolution.state.warned = true;
}
let resolution = @mut ImportResolution(privacy, id);
resolution.outstanding_references = 1;
module_.import_resolutions.insert(target, resolution);
}
@ -2069,13 +2017,12 @@ pub impl Resolver {
import_directive.span);
}
GlobImport => {
let span = import_directive.span;
let privacy = import_directive.privacy;
resolution_result =
self.resolve_glob_import(privacy,
module_,
containing_module,
span);
import_directive.id);
}
}
}
@ -2202,7 +2149,8 @@ pub impl Resolver {
if import_resolution.outstanding_references
== 0 => {
fn get_binding(import_resolution:
fn get_binding(this: @mut Resolver,
import_resolution:
@mut ImportResolution,
namespace: Namespace)
-> NamespaceResult {
@ -2219,7 +2167,7 @@ pub impl Resolver {
return UnboundResult;
}
Some(target) => {
import_resolution.state.used = true;
this.used_imports.insert(import_resolution.id);
return BoundResult(target.target_module,
target.bindings);
}
@ -2229,11 +2177,11 @@ pub impl Resolver {
// The name is an import which has been fully
// resolved. We can, therefore, just follow it.
if value_result.is_unknown() {
value_result = get_binding(*import_resolution,
value_result = get_binding(self, *import_resolution,
ValueNS);
}
if type_result.is_unknown() {
type_result = get_binding(*import_resolution,
type_result = get_binding(self, *import_resolution,
TypeNS);
}
}
@ -2358,13 +2306,12 @@ pub impl Resolver {
privacy: Privacy,
module_: @mut Module,
containing_module: @mut Module,
span: span)
id: node_id)
-> ResolveResult<()> {
// This function works in a highly imperative manner; it eagerly adds
// everything it can to the list of import resolutions of the module
// node.
debug!("(resolving glob import) resolving %? glob import", privacy);
let state = @mut ImportState();
// We must bail out if the node has unresolved imports of any kind
// (including globs).
@ -2390,9 +2337,7 @@ pub impl Resolver {
None if target_import_resolution.privacy == Public => {
// Simple: just copy the old import resolution.
let new_import_resolution =
@mut ImportResolution(privacy,
target_import_resolution.span,
state);
@mut ImportResolution(privacy, id);
new_import_resolution.value_target =
copy target_import_resolution.value_target;
new_import_resolution.type_target =
@ -2434,9 +2379,7 @@ pub impl Resolver {
match module_.import_resolutions.find(&ident) {
None => {
// Create a new import resolution from this child.
dest_import_resolution = @mut ImportResolution(privacy,
span,
state);
dest_import_resolution = @mut ImportResolution(privacy, id);
module_.import_resolutions.insert
(ident, dest_import_resolution);
}
@ -2714,7 +2657,7 @@ pub impl Resolver {
Some(target) => {
debug!("(resolving item in lexical scope) using \
import resolution");
import_resolution.state.used = true;
self.used_imports.insert(import_resolution.id);
return Success(copy target);
}
}
@ -2985,7 +2928,7 @@ pub impl Resolver {
import_resolution.privacy == Public => {
debug!("(resolving name in module) resolved to \
import");
import_resolution.state.used = true;
self.used_imports.insert(import_resolution.id);
return Success(copy target);
}
Some(_) => {
@ -4466,7 +4409,7 @@ pub impl Resolver {
namespace)) {
(Some(def), Some(Public)) => {
// Found it.
import_resolution.state.used = true;
self.used_imports.insert(import_resolution.id);
return ImportNameDefinition(def);
}
(Some(_), _) | (None, _) => {
@ -5046,8 +4989,8 @@ pub impl Resolver {
&mut found_traits,
trait_def_id, name);
if added {
import_resolution.state.used =
true;
self.used_imports.insert(
import_resolution.id);
}
}
_ => {
@ -5145,86 +5088,50 @@ pub impl Resolver {
// resolve data structures.
//
fn unused_import_lint_level(@mut self, m: @mut Module) -> level {
let settings = self.session.lint_settings;
match m.def_id {
Some(def) => get_lint_settings_level(settings, unused_imports,
def.node, def.node),
None => get_lint_level(settings.default_settings, unused_imports)
}
fn check_for_unused_imports(@mut self) {
let vt = mk_simple_visitor(@SimpleVisitor {
visit_view_item: |vi| self.check_for_item_unused_imports(vi),
.. *default_simple_visitor()
});
visit_crate(self.crate, (), vt);
}
fn check_for_unused_imports_if_necessary(@mut self) {
if self.unused_import_lint_level(self.current_module) == allow {
return;
}
fn check_for_item_unused_imports(&mut self, vi: @view_item) {
// Ignore public import statements because there's no way to be sure
// whether they're used or not. Also ignore imports with a dummy span
// because this means that they were generated in some fashion by the
// compiler and we don't need to consider them.
if vi.vis == public { return }
if vi.span == dummy_sp() { return }
let root_module = self.graph_root.get_module();
self.check_for_unused_imports_in_module_subtree(root_module);
}
match vi.node {
view_item_extern_mod(*) => {} // ignore
view_item_use(ref path) => {
for path.each |p| {
match p.node {
view_path_simple(_, _, id) | view_path_glob(_, id) => {
if !self.used_imports.contains(&id) {
self.session.add_lint(unused_imports,
id, vi.span,
~"unused import");
}
}
fn check_for_unused_imports_in_module_subtree(@mut self,
module_: @mut Module) {
// If this isn't a local crate, then bail out. We don't need to check
// for unused imports in external crates.
match module_.def_id {
Some(def_id) if def_id.crate == local_crate => {
// OK. Continue.
}
None => {
// Check for unused imports in the root module.
}
Some(_) => {
// Bail out.
debug!("(checking for unused imports in module subtree) not \
checking for unused imports for `%s`",
self.module_to_str(module_));
return;
}
}
self.check_for_unused_imports_in_module(module_);
for module_.children.each_value |&child_name_bindings| {
match (*child_name_bindings).get_module_if_available() {
None => {
// Nothing to do.
}
Some(child_module) => {
self.check_for_unused_imports_in_module_subtree
(child_module);
view_path_list(_, ref list, _) => {
for list.each |i| {
if !self.used_imports.contains(&i.node.id) {
self.session.add_lint(unused_imports,
i.node.id, i.span,
~"unused import");
}
}
}
}
}
}
}
for module_.anonymous_children.each_value |&child_module| {
self.check_for_unused_imports_in_module_subtree(child_module);
}
}
fn check_for_unused_imports_in_module(@mut self, module_: @mut Module) {
for module_.import_resolutions.each_value |&import_resolution| {
// Ignore dummy spans for things like automatically injected
// imports for the prelude, and also don't warn about the same
// import statement being unused more than once. Furthermore, if
// the import is public, then we can't be sure whether it's unused
// or not so don't warn about it.
if !import_resolution.state.used &&
!import_resolution.state.warned &&
import_resolution.span != dummy_sp() &&
import_resolution.privacy != Public {
import_resolution.state.warned = true;
let span = import_resolution.span;
self.session.span_lint_level(
self.unused_import_lint_level(module_),
span,
~"unused import");
}
}
}
//
// Diagnostics
//

View file

@ -17,8 +17,7 @@ use metadata::csearch::get_type_name_if_impl;
use metadata::cstore::find_extern_mod_stmt_cnum;
use metadata::decoder::{def_like, dl_def, dl_field, dl_impl};
use middle::lang_items::LanguageItems;
use middle::lint::{allow, level, unused_imports};
use middle::lint::{get_lint_level, get_lint_settings_level};
use middle::lint::{allow, level, warn};
use middle::pat_util::pat_bindings;
use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm};
@ -5168,14 +5167,7 @@ pub impl Resolver {
// resolve data structures.
//
fn unused_import_lint_level(@mut self, m: @mut Module) -> level {
let settings = self.session.lint_settings;
match m.def_id {
Some(def) => get_lint_settings_level(settings, unused_imports,
def.node, def.node),
None => get_lint_level(settings.default_settings, unused_imports)
}
}
fn unused_import_lint_level(@mut self, _: @mut Module) -> level { warn }
fn check_for_unused_imports_if_necessary(@mut self) {
if self.unused_import_lint_level(self.current_module) == allow {

View file

@ -412,7 +412,12 @@ pub fn id_visitor(vfn: @fn(node_id)) -> visit::vt<()> {
match vp.node {
view_path_simple(_, _, id) => vfn(id),
view_path_glob(_, id) => vfn(id),
view_path_list(_, _, id) => vfn(id)
view_path_list(_, ref paths, id) => {
vfn(id);
for paths.each |p| {
vfn(p.node.id);
}
}
}
}
}

View file

@ -196,6 +196,7 @@ pub fn mk_global_struct_e(cx: @ext_ctxt,
}
pub fn mk_glob_use(cx: @ext_ctxt,
sp: span,
vis: ast::visibility,
path: ~[ast::ident]) -> @ast::view_item {
let glob = @codemap::spanned {
node: ast::view_path_glob(mk_raw_path(sp, path), cx.next_id()),
@ -203,7 +204,7 @@ pub fn mk_glob_use(cx: @ext_ctxt,
};
@ast::view_item { node: ast::view_item_use(~[glob]),
attrs: ~[],
vis: ast::private,
vis: vis,
span: sp }
}
pub fn mk_local(cx: @ext_ctxt, sp: span, mutbl: bool,

View file

@ -41,7 +41,6 @@ pub mod rt {
pub use parse::new_parser_from_tts;
pub use codemap::{BytePos, span, dummy_spanned};
use print::pprust;
use print::pprust::{item_to_str, ty_to_str};
pub trait ToTokens {
@ -678,10 +677,11 @@ fn expand_tts(cx: @ext_ctxt,
// We want to emit a block expression that does a sequence of 'use's to
// import the runtime module, followed by a tt-building expression.
let uses = ~[ build::mk_glob_use(cx, sp, ids_ext(cx, ~[~"syntax",
~"ext",
~"quote",
~"rt"])) ];
let uses = ~[ build::mk_glob_use(cx, sp, ast::public,
ids_ext(cx, ~[~"syntax",
~"ext",
~"quote",
~"rt"])) ];
// We also bind a single value, sp, to ext_cx.call_site()
//

View file

@ -0,0 +1,37 @@
// Copyright 2013 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.
#[allow(while_true)];
struct A(int);
impl A {
fn foo(&self) { while true {} }
#[deny(while_true)]
fn bar(&self) { while true {} } //~ ERROR: infinite loops
}
#[deny(while_true)]
mod foo {
struct B(int);
impl B {
fn foo(&self) { while true {} } //~ ERROR: infinite loops
#[allow(while_true)]
fn bar(&self) { while true {} }
}
}
#[deny(while_true)]
fn main() {
while true {} //~ ERROR: infinite loops
}

View file

@ -8,12 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[deny(dead_assignment)];
fn f1(x: &mut int) {
*x = 1; // no error
}
fn f2() {
let mut x = 3; //~ WARNING value assigned to `x` is never read
let mut x = 3; //~ ERROR: value assigned to `x` is never read
x = 4;
copy x;
}
@ -21,10 +23,7 @@ fn f2() {
fn f3() {
let mut x = 3;
copy x;
x = 4; //~ WARNING value assigned to `x` is never read
x = 4; //~ ERROR: value assigned to `x` is never read
}
fn main() { // leave this in here just to trigger compile-fail:
let x: int;
copy x; //~ ERROR use of possibly uninitialized variable: `x`
}
fn main() {}