rust/src/comp/middle/lint.rs
Haitao Li 2a59ab8fa5 rustc: Specify lint checks via crate attributes
A crate attribute like `#[lint(no_ctypes)]` can now be used to turn off
ctypes checking.

Issue #1543
2012-01-22 01:33:37 +08:00

159 lines
4.0 KiB
Rust

import driver::session::session;
import middle::ty::ctxt;
import syntax::{ast, visit};
import front::attr;
import std::io;
import io::writer_util;
enum option {
ctypes,
}
impl opt_ for option {
fn desc() -> str {
"lint: " + alt self {
ctypes { "ctypes usage checking" }
}
}
fn run(tcx: ty::ctxt, crate: @ast::crate, time_pass: bool) {
let checker = alt self {
ctypes {
bind check_ctypes(tcx, crate)
}
};
time(time_pass, self.desc(), checker);
}
}
// FIXME: Copied from driver.rs, to work around a bug(#1566)
fn time(do_it: bool, what: str, thunk: block()) {
if !do_it{ ret thunk(); }
let start = std::time::precise_time_s();
thunk();
let end = std::time::precise_time_s();
io::stdout().write_str(#fmt("time: %3.3f s\t%s\n",
end - start, what));
}
// Merge lint options specified by crate attributes and rustc command
// line. Precedence: cmdline > attribute > default
fn merge_opts(attrs: [ast::attribute], cmd_opts: [(option, bool)]) ->
[(option, bool)] {
fn str_to_option(name: str) -> (option, bool) {
ret alt name {
"ctypes" { (ctypes, true) }
"no_ctypes" { (ctypes, false) }
}
}
fn meta_to_option(meta: @ast::meta_item) -> (option, bool) {
ret alt meta.node {
ast::meta_word(name) {
str_to_option(name)
}
};
}
fn default() -> [(option, bool)] {
[(ctypes, true)]
}
fn contains(xs: [(option, bool)], x: option) -> bool {
for (o, _) in xs {
if o == x { ret true; }
}
ret false;
}
let result = cmd_opts;
let lint_metas =
attr::attr_metas(attr::find_attrs_by_name(attrs, "lint"));
vec::iter(lint_metas) {|mi|
alt mi.node {
ast::meta_list(_, list) {
vec::iter(list) {|e|
let (o, v) = meta_to_option(e);
if !contains(cmd_opts, o) {
result += [(o, v)];
}
}
}
_ { }
}
};
for (o, v) in default() {
if !contains(result, o) {
result += [(o, v)];
}
}
ret result;
}
fn check_ctypes(tcx: ty::ctxt, crate: @ast::crate) {
fn check_native_fn(tcx: ty::ctxt, decl: ast::fn_decl) {
let tys = vec::map(decl.inputs) {|a| a.ty };
for ty in (tys + [decl.output]) {
alt ty.node {
ast::ty_int(ast::ty_i) {
tcx.sess.span_warn(
ty.span,
"found rust type `int` in native module, while \
ctypes::c_int or ctypes::long should be used");
}
ast::ty_uint(ast::ty_u) {
tcx.sess.span_warn(
ty.span,
"found rust type `uint` in native module, while \
ctypes::c_uint or ctypes::ulong should be used");
}
_ { }
}
}
}
fn check_item(tcx: ty::ctxt, it: @ast::item) {
alt it.node {
ast::item_native_mod(nmod) {
for ni in nmod.items {
alt ni.node {
ast::native_item_fn(decl, tps) {
check_native_fn(tcx, decl);
}
_ { }
}
}
}
_ {/* nothing to do */ }
}
}
let visit = visit::mk_simple_visitor(@{
visit_item: bind check_item(tcx, _)
with *visit::default_simple_visitor()
});
visit::visit_crate(*crate, (), visit);
}
fn check_crate(tcx: ty::ctxt, crate: @ast::crate,
opts: [(option, bool)], time: bool) {
let lint_opts = lint::merge_opts(crate.node.attrs, opts);
for (lopt, switch) in lint_opts {
if switch == true {
lopt.run(tcx, crate, time);
}
}
}
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//