1
Fork 0

Replace enum LintId with an extensible alternative

This commit is contained in:
Keegan McAllister 2014-06-04 14:35:58 -07:00
parent 69b6bc5eee
commit 442fbc473e
16 changed files with 742 additions and 764 deletions

View file

@ -70,7 +70,8 @@ pub struct Options {
pub gc: bool, pub gc: bool,
pub optimize: OptLevel, pub optimize: OptLevel,
pub debuginfo: DebugInfoLevel, pub debuginfo: DebugInfoLevel,
pub lint_opts: Vec<(lint::LintId, lint::Level)> , pub lint_opts: Vec<(String, lint::Level)>,
pub describe_lints: bool,
pub output_types: Vec<back::link::OutputType> , pub output_types: Vec<back::link::OutputType> ,
// This was mutable for rustpkg, which updates search paths based on the // This was mutable for rustpkg, which updates search paths based on the
// parsed code. It remains mutable in case its replacements wants to use // parsed code. It remains mutable in case its replacements wants to use
@ -104,6 +105,7 @@ pub fn basic_options() -> Options {
optimize: No, optimize: No,
debuginfo: NoDebugInfo, debuginfo: NoDebugInfo,
lint_opts: Vec::new(), lint_opts: Vec::new(),
describe_lints: false,
output_types: Vec::new(), output_types: Vec::new(),
addl_lib_search_paths: RefCell::new(HashSet::new()), addl_lib_search_paths: RefCell::new(HashSet::new()),
maybe_sysroot: None, maybe_sysroot: None,
@ -585,30 +587,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let no_trans = matches.opt_present("no-trans"); let no_trans = matches.opt_present("no-trans");
let no_analysis = matches.opt_present("no-analysis"); let no_analysis = matches.opt_present("no-analysis");
let lint_levels = [lint::Allow, lint::Warn, let mut lint_opts = vec!();
lint::Deny, lint::Forbid]; let mut describe_lints = false;
let mut lint_opts = Vec::new();
let lint_dict = lint::get_lint_dict();
for level in lint_levels.iter() {
let level_name = lint::level_to_str(*level);
let level_short = level_name.slice_chars(0, 1); for &level in [lint::Allow, lint::Warn, lint::Deny, lint::Forbid].iter() {
let level_short = level_short.to_ascii().to_upper().into_str(); for lint_name in matches.opt_strs(level.as_str()).move_iter() {
let flags = matches.opt_strs(level_short.as_slice()) if lint_name.as_slice() == "help" {
.move_iter() describe_lints = true;
.collect::<Vec<_>>() } else {
.append(matches.opt_strs(level_name).as_slice()); lint_opts.push((lint_name.replace("-", "_").into_string(), level));
for lint_name in flags.iter() {
let lint_name = lint_name.replace("-", "_").into_string();
match lint_dict.find_equiv(&lint_name) {
None => {
early_error(format!("unknown {} flag: {}",
level_name,
lint_name).as_slice());
}
Some(lint) => {
lint_opts.push((lint.lint, *level));
}
} }
} }
} }
@ -752,6 +739,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
optimize: opt_level, optimize: opt_level,
debuginfo: debuginfo, debuginfo: debuginfo,
lint_opts: lint_opts, lint_opts: lint_opts,
describe_lints: describe_lints,
output_types: output_types, output_types: output_types,
addl_lib_search_paths: RefCell::new(addl_lib_search_paths), addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
maybe_sysroot: sysroot_opt, maybe_sysroot: sysroot_opt,

View file

@ -767,7 +767,7 @@ pub fn collect_crate_types(session: &Session,
} }
Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable), Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable),
Some(_) => { Some(_) => {
session.add_lint(lint::UnknownCrateType, session.add_lint(lint::builtin::unknown_crate_type,
ast::CRATE_NODE_ID, ast::CRATE_NODE_ID,
a.span, a.span,
"invalid `crate_type` \ "invalid `crate_type` \
@ -775,7 +775,7 @@ pub fn collect_crate_types(session: &Session,
None None
} }
_ => { _ => {
session.add_lint(lint::UnknownCrateType, session.add_lint(lint::builtin::unknown_crate_type,
ast::CRATE_NODE_ID, ast::CRATE_NODE_ID,
a.span, a.span,
"`crate_type` requires a \ "`crate_type` requires a \

View file

@ -17,7 +17,6 @@ use lint;
use metadata; use metadata;
use std::any::AnyRefExt; use std::any::AnyRefExt;
use std::cmp;
use std::io; use std::io;
use std::os; use std::os;
use std::str; use std::str;
@ -50,6 +49,12 @@ fn run_compiler(args: &[String]) {
None => return None => return
}; };
let sopts = config::build_session_options(&matches);
if sopts.describe_lints {
describe_lints();
return;
}
let (input, input_file_path) = match matches.free.len() { let (input, input_file_path) = match matches.free.len() {
0u => early_error("no input filename given"), 0u => early_error("no input filename given"),
1u => { 1u => {
@ -66,7 +71,6 @@ fn run_compiler(args: &[String]) {
_ => early_error("multiple input filenames provided") _ => early_error("multiple input filenames provided")
}; };
let sopts = config::build_session_options(&matches);
let sess = build_session(sopts, input_file_path); let sess = build_session(sopts, input_file_path);
let cfg = config::build_configuration(&sess); let cfg = config::build_configuration(&sess);
let odir = matches.opt_str("out-dir").map(|o| Path::new(o)); let odir = matches.opt_str("out-dir").map(|o| Path::new(o));
@ -124,7 +128,7 @@ Additional help:
config::optgroups().as_slice())); config::optgroups().as_slice()));
} }
fn describe_warnings() { fn describe_lints() {
println!(" println!("
Available lint options: Available lint options:
-W <foo> Warn about <foo> -W <foo> Warn about <foo>
@ -133,30 +137,32 @@ Available lint options:
-F <foo> Forbid <foo> (deny, and deny all overrides) -F <foo> Forbid <foo> (deny, and deny all overrides)
"); ");
let lint_dict = lint::get_lint_dict(); let mut builtin_specs = lint::builtin_lint_specs();
let mut lint_dict = lint_dict.move_iter() builtin_specs.sort_by(|x, y| {
.map(|(k, v)| (v, k)) match x.default_level.cmp(&y.default_level) {
.collect::<Vec<(lint::LintSpec, &'static str)> >(); Equal => x.name.cmp(&y.name),
lint_dict.as_mut_slice().sort(); r => r,
}
});
// FIXME: What if someone uses combining characters or East Asian fullwidth
// characters in a lint name?!?!?
let max_name_len = builtin_specs.iter()
.map(|&s| s.name.char_len())
.max().unwrap_or(0);
let padded = |x: &str| {
format!("{}{}", " ".repeat(max_name_len - x.char_len()), x)
};
let mut max_key = 0;
for &(_, name) in lint_dict.iter() {
max_key = cmp::max(name.len(), max_key);
}
fn padded(max: uint, s: &str) -> String {
format!("{}{}", " ".repeat(max - s.len()), s)
}
println!("\nAvailable lint checks:\n"); println!("\nAvailable lint checks:\n");
println!(" {} {:7.7s} {}", println!(" {} {:7.7s} {}", padded("name"), "default", "meaning");
padded(max_key, "name"), "default", "meaning"); println!(" {} {:7.7s} {}", padded("----"), "-------", "-------");
println!(" {} {:7.7s} {}\n", println!("");
padded(max_key, "----"), "-------", "-------");
for (spec, name) in lint_dict.move_iter() { for spec in builtin_specs.move_iter() {
let name = name.replace("_", "-"); let name = spec.name.replace("_", "-");
println!(" {} {:7.7s} {}", println!(" {} {:7.7s} {}",
padded(max_key, name.as_slice()), padded(name.as_slice()), spec.default_level.as_str(), spec.desc);
lint::level_to_str(spec.default),
spec.desc);
} }
println!(""); println!("");
} }
@ -214,12 +220,7 @@ pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
return None; return None;
} }
let lint_flags = matches.opt_strs("W").move_iter().collect::<Vec<_>>().append( // Don't handle -W help here, because we might first load plugins.
matches.opt_strs("warn").as_slice());
if lint_flags.iter().any(|x| x.as_slice() == "help") {
describe_warnings();
return None;
}
let r = matches.opt_strs("Z"); let r = matches.opt_strs("Z");
if r.iter().any(|x| x.as_slice() == "help") { if r.iter().any(|x| x.as_slice() == "help") {

View file

@ -106,16 +106,17 @@ impl Session {
self.diagnostic().handler().unimpl(msg) self.diagnostic().handler().unimpl(msg)
} }
pub fn add_lint(&self, pub fn add_lint(&self,
lint: lint::LintId, lint: &'static lint::Lint,
id: ast::NodeId, id: ast::NodeId,
sp: Span, sp: Span,
msg: String) { msg: String) {
let lint_id = lint::LintId::of(lint);
let mut lints = self.lints.borrow_mut(); let mut lints = self.lints.borrow_mut();
match lints.find_mut(&id) { match lints.find_mut(&id) {
Some(arr) => { arr.push((lint, sp, msg)); return; } Some(arr) => { arr.push((lint_id, sp, msg)); return; }
None => {} None => {}
} }
lints.insert(id, vec!((lint, sp, msg))); lints.insert(id, vec!((lint_id, sp, msg)));
} }
pub fn next_node_id(&self) -> ast::NodeId { pub fn next_node_id(&self) -> ast::NodeId {
self.reserve_node_ids(1) self.reserve_node_ids(1)

View file

@ -409,7 +409,7 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) {
directive not necessary"); directive not necessary");
} }
None => { None => {
sess.add_lint(lint::UnknownFeatures, sess.add_lint(lint::builtin::unknown_features,
ast::CRATE_NODE_ID, ast::CRATE_NODE_ID,
mi.span, mi.span,
"unknown feature".to_string()); "unknown feature".to_string());

View file

@ -127,6 +127,15 @@ pub mod lib {
pub mod llvmdeps; pub mod llvmdeps;
} }
// A private module so that macro-expanded idents like
// `::rustc::lint::Lint` will also work in `rustc` itself.
//
// `libstd` uses the same trick.
#[doc(hidden)]
mod rustc {
pub use lint;
}
pub fn main() { pub fn main() {
let args = std::os::args().iter() let args = std::os::args().iter()
.map(|x| x.to_string()) .map(|x| x.to_string())

View file

@ -19,7 +19,7 @@ use middle::privacy::ExportedItems;
use middle::{typeck, ty, def, pat_util}; use middle::{typeck, ty, def, pat_util};
use util::ppaux::{ty_to_str}; use util::ppaux::{ty_to_str};
use util::nodemap::NodeSet; use util::nodemap::NodeSet;
use lint::{Context, LintPass}; use lint::{Context, LintPass, LintArray};
use lint; use lint;
use std::cmp; use std::cmp;
@ -41,31 +41,17 @@ use syntax::codemap::Span;
use syntax::parse::token; use syntax::parse::token;
use syntax::{ast, ast_util, visit}; use syntax::{ast, ast_util, visit};
/// Doesn't actually warn; just gathers information for use by declare_lint!(while_true, Warn,
/// checks in trans. "suggest using `loop { }` instead of `while true { }`")
#[deriving(Default)]
pub struct GatherNodeLevels;
impl LintPass for GatherNodeLevels {
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
match it.node {
ast::ItemEnum(..) => {
match cx.cur.find(&(lint::VariantSizeDifference as uint)) {
Some(&(lvl, src)) if lvl != lint::Allow => {
cx.insert_node_level(it.id, lint::VariantSizeDifference, lvl, src);
},
_ => { }
}
},
_ => { }
}
}
}
#[deriving(Default)] #[deriving(Default)]
pub struct WhileTrue; pub struct WhileTrue;
impl LintPass for WhileTrue { impl LintPass for WhileTrue {
fn get_lints(&self) -> LintArray {
lint_array!(while_true)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
match e.node { match e.node {
ast::ExprWhile(cond, _) => { ast::ExprWhile(cond, _) => {
@ -73,8 +59,7 @@ impl LintPass for WhileTrue {
ast::ExprLit(lit) => { ast::ExprLit(lit) => {
match lit.node { match lit.node {
ast::LitBool(true) => { ast::LitBool(true) => {
cx.span_lint(lint::WhileTrue, cx.span_lint(while_true, e.span,
e.span,
"denote infinite loops with loop \ "denote infinite loops with loop \
{ ... }"); { ... }");
} }
@ -89,17 +74,23 @@ impl LintPass for WhileTrue {
} }
} }
declare_lint!(unnecessary_typecast, Allow,
"detects unnecessary type casts, that can be removed")
#[deriving(Default)] #[deriving(Default)]
pub struct UnusedCasts; pub struct UnusedCasts;
impl LintPass for UnusedCasts { impl LintPass for UnusedCasts {
fn get_lints(&self) -> LintArray {
lint_array!(unnecessary_typecast)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
match e.node { match e.node {
ast::ExprCast(expr, ty) => { ast::ExprCast(expr, ty) => {
let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), ty); let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), ty);
if ty::get(ty::expr_ty(cx.tcx, expr)).sty == ty::get(t_t).sty { if ty::get(ty::expr_ty(cx.tcx, expr)).sty == ty::get(t_t).sty {
cx.span_lint(lint::UnnecessaryTypecast, ty.span, cx.span_lint(unnecessary_typecast, ty.span, "unnecessary type cast");
"unnecessary type cast");
} }
} }
_ => () _ => ()
@ -107,6 +98,15 @@ impl LintPass for UnusedCasts {
} }
} }
declare_lint!(unsigned_negate, Warn,
"using an unary minus operator on unsigned type")
declare_lint!(type_limits, Warn,
"comparisons made useless by limits of the types involved")
declare_lint!(type_overflow, Warn,
"literal out of range for its type")
pub struct TypeLimits { pub struct TypeLimits {
/// Id of the last visited negated expression /// Id of the last visited negated expression
negated_expr_id: ast::NodeId, negated_expr_id: ast::NodeId,
@ -121,6 +121,10 @@ impl Default for TypeLimits {
} }
impl LintPass for TypeLimits { impl LintPass for TypeLimits {
fn get_lints(&self) -> LintArray {
lint_array!(unsigned_negate, type_limits, type_overflow)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
match e.node { match e.node {
ast::ExprUnary(ast::UnNeg, expr) => { ast::ExprUnary(ast::UnNeg, expr) => {
@ -128,7 +132,7 @@ impl LintPass for TypeLimits {
ast::ExprLit(lit) => { ast::ExprLit(lit) => {
match lit.node { match lit.node {
ast::LitUint(..) => { ast::LitUint(..) => {
cx.span_lint(lint::UnsignedNegate, e.span, cx.span_lint(unsigned_negate, e.span,
"negation of unsigned int literal may \ "negation of unsigned int literal may \
be unintentional"); be unintentional");
}, },
@ -139,7 +143,7 @@ impl LintPass for TypeLimits {
let t = ty::expr_ty(cx.tcx, expr); let t = ty::expr_ty(cx.tcx, expr);
match ty::get(t).sty { match ty::get(t).sty {
ty::ty_uint(_) => { ty::ty_uint(_) => {
cx.span_lint(lint::UnsignedNegate, e.span, cx.span_lint(unsigned_negate, e.span,
"negation of unsigned int variable may \ "negation of unsigned int variable may \
be unintentional"); be unintentional");
}, },
@ -157,7 +161,7 @@ impl LintPass for TypeLimits {
}, },
ast::ExprBinary(binop, l, r) => { ast::ExprBinary(binop, l, r) => {
if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) { if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
cx.span_lint(lint::TypeLimits, e.span, cx.span_lint(type_limits, e.span,
"comparison is useless due to type limits"); "comparison is useless due to type limits");
} }
}, },
@ -178,7 +182,7 @@ impl LintPass for TypeLimits {
lit_val *= -1; lit_val *= -1;
} }
if lit_val < min || lit_val > max { if lit_val < min || lit_val > max {
cx.span_lint(lint::TypeOverflow, e.span, cx.span_lint(type_overflow, e.span,
"literal out of range for its type"); "literal out of range for its type");
} }
}, },
@ -194,7 +198,7 @@ impl LintPass for TypeLimits {
_ => fail!() _ => fail!()
}; };
if lit_val < min || lit_val > max { if lit_val < min || lit_val > max {
cx.span_lint(lint::TypeOverflow, e.span, cx.span_lint(type_overflow, e.span,
"literal out of range for its type"); "literal out of range for its type");
} }
}, },
@ -300,30 +304,37 @@ impl LintPass for TypeLimits {
} }
} }
declare_lint!(ctypes, Warn,
"proper use of libc types in foreign modules")
#[deriving(Default)] #[deriving(Default)]
pub struct CTypes; pub struct CTypes;
impl LintPass for CTypes { impl LintPass for CTypes {
fn get_lints(&self) -> LintArray {
lint_array!(ctypes)
}
fn check_item(&mut self, cx: &Context, it: &ast::Item) { fn check_item(&mut self, cx: &Context, it: &ast::Item) {
fn check_ty(cx: &Context, ty: &ast::Ty) { fn check_ty(cx: &Context, ty: &ast::Ty) {
match ty.node { match ty.node {
ast::TyPath(_, _, id) => { ast::TyPath(_, _, id) => {
match cx.tcx.def_map.borrow().get_copy(&id) { match cx.tcx.def_map.borrow().get_copy(&id) {
def::DefPrimTy(ast::TyInt(ast::TyI)) => { def::DefPrimTy(ast::TyInt(ast::TyI)) => {
cx.span_lint(lint::CTypes, ty.span, cx.span_lint(ctypes, ty.span,
"found rust type `int` in foreign module, while \ "found rust type `int` in foreign module, while \
libc::c_int or libc::c_long should be used"); libc::c_int or libc::c_long should be used");
} }
def::DefPrimTy(ast::TyUint(ast::TyU)) => { def::DefPrimTy(ast::TyUint(ast::TyU)) => {
cx.span_lint(lint::CTypes, ty.span, cx.span_lint(ctypes, ty.span,
"found rust type `uint` in foreign module, while \ "found rust type `uint` in foreign module, while \
libc::c_uint or libc::c_ulong should be used"); libc::c_uint or libc::c_ulong should be used");
} }
def::DefTy(def_id) => { def::DefTy(def_id) => {
if !adt::is_ffi_safe(cx.tcx, def_id) { if !adt::is_ffi_safe(cx.tcx, def_id) {
cx.span_lint(lint::CTypes, ty.span, cx.span_lint(ctypes, ty.span,
"found enum type without foreign-function-safe \ "found enum type without foreign-function-safe \
representation annotation in foreign module"); representation annotation in foreign module");
// hmm... this message could be more helpful // hmm... this message could be more helpful
} }
} }
@ -356,54 +367,64 @@ impl LintPass for CTypes {
} }
} }
declare_lint!(managed_heap_memory, Allow,
"use of managed (@ type) heap memory")
declare_lint!(owned_heap_memory, Allow,
"use of owned (Box type) heap memory")
declare_lint!(heap_memory, Allow,
"use of any (Box type or @ type) heap memory")
#[deriving(Default)] #[deriving(Default)]
pub struct HeapMemory; pub struct HeapMemory;
impl HeapMemory { impl HeapMemory {
fn check_heap_type(&self, cx: &Context, span: Span, ty: ty::t) { fn check_heap_type(&self, cx: &Context, span: Span, ty: ty::t) {
let xs = [lint::ManagedHeapMemory, lint::OwnedHeapMemory, lint::HeapMemory]; let mut n_box = 0;
for &lint in xs.iter() { let mut n_uniq = 0;
if cx.get_level(lint) == lint::Allow { continue } ty::fold_ty(cx.tcx, ty, |t| {
match ty::get(t).sty {
ty::ty_box(_) => {
n_box += 1;
}
ty::ty_uniq(_) |
ty::ty_trait(box ty::TyTrait {
store: ty::UniqTraitStore, ..
}) |
ty::ty_closure(box ty::ClosureTy {
store: ty::UniqTraitStore,
..
}) => {
n_uniq += 1;
}
let mut n_box = 0; _ => ()
let mut n_uniq = 0; };
ty::fold_ty(cx.tcx, ty, |t| { t
match ty::get(t).sty { });
ty::ty_box(_) => {
n_box += 1;
}
ty::ty_uniq(_) |
ty::ty_trait(box ty::TyTrait {
store: ty::UniqTraitStore, ..
}) |
ty::ty_closure(box ty::ClosureTy {
store: ty::UniqTraitStore,
..
}) => {
n_uniq += 1;
}
_ => () if n_uniq > 0 {
}; let s = ty_to_str(cx.tcx, ty);
t let m = format!("type uses owned (Box type) pointers: {}", s);
}); cx.span_lint(owned_heap_memory, span, m.as_slice());
cx.span_lint(heap_memory, span, m.as_slice());
}
if n_uniq > 0 && lint != lint::ManagedHeapMemory { if n_box > 0 {
let s = ty_to_str(cx.tcx, ty); let s = ty_to_str(cx.tcx, ty);
let m = format!("type uses owned (Box type) pointers: {}", s); let m = format!("type uses managed (@ type) pointers: {}", s);
cx.span_lint(lint, span, m.as_slice()); cx.span_lint(managed_heap_memory, span, m.as_slice());
} cx.span_lint(heap_memory, span, m.as_slice());
if n_box > 0 && lint != lint::OwnedHeapMemory {
let s = ty_to_str(cx.tcx, ty);
let m = format!("type uses managed (@ type) pointers: {}", s);
cx.span_lint(lint, span, m.as_slice());
}
} }
} }
} }
impl LintPass for HeapMemory { impl LintPass for HeapMemory {
fn get_lints(&self) -> LintArray {
lint_array!(managed_heap_memory, owned_heap_memory, heap_memory)
}
fn check_item(&mut self, cx: &Context, it: &ast::Item) { fn check_item(&mut self, cx: &Context, it: &ast::Item) {
match it.node { match it.node {
ast::ItemFn(..) | ast::ItemFn(..) |
@ -434,6 +455,9 @@ impl LintPass for HeapMemory {
} }
} }
declare_lint!(raw_pointer_deriving, Warn,
"uses of #[deriving] with raw pointers are rarely correct")
struct RawPtrDerivingVisitor<'a> { struct RawPtrDerivingVisitor<'a> {
cx: &'a Context<'a> cx: &'a Context<'a>
} }
@ -442,7 +466,7 @@ impl<'a> visit::Visitor<()> for RawPtrDerivingVisitor<'a> {
fn visit_ty(&mut self, ty: &ast::Ty, _: ()) { fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
static MSG: &'static str = "use of `#[deriving]` with a raw pointer"; static MSG: &'static str = "use of `#[deriving]` with a raw pointer";
match ty.node { match ty.node {
ast::TyPtr(..) => self.cx.span_lint(lint::RawPointerDeriving, ty.span, MSG), ast::TyPtr(..) => self.cx.span_lint(raw_pointer_deriving, ty.span, MSG),
_ => {} _ => {}
} }
visit::walk_ty(self, ty, ()); visit::walk_ty(self, ty, ());
@ -465,6 +489,10 @@ impl Default for RawPointerDeriving {
} }
impl LintPass for RawPointerDeriving { impl LintPass for RawPointerDeriving {
fn get_lints(&self) -> LintArray {
lint_array!(raw_pointer_deriving)
}
fn check_item(&mut self, cx: &Context, item: &ast::Item) { fn check_item(&mut self, cx: &Context, item: &ast::Item) {
if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") { if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") {
return return
@ -495,10 +523,17 @@ impl LintPass for RawPointerDeriving {
} }
} }
declare_lint!(unused_attribute, Warn,
"detects attributes that were not used by the compiler")
#[deriving(Default)] #[deriving(Default)]
pub struct UnusedAttribute; pub struct UnusedAttribute;
impl LintPass for UnusedAttribute { impl LintPass for UnusedAttribute {
fn get_lints(&self) -> LintArray {
lint_array!(unused_attribute)
}
fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) { fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) {
static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [ static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [
// FIXME: #14408 whitelist docs since rustdoc looks at them // FIXME: #14408 whitelist docs since rustdoc looks at them
@ -556,7 +591,7 @@ impl LintPass for UnusedAttribute {
} }
if !attr::is_used(attr) { if !attr::is_used(attr) {
cx.span_lint(lint::UnusedAttribute, attr.span, "unused attribute"); cx.span_lint(unused_attribute, attr.span, "unused attribute");
if CRATE_ATTRS.contains(&attr.name().get()) { if CRATE_ATTRS.contains(&attr.name().get()) {
let msg = match attr.node.style { let msg = match attr.node.style {
ast::AttrOuter => "crate-level attribute should be an inner \ ast::AttrOuter => "crate-level attribute should be an inner \
@ -564,26 +599,30 @@ impl LintPass for UnusedAttribute {
ast::AttrInner => "crate-level attribute should be in the \ ast::AttrInner => "crate-level attribute should be in the \
root module", root module",
}; };
cx.span_lint(lint::UnusedAttribute, attr.span, msg); cx.span_lint(unused_attribute, attr.span, msg);
} }
} }
} }
} }
declare_lint!(path_statement, Warn,
"path statements with no effect")
#[deriving(Default)] #[deriving(Default)]
pub struct PathStatement; pub struct PathStatement;
impl LintPass for PathStatement { impl LintPass for PathStatement {
fn get_lints(&self) -> LintArray {
lint_array!(path_statement)
}
fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
match s.node { match s.node {
ast::StmtSemi(expr, _) => { ast::StmtSemi(expr, _) => {
match expr.node { match expr.node {
ast::ExprPath(_) => { ast::ExprPath(_) => cx.span_lint(path_statement, s.span,
cx.span_lint(lint::PathStatement, "path statement with no effect"),
s.span, _ => ()
"path statement with no effect");
}
_ => {}
} }
} }
_ => () _ => ()
@ -591,10 +630,20 @@ impl LintPass for PathStatement {
} }
} }
#[deriving(Default)] declare_lint!(unused_must_use, Warn,
pub struct UnusedMustUse; "unused result of a type flagged as #[must_use]")
declare_lint!(unused_result, Allow,
"unused result of an expression in a statement")
#[deriving(Default)]
pub struct UnusedResult;
impl LintPass for UnusedResult {
fn get_lints(&self) -> LintArray {
lint_array!(unused_must_use, unused_result)
}
impl LintPass for UnusedMustUse {
fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
let expr = match s.node { let expr = match s.node {
ast::StmtSemi(expr, _) => expr, ast::StmtSemi(expr, _) => expr,
@ -620,7 +669,7 @@ impl LintPass for UnusedMustUse {
ast_map::NodeItem(it) => { ast_map::NodeItem(it) => {
if attr::contains_name(it.attrs.as_slice(), if attr::contains_name(it.attrs.as_slice(),
"must_use") { "must_use") {
cx.span_lint(lint::UnusedMustUse, s.span, cx.span_lint(unused_must_use, s.span,
"unused result which must be used"); "unused result which must be used");
warned = true; warned = true;
} }
@ -630,7 +679,7 @@ impl LintPass for UnusedMustUse {
} else { } else {
csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| { csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| {
if attr::contains_name(attrs.as_slice(), "must_use") { if attr::contains_name(attrs.as_slice(), "must_use") {
cx.span_lint(lint::UnusedMustUse, s.span, cx.span_lint(unused_must_use, s.span,
"unused result which must be used"); "unused result which must be used");
warned = true; warned = true;
} }
@ -640,21 +689,28 @@ impl LintPass for UnusedMustUse {
_ => {} _ => {}
} }
if !warned { if !warned {
cx.span_lint(lint::UnusedResult, s.span, "unused result"); cx.span_lint(unused_result, s.span, "unused result");
} }
} }
} }
declare_lint!(deprecated_owned_vector, Allow,
"use of a `~[T]` vector")
#[deriving(Default)] #[deriving(Default)]
pub struct DeprecatedOwnedVector; pub struct DeprecatedOwnedVector;
impl LintPass for DeprecatedOwnedVector { impl LintPass for DeprecatedOwnedVector {
fn get_lints(&self) -> LintArray {
lint_array!(deprecated_owned_vector)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
let t = ty::expr_ty(cx.tcx, e); let t = ty::expr_ty(cx.tcx, e);
match ty::get(t).sty { match ty::get(t).sty {
ty::ty_uniq(t) => match ty::get(t).sty { ty::ty_uniq(t) => match ty::get(t).sty {
ty::ty_vec(_, None) => { ty::ty_vec(_, None) => {
cx.span_lint(lint::DeprecatedOwnedVector, e.span, cx.span_lint(deprecated_owned_vector, e.span,
"use of deprecated `~[]` vector; replaced by `std::vec::Vec`") "use of deprecated `~[]` vector; replaced by `std::vec::Vec`")
} }
_ => {} _ => {}
@ -664,10 +720,17 @@ impl LintPass for DeprecatedOwnedVector {
} }
} }
declare_lint!(non_camel_case_types, Warn,
"types, variants and traits should have camel case names")
#[deriving(Default)] #[deriving(Default)]
pub struct NonCamelCaseTypes; pub struct NonCamelCaseTypes;
impl LintPass for NonCamelCaseTypes { impl LintPass for NonCamelCaseTypes {
fn get_lints(&self) -> LintArray {
lint_array!(non_camel_case_types)
}
fn check_item(&mut self, cx: &Context, it: &ast::Item) { fn check_item(&mut self, cx: &Context, it: &ast::Item) {
fn is_camel_case(ident: ast::Ident) -> bool { fn is_camel_case(ident: ast::Ident) -> bool {
let ident = token::get_ident(ident); let ident = token::get_ident(ident);
@ -690,8 +753,7 @@ impl LintPass for NonCamelCaseTypes {
let s = token::get_ident(ident); let s = token::get_ident(ident);
if !is_camel_case(ident) { if !is_camel_case(ident) {
cx.span_lint(lint:: cx.span_lint(non_camel_case_types, span,
NonCamelCaseTypes, span,
format!("{} `{}` should have a camel case name such as `{}`", format!("{} `{}` should have a camel case name such as `{}`",
sort, s, to_camel_case(s.get())).as_slice()); sort, s, to_camel_case(s.get())).as_slice());
} }
@ -744,6 +806,9 @@ fn method_context(cx: &Context, m: &ast::Method) -> MethodContext {
} }
} }
declare_lint!(non_snake_case_functions, Warn,
"methods and functions should have snake case names")
#[deriving(Default)] #[deriving(Default)]
pub struct NonSnakeCaseFunctions; pub struct NonSnakeCaseFunctions;
@ -785,7 +850,7 @@ impl NonSnakeCaseFunctions {
let s = token::get_ident(ident); let s = token::get_ident(ident);
if !is_snake_case(ident) { if !is_snake_case(ident) {
cx.span_lint(lint::NonSnakeCaseFunctions, span, cx.span_lint(non_snake_case_functions, span,
format!("{} `{}` should have a snake case name such as `{}`", format!("{} `{}` should have a snake case name such as `{}`",
sort, s, to_snake_case(s.get())).as_slice()); sort, s, to_snake_case(s.get())).as_slice());
} }
@ -793,6 +858,10 @@ impl NonSnakeCaseFunctions {
} }
impl LintPass for NonSnakeCaseFunctions { impl LintPass for NonSnakeCaseFunctions {
fn get_lints(&self) -> LintArray {
lint_array!(non_snake_case_functions)
}
fn check_fn(&mut self, cx: &Context, fn check_fn(&mut self, cx: &Context,
fk: &visit::FnKind, _: &ast::FnDecl, fk: &visit::FnKind, _: &ast::FnDecl,
_: &ast::Block, span: Span, _: ast::NodeId) { _: &ast::Block, span: Span, _: ast::NodeId) {
@ -815,10 +884,17 @@ impl LintPass for NonSnakeCaseFunctions {
} }
} }
declare_lint!(non_uppercase_statics, Allow,
"static constants should have uppercase identifiers")
#[deriving(Default)] #[deriving(Default)]
pub struct NonUppercaseStatics; pub struct NonUppercaseStatics;
impl LintPass for NonUppercaseStatics { impl LintPass for NonUppercaseStatics {
fn get_lints(&self) -> LintArray {
lint_array!(non_uppercase_statics)
}
fn check_item(&mut self, cx: &Context, it: &ast::Item) { fn check_item(&mut self, cx: &Context, it: &ast::Item) {
match it.node { match it.node {
// only check static constants // only check static constants
@ -828,7 +904,7 @@ impl LintPass for NonUppercaseStatics {
// ones (some scripts don't have a concept of // ones (some scripts don't have a concept of
// upper/lowercase) // upper/lowercase)
if s.get().chars().any(|c| c.is_lowercase()) { if s.get().chars().any(|c| c.is_lowercase()) {
cx.span_lint(lint::NonUppercaseStatics, it.span, cx.span_lint(non_uppercase_statics, it.span,
format!("static constant `{}` should have an uppercase name \ format!("static constant `{}` should have an uppercase name \
such as `{}`", s.get(), such as `{}`", s.get(),
s.get().chars().map(|c| c.to_uppercase()) s.get().chars().map(|c| c.to_uppercase())
@ -838,6 +914,18 @@ impl LintPass for NonUppercaseStatics {
_ => {} _ => {}
} }
} }
}
declare_lint!(non_uppercase_pattern_statics, Warn,
"static constants in match patterns should be all caps")
#[deriving(Default)]
pub struct NonUppercasePatternStatics;
impl LintPass for NonUppercasePatternStatics {
fn get_lints(&self) -> LintArray {
lint_array!(non_uppercase_pattern_statics)
}
fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
// Lint for constants that look like binding identifiers (#7526) // Lint for constants that look like binding identifiers (#7526)
@ -847,7 +935,7 @@ impl LintPass for NonUppercaseStatics {
let ident = path.segments.last().unwrap().identifier; let ident = path.segments.last().unwrap().identifier;
let s = token::get_ident(ident); let s = token::get_ident(ident);
if s.get().chars().any(|c| c.is_lowercase()) { if s.get().chars().any(|c| c.is_lowercase()) {
cx.span_lint(lint::NonUppercasePatternStatics, path.span, cx.span_lint(non_uppercase_pattern_statics, path.span,
format!("static constant in pattern `{}` should have an uppercase \ format!("static constant in pattern `{}` should have an uppercase \
name such as `{}`", s.get(), name such as `{}`", s.get(),
s.get().chars().map(|c| c.to_uppercase()) s.get().chars().map(|c| c.to_uppercase())
@ -859,10 +947,17 @@ impl LintPass for NonUppercaseStatics {
} }
} }
declare_lint!(uppercase_variables, Warn,
"variable and structure field names should start with a lowercase character")
#[deriving(Default)] #[deriving(Default)]
pub struct UppercaseVariables; pub struct UppercaseVariables;
impl LintPass for UppercaseVariables { impl LintPass for UppercaseVariables {
fn get_lints(&self) -> LintArray {
lint_array!(uppercase_variables)
}
fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
match &p.node { match &p.node {
&ast::PatIdent(_, ref path, _) => { &ast::PatIdent(_, ref path, _) => {
@ -873,9 +968,7 @@ impl LintPass for UppercaseVariables {
let ident = path.segments.last().unwrap().identifier; let ident = path.segments.last().unwrap().identifier;
let s = token::get_ident(ident); let s = token::get_ident(ident);
if s.get().len() > 0 && s.get().char_at(0).is_uppercase() { if s.get().len() > 0 && s.get().char_at(0).is_uppercase() {
cx.span_lint(lint:: cx.span_lint(uppercase_variables, path.span,
UppercaseVariables,
path.span,
"variable names should start with a lowercase character"); "variable names should start with a lowercase character");
} }
} }
@ -893,8 +986,7 @@ impl LintPass for UppercaseVariables {
ast::StructField_ { kind: ast::NamedField(ident, _), .. } => { ast::StructField_ { kind: ast::NamedField(ident, _), .. } => {
let s = token::get_ident(ident); let s = token::get_ident(ident);
if s.get().char_at(0).is_uppercase() { if s.get().char_at(0).is_uppercase() {
cx.span_lint(lint::UppercaseVariables, cx.span_lint(uppercase_variables, sf.span,
sf.span,
"structure field names should start with a lowercase character"); "structure field names should start with a lowercase character");
} }
} }
@ -904,6 +996,9 @@ impl LintPass for UppercaseVariables {
} }
} }
declare_lint!(unnecessary_parens, Warn,
"`if`, `match`, `while` and `return` do not need parentheses")
#[deriving(Default)] #[deriving(Default)]
pub struct UnnecessaryParens; pub struct UnnecessaryParens;
@ -911,9 +1006,8 @@ impl UnnecessaryParens {
fn check_unnecessary_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str) { fn check_unnecessary_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str) {
match value.node { match value.node {
ast::ExprParen(_) => { ast::ExprParen(_) => {
cx.span_lint(lint::UnnecessaryParens, value.span, cx.span_lint(unnecessary_parens, value.span,
format!("unnecessary parentheses around {}", format!("unnecessary parentheses around {}", msg).as_slice())
msg).as_slice())
} }
_ => {} _ => {}
} }
@ -921,6 +1015,10 @@ impl UnnecessaryParens {
} }
impl LintPass for UnnecessaryParens { impl LintPass for UnnecessaryParens {
fn get_lints(&self) -> LintArray {
lint_array!(unnecessary_parens)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
let (value, msg) = match e.node { let (value, msg) = match e.node {
ast::ExprIf(cond, _, _) => (cond, "`if` condition"), ast::ExprIf(cond, _, _) => (cond, "`if` condition"),
@ -949,18 +1047,24 @@ impl LintPass for UnnecessaryParens {
} }
} }
declare_lint!(unused_unsafe, Warn,
"unnecessary use of an `unsafe` block")
#[deriving(Default)] #[deriving(Default)]
pub struct UnusedUnsafe; pub struct UnusedUnsafe;
impl LintPass for UnusedUnsafe { impl LintPass for UnusedUnsafe {
fn get_lints(&self) -> LintArray {
lint_array!(unused_unsafe)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
match e.node { match e.node {
// Don't warn about generated blocks, that'll just pollute the output. // Don't warn about generated blocks, that'll just pollute the output.
ast::ExprBlock(ref blk) => { ast::ExprBlock(ref blk) => {
if blk.rules == ast::UnsafeBlock(ast::UserProvided) && if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
!cx.tcx.used_unsafe.borrow().contains(&blk.id) { !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
cx.span_lint(lint::UnusedUnsafe, blk.span, cx.span_lint(unused_unsafe, blk.span, "unnecessary `unsafe` block");
"unnecessary `unsafe` block");
} }
} }
_ => () _ => ()
@ -968,21 +1072,31 @@ impl LintPass for UnusedUnsafe {
} }
} }
declare_lint!(unsafe_block, Allow,
"usage of an `unsafe` block")
#[deriving(Default)] #[deriving(Default)]
pub struct UnsafeBlock; pub struct UnsafeBlock;
impl LintPass for UnsafeBlock { impl LintPass for UnsafeBlock {
fn get_lints(&self) -> LintArray {
lint_array!(unsafe_block)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
match e.node { match e.node {
// Don't warn about generated blocks, that'll just pollute the output. // Don't warn about generated blocks, that'll just pollute the output.
ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => { ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
cx.span_lint(lint::UnsafeBlock, blk.span, "usage of an `unsafe` block"); cx.span_lint(unsafe_block, blk.span, "usage of an `unsafe` block");
} }
_ => () _ => ()
} }
} }
} }
declare_lint!(unused_mut, Warn,
"detect mut variables which don't need to be mutable")
#[deriving(Default)] #[deriving(Default)]
pub struct UnusedMut; pub struct UnusedMut;
@ -1016,14 +1130,18 @@ impl UnusedMut {
let used_mutables = cx.tcx.used_mut_nodes.borrow(); let used_mutables = cx.tcx.used_mut_nodes.borrow();
for (_, v) in mutables.iter() { for (_, v) in mutables.iter() {
if !v.iter().any(|e| used_mutables.contains(e)) { if !v.iter().any(|e| used_mutables.contains(e)) {
cx.span_lint(lint::UnusedMut, cx.tcx.map.span(*v.get(0)), cx.span_lint(unused_mut, cx.tcx.map.span(*v.get(0)),
"variable does not need to be mutable"); "variable does not need to be mutable");
} }
} }
} }
} }
impl LintPass for UnusedMut { impl LintPass for UnusedMut {
fn get_lints(&self) -> LintArray {
lint_array!(unused_mut)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
match e.node { match e.node {
ast::ExprMatch(_, ref arms) => { ast::ExprMatch(_, ref arms) => {
@ -1063,10 +1181,17 @@ enum Allocation {
BoxAllocation BoxAllocation
} }
declare_lint!(unnecessary_allocation, Warn,
"detects unnecessary allocations that can be eliminated")
#[deriving(Default)] #[deriving(Default)]
pub struct UnnecessaryAllocation; pub struct UnnecessaryAllocation;
impl LintPass for UnnecessaryAllocation { impl LintPass for UnnecessaryAllocation {
fn get_lints(&self) -> LintArray {
lint_array!(unnecessary_allocation)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
// Warn if string and vector literals with sigils, or boxing expressions, // Warn if string and vector literals with sigils, or boxing expressions,
// are immediately borrowed. // are immediately borrowed.
@ -1086,27 +1211,24 @@ impl LintPass for UnnecessaryAllocation {
_ => return _ => return
}; };
let report = |msg| {
cx.span_lint(lint::UnnecessaryAllocation, e.span, msg);
};
match cx.tcx.adjustments.borrow().find(&e.id) { match cx.tcx.adjustments.borrow().find(&e.id) {
Some(adjustment) => { Some(adjustment) => {
match *adjustment { match *adjustment {
ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => {
match (allocation, autoref) { match (allocation, autoref) {
(VectorAllocation, Some(ty::AutoBorrowVec(..))) => { (VectorAllocation, Some(ty::AutoBorrowVec(..))) => {
report("unnecessary allocation, the sigil can be \ cx.span_lint(unnecessary_allocation, e.span,
removed"); "unnecessary allocation, the sigil can be removed");
} }
(BoxAllocation, (BoxAllocation,
Some(ty::AutoPtr(_, ast::MutImmutable))) => { Some(ty::AutoPtr(_, ast::MutImmutable))) => {
report("unnecessary allocation, use & instead"); cx.span_lint(unnecessary_allocation, e.span,
"unnecessary allocation, use & instead");
} }
(BoxAllocation, (BoxAllocation,
Some(ty::AutoPtr(_, ast::MutMutable))) => { Some(ty::AutoPtr(_, ast::MutMutable))) => {
report("unnecessary allocation, use &mut \ cx.span_lint(unnecessary_allocation, e.span,
instead"); "unnecessary allocation, use &mut instead");
} }
_ => () _ => ()
} }
@ -1119,6 +1241,9 @@ impl LintPass for UnnecessaryAllocation {
} }
} }
declare_lint!(missing_doc, Allow,
"detects missing documentation for public members")
pub struct MissingDoc { pub struct MissingDoc {
/// Set of nodes exported from this module. /// Set of nodes exported from this module.
exported_items: Option<ExportedItems>, exported_items: Option<ExportedItems>,
@ -1175,15 +1300,17 @@ impl MissingDoc {
} }
}); });
if !has_doc { if !has_doc {
cx.span_lint(lint::MissingDoc, cx.span_lint(missing_doc, sp,
sp, format!("missing documentation for {}", desc).as_slice());
format!("missing documentation for {}",
desc).as_slice());
} }
} }
} }
impl LintPass for MissingDoc { impl LintPass for MissingDoc {
fn get_lints(&self) -> LintArray {
lint_array!(missing_doc)
}
fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) { fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) {
let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| { let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
attr.check_name("doc") && match attr.meta_item_list() { attr.check_name("doc") && match attr.meta_item_list() {
@ -1268,12 +1395,25 @@ impl LintPass for MissingDoc {
} }
} }
declare_lint!(deprecated, Warn,
"detects use of #[deprecated] items")
declare_lint!(experimental, Warn,
"detects use of #[experimental] items")
declare_lint!(unstable, Allow,
"detects use of #[unstable] items (incl. items with no stability attribute)")
/// Checks for use of items with #[deprecated], #[experimental] and /// Checks for use of items with #[deprecated], #[experimental] and
/// #[unstable] (or none of them) attributes. /// #[unstable] (or none of them) attributes.
#[deriving(Default)] #[deriving(Default)]
pub struct Stability; pub struct Stability;
impl LintPass for Stability { impl LintPass for Stability {
fn get_lints(&self) -> LintArray {
lint_array!(deprecated, experimental, unstable)
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
let id = match e.node { let id = match e.node {
ast::ExprPath(..) | ast::ExprStruct(..) => { ast::ExprPath(..) | ast::ExprStruct(..) => {
@ -1342,13 +1482,13 @@ impl LintPass for Stability {
let (lint, label) = match stability { let (lint, label) = match stability {
// no stability attributes == Unstable // no stability attributes == Unstable
None => (lint::Unstable, "unmarked"), None => (unstable, "unmarked"),
Some(attr::Stability { level: attr::Unstable, .. }) => Some(attr::Stability { level: attr::Unstable, .. }) =>
(lint::Unstable, "unstable"), (unstable, "unstable"),
Some(attr::Stability { level: attr::Experimental, .. }) => Some(attr::Stability { level: attr::Experimental, .. }) =>
(lint::Experimental, "experimental"), (experimental, "experimental"),
Some(attr::Stability { level: attr::Deprecated, .. }) => Some(attr::Stability { level: attr::Deprecated, .. }) =>
(lint::Deprecated, "deprecated"), (deprecated, "deprecated"),
_ => return _ => return
}; };
@ -1362,3 +1502,80 @@ impl LintPass for Stability {
cx.span_lint(lint, e.span, msg.as_slice()); cx.span_lint(lint, e.span, msg.as_slice());
} }
} }
/// Doesn't actually warn; just gathers information for use by
/// checks in trans.
#[deriving(Default)]
pub struct GatherNodeLevels;
impl LintPass for GatherNodeLevels {
fn get_lints(&self) -> LintArray {
lint_array!()
}
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
match it.node {
ast::ItemEnum(..) => {
let lint_id = lint::LintId::of(variant_size_difference);
match cx.get_level_source(lint_id) {
lvlsrc @ (lvl, _) if lvl != lint::Allow => {
cx.insert_node_level(it.id, lint_id, lvlsrc);
},
_ => { }
}
},
_ => { }
}
}
}
declare_lint!(pub unused_imports, Warn,
"imports that are never used")
declare_lint!(pub unnecessary_qualification, Allow,
"detects unnecessarily qualified names")
declare_lint!(pub unrecognized_lint, Warn,
"unrecognized lint attribute")
declare_lint!(pub unused_variable, Warn,
"detect variables which are not used in any way")
declare_lint!(pub dead_assignment, Warn,
"detect assignments that will never be read")
declare_lint!(pub dead_code, Warn,
"detect piece of code that will never be used")
declare_lint!(pub visible_private_types, Warn,
"detect use of private types in exported type signatures")
declare_lint!(pub unreachable_code, Warn,
"detects unreachable code")
declare_lint!(pub warnings, Warn,
"mass-change the level for lints which produce warnings")
declare_lint!(pub unknown_features, Deny,
"unknown features found in crate-level #[feature] directives")
declare_lint!(pub unknown_crate_type, Deny,
"unknown crate type found in #[crate_type] directive")
declare_lint!(pub variant_size_difference, Allow,
"detects enums with widely varying variant sizes")
/// Does nothing as a lint pass, but registers some `Lint`s
/// which are used by other parts of the compiler.
#[deriving(Default)]
pub struct HardwiredLints;
impl LintPass for HardwiredLints {
fn get_lints(&self) -> LintArray {
lint_array!(
unused_imports, unnecessary_qualification, unrecognized_lint,
unused_variable, dead_assignment, dead_code, visible_private_types,
unreachable_code, warnings, unknown_features, unknown_crate_type,
variant_size_difference)
}
}

View file

@ -41,13 +41,13 @@
//! this file, use `span_lint` instead of `add_lint`. //! this file, use `span_lint` instead of `add_lint`.
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![macro_escape]
use driver::session;
use middle::dead::DEAD_CODE_LINT_STR;
use middle::privacy::ExportedItems; use middle::privacy::ExportedItems;
use middle::ty; use middle::ty;
use middle::typeck::astconv::AstConv; use middle::typeck::astconv::AstConv;
use middle::typeck::infer; use middle::typeck::infer;
use driver::session::Session;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@ -55,23 +55,83 @@ use std::gc::Gc;
use std::to_str::ToStr; use std::to_str::ToStr;
use std::cell::RefCell; use std::cell::RefCell;
use std::default::Default; use std::default::Default;
use std::collections::SmallIntMap; use std::hash::Hash;
use std::tuple::Tuple2;
use std::hash;
use syntax::ast_util::IdVisitingOperation; use syntax::ast_util::IdVisitingOperation;
use syntax::attr::AttrMetaMethods; use syntax::attr::AttrMetaMethods;
use syntax::attr;
use syntax::codemap::Span; use syntax::codemap::Span;
use syntax::parse::token::InternedString;
use syntax::visit::{Visitor, FnKind}; use syntax::visit::{Visitor, FnKind};
use syntax::{ast, ast_util, visit}; use syntax::{ast, ast_util, visit};
mod builtin; #[macro_export]
macro_rules! lint_initializer (
($name:ident, $level:ident, $desc:expr) => (
::rustc::lint::Lint {
name: stringify!($name),
default_level: ::rustc::lint::$level,
desc: $desc,
}
)
)
/// Trait for types providing lint checks. Each method checks a single syntax #[macro_export]
/// node, and should not invoke methods recursively (unlike `Visitor`). Each macro_rules! declare_lint (
/// method has a default do-nothing implementation. The trait also contains a // FIXME(#14660): deduplicate
/// few lint-specific methods with no equivalent in `Visitor`. (pub $name:ident, $level:ident, $desc:expr) => (
pub static $name: &'static ::rustc::lint::Lint
= &lint_initializer!($name, $level, $desc);
);
($name:ident, $level:ident, $desc:expr) => (
static $name: &'static ::rustc::lint::Lint
= &lint_initializer!($name, $level, $desc);
);
)
#[macro_export]
macro_rules! lint_array ( ($( $lint:expr ),*) => (
{
static array: LintArray = &[ $( $lint ),* ];
array
}
))
pub mod builtin;
/// Specification of a single lint.
pub struct Lint {
/// An identifier for the lint, written with underscores,
/// e.g. "unused_imports". This identifies the lint in
/// attributes and in command-line arguments. On the
/// command line, underscores become dashes.
pub name: &'static str,
/// Default level for the lint.
pub default_level: Level,
/// Description of the lint or the issue it detects,
/// e.g. "imports that are never used"
pub desc: &'static str,
}
type LintArray = &'static [&'static Lint];
/// Trait for types providing lint checks. Each `check` method checks a single
/// syntax node, and should not invoke methods recursively (unlike `Visitor`).
/// By default they do nothing.
// //
// FIXME: eliminate the duplication with `Visitor` // FIXME: eliminate the duplication with `Visitor`. But this also
// contains a few lint-specific methods with no equivalent in `Visitor`.
trait LintPass { trait LintPass {
/// Get descriptions of the lints this `LintPass` object can emit.
///
/// NB: there is no enforcement that the object only emits lints it registered.
/// And some `rustc` internal `LintPass`es register lints to be emitted by other
/// parts of the compiler. If you want enforced access restrictions for your
/// `Lint`, make it a private `static` item in its own module.
fn get_lints(&self) -> LintArray;
fn check_crate(&mut self, _: &Context, _: &ExportedItems, _: &ast::Crate) { } fn check_crate(&mut self, _: &Context, _: &ExportedItems, _: &ast::Crate) { }
fn check_ident(&mut self, _: &Context, _: Span, _: ast::Ident) { } fn check_ident(&mut self, _: &Context, _: Span, _: ast::Ident) { }
fn check_mod(&mut self, _: &Context, _: &ast::Mod, _: Span, _: ast::NodeId) { } fn check_mod(&mut self, _: &Context, _: &ast::Mod, _: Span, _: ast::NodeId) { }
@ -116,63 +176,37 @@ trait LintPass {
type LintPassObject = Box<LintPass + 'static>; type LintPassObject = Box<LintPass + 'static>;
#[deriving(Clone, Show, PartialEq, PartialOrd, Eq, Ord, Hash)] /// Identifies a lint known to the compiler.
pub enum LintId { #[deriving(Clone)]
CTypes, pub struct LintId {
UnusedImports, // Identity is based on pointer equality of this field.
UnnecessaryQualification, lint: &'static Lint,
WhileTrue,
PathStatement,
UnrecognizedLint,
NonCamelCaseTypes,
NonUppercaseStatics,
NonUppercasePatternStatics,
NonSnakeCaseFunctions,
UppercaseVariables,
UnnecessaryParens,
TypeLimits,
TypeOverflow,
UnusedUnsafe,
UnsafeBlock,
UnusedAttribute,
UnknownFeatures,
UnknownCrateType,
UnsignedNegate,
VariantSizeDifference,
ManagedHeapMemory,
OwnedHeapMemory,
HeapMemory,
UnusedVariable,
DeadAssignment,
UnusedMut,
UnnecessaryAllocation,
DeadCode,
VisiblePrivateTypes,
UnnecessaryTypecast,
MissingDoc,
UnreachableCode,
Deprecated,
Experimental,
Unstable,
UnusedMustUse,
UnusedResult,
Warnings,
RawPointerDeriving,
} }
pub fn level_to_str(lv: Level) -> &'static str { impl PartialEq for LintId {
match lv { fn eq(&self, other: &LintId) -> bool {
Allow => "allow", (self.lint as *Lint) == (other.lint as *Lint)
Warn => "warn", }
Deny => "deny", }
Forbid => "forbid"
impl Eq for LintId { }
impl<S: hash::Writer> Hash<S> for LintId {
fn hash(&self, state: &mut S) {
let ptr = self.lint as *Lint;
ptr.hash(state);
}
}
impl LintId {
pub fn of(lint: &'static Lint) -> LintId {
LintId {
lint: lint,
}
}
pub fn as_str(&self) -> &'static str {
self.lint.name
} }
} }
@ -181,14 +215,26 @@ pub enum Level {
Allow, Warn, Deny, Forbid Allow, Warn, Deny, Forbid
} }
#[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)] impl Level {
pub struct LintSpec { pub fn as_str(self) -> &'static str {
pub default: Level, match self {
pub lint: LintId, Allow => "allow",
pub desc: &'static str, Warn => "warn",
} Deny => "deny",
Forbid => "forbid",
}
}
pub type LintDict = HashMap<&'static str, LintSpec>; pub fn from_str(x: &str) -> Option<Level> {
match x {
"allow" => Some(Allow),
"warn" => Some(Warn),
"deny" => Some(Deny),
"forbid" => Some(Forbid),
_ => None,
}
}
}
// this is public for the lints that run in trans // this is public for the lints that run in trans
#[deriving(PartialEq)] #[deriving(PartialEq)]
@ -198,331 +244,49 @@ pub enum LintSource {
CommandLine CommandLine
} }
static lint_table: &'static [(&'static str, LintSpec)] = &[ pub type LevelSource = (Level, LintSource);
("ctypes",
LintSpec {
lint: CTypes,
desc: "proper use of libc types in foreign modules",
default: Warn
}),
("unused_imports",
LintSpec {
lint: UnusedImports,
desc: "imports that are never used",
default: Warn
}),
("unnecessary_qualification",
LintSpec {
lint: UnnecessaryQualification,
desc: "detects unnecessarily qualified names",
default: Allow
}),
("while_true",
LintSpec {
lint: WhileTrue,
desc: "suggest using `loop { }` instead of `while true { }`",
default: Warn
}),
("path_statement",
LintSpec {
lint: PathStatement,
desc: "path statements with no effect",
default: Warn
}),
("unrecognized_lint",
LintSpec {
lint: UnrecognizedLint,
desc: "unrecognized lint attribute",
default: Warn
}),
("non_camel_case_types",
LintSpec {
lint: NonCamelCaseTypes,
desc: "types, variants and traits should have camel case names",
default: Warn
}),
("non_uppercase_statics",
LintSpec {
lint: NonUppercaseStatics,
desc: "static constants should have uppercase identifiers",
default: Allow
}),
("non_uppercase_pattern_statics",
LintSpec {
lint: NonUppercasePatternStatics,
desc: "static constants in match patterns should be all caps",
default: Warn
}),
("non_snake_case_functions",
LintSpec {
lint: NonSnakeCaseFunctions,
desc: "methods and functions should have snake case names",
default: Warn
}),
("uppercase_variables",
LintSpec {
lint: UppercaseVariables,
desc: "variable and structure field names should start with a lowercase character",
default: Warn
}),
("unnecessary_parens",
LintSpec {
lint: UnnecessaryParens,
desc: "`if`, `match`, `while` and `return` do not need parentheses",
default: Warn
}),
("managed_heap_memory",
LintSpec {
lint: ManagedHeapMemory,
desc: "use of managed (@ type) heap memory",
default: Allow
}),
("owned_heap_memory",
LintSpec {
lint: OwnedHeapMemory,
desc: "use of owned (Box type) heap memory",
default: Allow
}),
("heap_memory",
LintSpec {
lint: HeapMemory,
desc: "use of any (Box type or @ type) heap memory",
default: Allow
}),
("type_limits",
LintSpec {
lint: TypeLimits,
desc: "comparisons made useless by limits of the types involved",
default: Warn
}),
("type_overflow",
LintSpec {
lint: TypeOverflow,
desc: "literal out of range for its type",
default: Warn
}),
("unused_unsafe",
LintSpec {
lint: UnusedUnsafe,
desc: "unnecessary use of an `unsafe` block",
default: Warn
}),
("unsafe_block",
LintSpec {
lint: UnsafeBlock,
desc: "usage of an `unsafe` block",
default: Allow
}),
("unused_attribute",
LintSpec {
lint: UnusedAttribute,
desc: "detects attributes that were not used by the compiler",
default: Warn
}),
("unused_variable",
LintSpec {
lint: UnusedVariable,
desc: "detect variables which are not used in any way",
default: Warn
}),
("dead_assignment",
LintSpec {
lint: DeadAssignment,
desc: "detect assignments that will never be read",
default: Warn
}),
("unnecessary_typecast",
LintSpec {
lint: UnnecessaryTypecast,
desc: "detects unnecessary type casts, that can be removed",
default: Allow,
}),
("unused_mut",
LintSpec {
lint: UnusedMut,
desc: "detect mut variables which don't need to be mutable",
default: Warn
}),
("unnecessary_allocation",
LintSpec {
lint: UnnecessaryAllocation,
desc: "detects unnecessary allocations that can be eliminated",
default: Warn
}),
(DEAD_CODE_LINT_STR,
LintSpec {
lint: DeadCode,
desc: "detect piece of code that will never be used",
default: Warn
}),
("visible_private_types",
LintSpec {
lint: VisiblePrivateTypes,
desc: "detect use of private types in exported type signatures",
default: Warn
}),
("missing_doc",
LintSpec {
lint: MissingDoc,
desc: "detects missing documentation for public members",
default: Allow
}),
("unreachable_code",
LintSpec {
lint: UnreachableCode,
desc: "detects unreachable code",
default: Warn
}),
("deprecated",
LintSpec {
lint: Deprecated,
desc: "detects use of #[deprecated] items",
default: Warn
}),
("experimental",
LintSpec {
lint: Experimental,
desc: "detects use of #[experimental] items",
// FIXME #6875: Change to Warn after std library stabilization is complete
default: Allow
}),
("unstable",
LintSpec {
lint: Unstable,
desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
default: Allow
}),
("warnings",
LintSpec {
lint: Warnings,
desc: "mass-change the level for lints which produce warnings",
default: Warn
}),
("unknown_features",
LintSpec {
lint: UnknownFeatures,
desc: "unknown features found in crate-level #[feature] directives",
default: Deny,
}),
("unknown_crate_type",
LintSpec {
lint: UnknownCrateType,
desc: "unknown crate type found in #[crate_type] directive",
default: Deny,
}),
("unsigned_negate",
LintSpec {
lint: UnsignedNegate,
desc: "using an unary minus operator on unsigned type",
default: Warn
}),
("variant_size_difference",
LintSpec {
lint: VariantSizeDifference,
desc: "detects enums with widely varying variant sizes",
default: Allow,
}),
("unused_must_use",
LintSpec {
lint: UnusedMustUse,
desc: "unused result of a type flagged as #[must_use]",
default: Warn,
}),
("unused_result",
LintSpec {
lint: UnusedResult,
desc: "unused result of an expression in a statement",
default: Allow,
}),
("raw_pointer_deriving",
LintSpec {
lint: RawPointerDeriving,
desc: "uses of #[deriving] with raw pointers are rarely correct",
default: Warn,
}),
];
/*
Pass names should not contain a '-', as the compiler normalizes
'-' to '_' in command-line flags
*/
pub fn get_lint_dict() -> LintDict {
lint_table.iter().map(|&(k, v)| (k, v)).collect()
}
struct Context<'a> { struct Context<'a> {
/// All known lint modes (string versions) /// Trait objects for each lint pass.
dict: LintDict, lint_objects: Vec<RefCell<LintPassObject>>,
/// Current levels of each lint warning
cur: SmallIntMap<(Level, LintSource)>, /// Lints indexed by name.
/// Context we're checking in (used to access fields like sess) lints_by_name: HashMap<&'static str, LintId>,
/// Current levels of each lint, and where they were set.
levels: HashMap<LintId, LevelSource>,
/// Context we're checking in (used to access fields like sess).
tcx: &'a ty::ctxt, tcx: &'a ty::ctxt,
/// When recursing into an attributed node of the ast which modifies lint /// When recursing into an attributed node of the ast which modifies lint
/// levels, this stack keeps track of the previous lint levels of whatever /// levels, this stack keeps track of the previous lint levels of whatever
/// was modified. /// was modified.
level_stack: Vec<(LintId, Level, LintSource)>, level_stack: Vec<(LintId, LevelSource)>,
/// Level of lints for certain NodeIds, stored here because the body of /// Level of lints for certain NodeIds, stored here because the body of
/// the lint needs to run in trans. /// the lint needs to run in trans.
node_levels: RefCell<HashMap<(ast::NodeId, LintId), (Level, LintSource)>>, node_levels: RefCell<HashMap<(ast::NodeId, LintId), LevelSource>>,
/// Trait objects for each lint.
lints: Vec<RefCell<LintPassObject>>,
} }
/// Convenience macro for calling a `LintPass` method on every lint in the context. /// Convenience macro for calling a `LintPass` method on every pass in the context.
macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ( macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => (
for tl in $cx.lints.iter() { for obj in $cx.lint_objects.iter() {
tl.borrow_mut().$f($cx, $($args),*); obj.borrow_mut().$f($cx, $($args),*);
} }
)) ))
pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span, /// Emit a lint as a `span_warn` or `span_err` (or not at all)
lint_str: &str, tcx: &ty::ctxt) { /// according to `level`. This lives outside of `Context` so
/// it can be used by checks in trans that run after the main
/// lint phase is finished.
pub fn emit_lint(sess: &Session, lint: &'static Lint,
lvlsrc: LevelSource, span: Span, msg: &str) {
let (level, source) = lvlsrc;
if level == Allow { return } if level == Allow { return }
let mut note = None; let mut note = None;
let msg = match src { let msg = match source {
Default => { Default => {
format!("{}, #[{}({})] on by default", msg, format!("{}, #[{}({})] on by default", msg,
level_to_str(level), lint_str) level_to_str(level), lint_str)
@ -532,75 +296,50 @@ pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span,
match level { match level {
Warn => 'W', Deny => 'D', Forbid => 'F', Warn => 'W', Deny => 'D', Forbid => 'F',
Allow => fail!() Allow => fail!()
}, lint_str.replace("_", "-")) }, lint.name.replace("_", "-"))
}, },
Node(src) => { Node(src) => {
note = Some(src); note = Some(src);
msg.to_str() msg.to_string()
} }
}; };
match level { match level {
Warn => { tcx.sess.span_warn(span, msg.as_slice()); } Warn => { sess.span_warn(span, msg.as_slice()); }
Deny | Forbid => { tcx.sess.span_err(span, msg.as_slice()); } Deny | Forbid => { sess.span_err(span, msg.as_slice()); }
Allow => fail!(), Allow => fail!(),
} }
for &span in note.iter() { for span in note.move_iter() {
tcx.sess.span_note(span, "lint level defined here"); sess.span_note(span, "lint level defined here");
} }
} }
pub fn lint_to_str(lint: LintId) -> &'static str {
for &(name, lspec) in lint_table.iter() {
if lspec.lint == lint {
return name;
}
}
fail!("unrecognized lint: {}", lint);
}
impl<'a> Context<'a> { impl<'a> Context<'a> {
fn get_level(&self, lint: LintId) -> Level { fn get_level_source(&self, lint: LintId) -> LevelSource {
match self.cur.find(&(lint as uint)) { match self.levels.find(&lint) {
Some(&(lvl, _)) => lvl, Some(&s) => s,
None => Allow None => (Allow, Default),
} }
} }
fn get_source(&self, lint: LintId) -> LintSource { fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
match self.cur.find(&(lint as uint)) { if lvlsrc.val0() == Allow {
Some(&(_, src)) => src, self.levels.remove(&lint);
None => Default
}
}
fn set_level(&mut self, lint: LintId, level: Level, src: LintSource) {
if level == Allow {
self.cur.remove(&(lint as uint));
} else { } else {
self.cur.insert(lint as uint, (level, src)); self.levels.insert(lint, lvlsrc);
} }
} }
fn lint_to_str(&self, lint: LintId) -> &'static str { fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
for (k, v) in self.dict.iter() { let (level, src) = match self.levels.find(&LintId::of(lint)) {
if v.lint == lint { None => return,
return *k; Some(&(Warn, src))
} => (self.get_level_source(LintId::of(builtin::warnings)).val0(), src),
}
fail!("unregistered lint {}", lint);
}
fn span_lint(&self, lint: LintId, span: Span, msg: &str) {
let (level, src) = match self.cur.find(&(lint as uint)) {
None => { return }
Some(&(Warn, src)) => (self.get_level(Warnings), src),
Some(&pair) => pair, Some(&pair) => pair,
}; };
emit_lint(level, src, msg, span, self.lint_to_str(lint), self.tcx); emit_lint(&self.tcx.sess, lint, (level, src), span, msg);
} }
/** /**
@ -615,35 +354,22 @@ impl<'a> Context<'a> {
// current dictionary of lint information. Along the way, keep a history // current dictionary of lint information. Along the way, keep a history
// of what we changed so we can roll everything back after invoking the // of what we changed so we can roll everything back after invoking the
// specified closure // specified closure
let lint_attrs = self.gather_lint_attrs(attrs);
let mut pushed = 0u; let mut pushed = 0u;
each_lint(&self.tcx.sess, attrs, |meta, level, lintname| { for (lint_id, level, span) in lint_attrs.move_iter() {
match self.dict.find_equiv(&lintname) { let now = self.get_level_source(lint_id).val0();
None => { if now == Forbid && level != Forbid {
self.span_lint( let lint_name = lint_id.as_str();
UnrecognizedLint, self.tcx.sess.span_err(span,
meta.span, format!("{}({}) overruled by outer forbid({})",
format!("unknown `{}` attribute: `{}`", level.as_str(), lint_name, lint_name).as_slice());
level_to_str(level), lintname).as_slice()); } else if now != level {
} let src = self.get_level_source(lint_id).val1();
Some(lint) => { self.level_stack.push((lint_id, (now, src)));
let lint = lint.lint; pushed += 1;
let now = self.get_level(lint); self.set_level(lint_id, (level, Node(span)));
if now == Forbid && level != Forbid {
self.tcx.sess.span_err(meta.span,
format!("{}({}) overruled by outer forbid({})",
level_to_str(level),
lintname,
lintname).as_slice());
} else if now != level {
let src = self.get_source(lint);
self.level_stack.push((lint, now, src));
pushed += 1;
self.set_level(lint, level, Node(meta.span));
}
}
} }
true }
});
run_lints!(self, enter_lint_attrs, attrs); run_lints!(self, enter_lint_attrs, attrs);
f(self); f(self);
@ -651,8 +377,8 @@ impl<'a> Context<'a> {
// rollback // rollback
for _ in range(0, pushed) { for _ in range(0, pushed) {
let (lint, lvl, src) = self.level_stack.pop().unwrap(); let (lint, lvlsrc) = self.level_stack.pop().unwrap();
self.set_level(lint, lvl, src); self.set_level(lint, lvlsrc);
} }
} }
@ -665,65 +391,49 @@ impl<'a> Context<'a> {
f(&mut v); f(&mut v);
} }
fn insert_node_level(&self, id: ast::NodeId, lint: LintId, lvl: Level, src: LintSource) { fn insert_node_level(&self, id: ast::NodeId, lint: LintId, lvlsrc: LevelSource) {
self.node_levels.borrow_mut().insert((id, lint), (lvl, src)); self.node_levels.borrow_mut().insert((id, lint), lvlsrc);
} }
}
/// Check that every lint from the list of attributes satisfies `f`. fn gather_lint_attrs(&mut self, attrs: &[ast::Attribute]) -> Vec<(LintId, Level, Span)> {
/// Return true if that's the case. Otherwise return false. // Doing this as an iterator is messy due to multiple borrowing.
pub fn each_lint(sess: &session::Session, // Allocating and copying these should be quick.
attrs: &[ast::Attribute], let mut out = vec!();
f: |Gc<ast::MetaItem>, Level, InternedString| -> bool) for attr in attrs.iter() {
-> bool { let level = match Level::from_str(attr.name().get()) {
let xs = [Allow, Warn, Deny, Forbid]; None => continue,
for &level in xs.iter() { Some(lvl) => lvl,
let level_name = level_to_str(level); };
for attr in attrs.iter().filter(|m| m.check_name(level_name)) {
attr::mark_used(attr);
let meta = attr.node.value; let meta = attr.node.value;
let metas = match meta.node { let metas = match meta.node {
ast::MetaList(_, ref metas) => metas, ast::MetaList(_, ref metas) => metas,
_ => { _ => {
sess.span_err(meta.span, "malformed lint attribute"); self.tcx.sess.span_err(meta.span, "malformed lint attribute");
continue; continue;
} }
}; };
for meta in metas.iter() { for meta in metas.iter() {
match meta.node { match meta.node {
ast::MetaWord(ref lintname) => { ast::MetaWord(ref lint_name) => {
if !f(*meta, level, (*lintname).clone()) { match self.lints_by_name.find_equiv(lint_name) {
return false; Some(lint_id) => out.push((*lint_id, level, meta.span)),
None => self.span_lint(builtin::unrecognized_lint,
meta.span,
format!("unknown `{}` attribute: `{}`",
level.as_str(), lint_name).as_slice()),
} }
} }
_ => { _ => self.tcx.sess.span_err(meta.span, "malformed lint attribute"),
sess.span_err(meta.span, "malformed lint attribute");
}
} }
} }
} }
out
} }
true
}
/// Check from a list of attributes if it contains the appropriate
/// `#[level(lintname)]` attribute (e.g. `#[allow(dead_code)]).
pub fn contains_lint(attrs: &[ast::Attribute],
level: Level,
lintname: &'static str)
-> bool {
let level_name = level_to_str(level);
for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
if attr.meta_item_list().is_none() {
continue
}
let list = attr.meta_item_list().unwrap();
for meta_item in list.iter() {
if meta_item.name().equiv(&lintname) {
return true;
}
}
}
false
} }
impl<'a> AstConv for Context<'a>{ impl<'a> AstConv for Context<'a>{
@ -912,59 +622,90 @@ impl<'a> Visitor<()> for Context<'a> {
} }
} }
// Output any lints that were previously added to the session.
impl<'a> IdVisitingOperation for Context<'a> { impl<'a> IdVisitingOperation for Context<'a> {
fn visit_id(&self, id: ast::NodeId) { fn visit_id(&self, id: ast::NodeId) {
match self.tcx.sess.lints.borrow_mut().pop(&id) { match self.tcx.sess.lints.borrow_mut().pop(&id) {
None => {} None => {}
Some(l) => { Some(lints) => {
for (lint, span, msg) in l.move_iter() { for (lint_id, span, msg) in lints.move_iter() {
self.span_lint(lint, span, msg.as_slice()) self.span_lint(lint_id.lint, span, msg.as_slice())
} }
} }
} }
} }
} }
pub fn check_crate(tcx: &ty::ctxt, fn builtin_lints() -> Vec<Box<LintPass>> {
exported_items: &ExportedItems,
krate: &ast::Crate) {
macro_rules! builtin_lints (( $($name:ident),*, ) => ( macro_rules! builtin_lints (( $($name:ident),*, ) => (
vec!($( vec!($(
{ {
let obj: builtin::$name = Default::default(); let obj: builtin::$name = Default::default();
RefCell::new(box obj as LintPassObject) box obj as LintPassObject
} }
),*) ),*)
)) ))
let builtin_lints = builtin_lints!( builtin_lints!(
GatherNodeLevels, WhileTrue, UnusedCasts, TypeLimits, CTypes, WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory,
HeapMemory, RawPointerDeriving, UnusedAttribute, RawPointerDeriving, UnusedAttribute, PathStatement,
PathStatement, UnusedMustUse, DeprecatedOwnedVector, UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes,
NonCamelCaseTypes, NonSnakeCaseFunctions, NonUppercaseStatics, NonSnakeCaseFunctions, NonUppercaseStatics,
UppercaseVariables, UnnecessaryParens, UnusedUnsafe, UnsafeBlock, NonUppercasePatternStatics, UppercaseVariables,
UnusedMut, UnnecessaryAllocation, MissingDoc, Stability, UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut,
); UnnecessaryAllocation, MissingDoc, Stability,
GatherNodeLevels, HardwiredLints,
)
}
/// Get specs for all builtin lints. Used for `-W help`.
pub fn builtin_lint_specs() -> Vec<&'static Lint> {
builtin_lints().move_iter()
.flat_map(|x| x.get_lints().iter().map(|&y| y))
.collect()
}
pub fn check_crate(tcx: &ty::ctxt,
exported_items: &ExportedItems,
krate: &ast::Crate) {
let lints = builtin_lints().move_iter().map(|x| RefCell::new(x)).collect();
let mut cx = Context { let mut cx = Context {
dict: get_lint_dict(), lint_objects: lints,
cur: SmallIntMap::new(), lints_by_name: HashMap::new(),
levels: HashMap::new(),
tcx: tcx, tcx: tcx,
level_stack: Vec::new(), level_stack: Vec::new(),
node_levels: RefCell::new(HashMap::new()), node_levels: RefCell::new(HashMap::new()),
lints: builtin_lints,
}; };
// Install default lint levels, followed by the command line levels, and // Index the lints by name, and set the default levels.
// then actually visit the whole crate. for obj in cx.lint_objects.iter() {
for (_, spec) in cx.dict.iter() { for &lint in obj.borrow_mut().get_lints().iter() {
if spec.default != Allow { let id = LintId::of(lint);
cx.cur.insert(spec.lint as uint, (spec.default, Default)); if !cx.lints_by_name.insert(lint.name, id) {
cx.tcx.sess.err(format!("duplicate specification of lint {}",
lint.name).as_slice());
}
if lint.default_level != Allow {
cx.levels.insert(id, (lint.default_level, Default));
}
} }
} }
for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
cx.set_level(lint, level, CommandLine); // Set command line lint levels.
for &(ref lint_name, level) in tcx.sess.opts.lint_opts.iter() {
match cx.lints_by_name.find_equiv(&lint_name.as_slice()) {
Some(&lint_id) => cx.set_level(lint_id, (level, CommandLine)),
None => cx.tcx.sess.err(format!("unknown {} flag: {}",
level.as_str(), lint_name).as_slice()),
}
} }
tcx.sess.abort_if_errors();
// Visit the whole crate.
cx.with_lint_attrs(krate.attrs.as_slice(), |cx| { cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
cx.visit_id(ast::CRATE_NODE_ID); cx.visit_id(ast::CRATE_NODE_ID);
cx.visit_ids(|v| { cx.visit_ids(|v| {
@ -983,8 +724,10 @@ pub fn check_crate(tcx: &ty::ctxt,
// in the iteration code. // in the iteration code.
for (id, v) in tcx.sess.lints.borrow().iter() { for (id, v) in tcx.sess.lints.borrow().iter() {
for &(lint, span, ref msg) in v.iter() { for &(lint, span, ref msg) in v.iter() {
tcx.sess.span_bug(span, format!("unprocessed lint {} at {}: {}", tcx.sess.span_bug(span,
lint, tcx.map.node_to_str(*id), *msg).as_slice()) format!("unprocessed lint {} at {}: {}",
lint.as_str(), tcx.map.node_to_str(*id), *msg)
.as_slice())
} }
} }

View file

@ -13,7 +13,7 @@
// from live codes are live, and everything else is dead. // from live codes are live, and everything else is dead.
use middle::def; use middle::def;
use lint::{Allow, contains_lint, DeadCode}; use lint;
use middle::privacy; use middle::privacy;
use middle::ty; use middle::ty;
use middle::typeck; use middle::typeck;
@ -23,14 +23,13 @@ use std::collections::HashSet;
use syntax::ast; use syntax::ast;
use syntax::ast_map; use syntax::ast_map;
use syntax::ast_util::{local_def, is_local}; use syntax::ast_util::{local_def, is_local};
use syntax::attr::AttrMetaMethods;
use syntax::attr; use syntax::attr;
use syntax::codemap; use syntax::codemap;
use syntax::parse::token; use syntax::parse::token;
use syntax::visit::Visitor; use syntax::visit::Visitor;
use syntax::visit; use syntax::visit;
pub static DEAD_CODE_LINT_STR: &'static str = "dead_code";
// Any local node that may call something in its body block should be // Any local node that may call something in its body block should be
// explored. For example, if it's a live NodeItem that is a // explored. For example, if it's a live NodeItem that is a
// function, then we should explore its block to check for codes that // function, then we should explore its block to check for codes that
@ -266,8 +265,24 @@ impl<'a> Visitor<MarkSymbolVisitorContext> for MarkSymbolVisitor<'a> {
} }
fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool { fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
contains_lint(attrs, Allow, DEAD_CODE_LINT_STR) if attr::contains_name(attrs.as_slice(), "lang") {
|| attr::contains_name(attrs.as_slice(), "lang") return true;
}
// FIXME: use the lint attr parsing already in rustc::lint
for attr in attrs.iter().filter(|a| a.check_name("allow")) {
match attr.node.value.node {
ast::MetaList(_, ref metas) => for meta in metas.iter() {
match meta.node {
ast::MetaWord(ref name) if name.get() == "dead_code"
=> return true,
_ => (),
}
},
_ => (),
}
}
false
} }
// This visitor seeds items that // This visitor seeds items that
@ -446,7 +461,7 @@ impl<'a> DeadVisitor<'a> {
ident: ast::Ident) { ident: ast::Ident) {
self.tcx self.tcx
.sess .sess
.add_lint(DeadCode, .add_lint(lint::builtin::dead_code,
id, id,
span, span,
format!("code is never used: `{}`", format!("code is never used: `{}`",

View file

@ -107,7 +107,7 @@ use middle::freevars;
use middle::mem_categorization::Typer; use middle::mem_categorization::Typer;
use middle::pat_util; use middle::pat_util;
use middle::ty; use middle::ty;
use lint::{UnusedVariable, DeadAssignment}; use lint;
use util::nodemap::NodeMap; use util::nodemap::NodeMap;
use std::fmt; use std::fmt;
@ -1560,11 +1560,11 @@ impl<'a> Liveness<'a> {
}; };
if is_assigned { if is_assigned {
self.ir.tcx.sess.add_lint(UnusedVariable, id, sp, self.ir.tcx.sess.add_lint(lint::builtin::unused_variable, id, sp,
format!("variable `{}` is assigned to, but never used", format!("variable `{}` is assigned to, but never used",
*name)); *name));
} else { } else {
self.ir.tcx.sess.add_lint(UnusedVariable, id, sp, self.ir.tcx.sess.add_lint(lint::builtin::unused_variable, id, sp,
format!("unused variable: `{}`", *name)); format!("unused variable: `{}`", *name));
} }
} }
@ -1582,7 +1582,7 @@ impl<'a> Liveness<'a> {
if self.live_on_exit(ln, var).is_none() { if self.live_on_exit(ln, var).is_none() {
let r = self.should_warn(var); let r = self.should_warn(var);
for name in r.iter() { for name in r.iter() {
self.ir.tcx.sess.add_lint(DeadAssignment, id, sp, self.ir.tcx.sess.add_lint(lint::builtin::dead_assignment, id, sp,
format!("value assigned to `{}` is never read", *name)); format!("value assigned to `{}` is never read", *name));
} }
} }

View file

@ -1394,7 +1394,7 @@ impl<'a> Visitor<()> for VisiblePrivateTypesVisitor<'a> {
ast::TyPath(ref p, _, path_id) => { ast::TyPath(ref p, _, path_id) => {
if self.path_is_private_type(path_id) { if self.path_is_private_type(path_id) {
self.tcx.sess.add_lint( self.tcx.sess.add_lint(
lint::VisiblePrivateTypes, lint::builtin::visible_private_types,
path_id, p.span, path_id, p.span,
"private type in exported type \ "private type in exported type \
signature".to_string()); signature".to_string());

View file

@ -15,9 +15,9 @@ use metadata::csearch;
use metadata::decoder::{DefLike, DlDef, DlField, DlImpl}; use metadata::decoder::{DefLike, DlDef, DlField, DlImpl};
use middle::def::*; use middle::def::*;
use middle::lang_items::LanguageItems; use middle::lang_items::LanguageItems;
use lint::{UnnecessaryQualification, UnusedImports};
use middle::pat_util::pat_bindings; use middle::pat_util::pat_bindings;
use middle::subst::{ParamSpace, FnSpace, TypeSpace}; use middle::subst::{ParamSpace, FnSpace, TypeSpace};
use lint;
use util::nodemap::{NodeMap, DefIdSet, FnvHashMap}; use util::nodemap::{NodeMap, DefIdSet, FnvHashMap};
use syntax::ast::*; use syntax::ast::*;
@ -4632,7 +4632,7 @@ impl<'a> Resolver<'a> {
match (def, unqualified_def) { match (def, unqualified_def) {
(Some((d, _)), Some((ud, _))) if d == ud => { (Some((d, _)), Some((ud, _))) if d == ud => {
self.session self.session
.add_lint(UnnecessaryQualification, .add_lint(lint::builtin::unnecessary_qualification,
id, id,
path.span, path.span,
"unnecessary qualification".to_string()); "unnecessary qualification".to_string());
@ -5487,7 +5487,7 @@ impl<'a> Resolver<'a> {
if !self.used_imports.contains(&(id, TypeNS)) && if !self.used_imports.contains(&(id, TypeNS)) &&
!self.used_imports.contains(&(id, ValueNS)) { !self.used_imports.contains(&(id, ValueNS)) {
self.session self.session
.add_lint(UnusedImports, .add_lint(lint::builtin::unused_imports,
id, id,
p.span, p.span,
"unused import".to_string()); "unused import".to_string());
@ -5511,7 +5511,7 @@ impl<'a> Resolver<'a> {
if !self.used_imports.contains(&(id, TypeNS)) && if !self.used_imports.contains(&(id, TypeNS)) &&
!self.used_imports.contains(&(id, ValueNS)) { !self.used_imports.contains(&(id, ValueNS)) {
self.session.add_lint(UnusedImports, self.session.add_lint(lint::builtin::unused_imports,
id, id,
span, span,
"unused import".to_string()); "unused import".to_string());

View file

@ -1552,48 +1552,50 @@ fn trans_enum_def(ccx: &CrateContext, enum_definition: &ast::EnumDef,
fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) { fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) {
let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully
let (lvl, src) = ccx.tcx.node_lint_levels.borrow() let levels = ccx.tcx.node_lint_levels.borrow();
.find(&(id, lint::VariantSizeDifference)) match levels.find(&(id, lint::LintId::of(lint::builtin::variant_size_difference))) {
.map_or((lint::Allow, lint::Default), |&(lvl,src)| (lvl, src)); None | Some(&(lint::Allow, _)) => (),
Some(&lvlsrc) => {
if lvl != lint::Allow { let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id));
let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id)); match *avar {
match *avar { adt::General(_, ref variants) => {
adt::General(_, ref variants) => { for var in variants.iter() {
for var in variants.iter() { let mut size = 0;
let mut size = 0; for field in var.fields.iter().skip(1) {
for field in var.fields.iter().skip(1) { // skip the discriminant
// skip the discriminant size += llsize_of_real(ccx, sizing_type_of(ccx, *field));
size += llsize_of_real(ccx, sizing_type_of(ccx, *field)); }
sizes.push(size);
} }
sizes.push(size); },
} _ => { /* its size is either constant or unimportant */ }
}, }
_ => { /* its size is either constant or unimportant */ }
}
let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0), let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0),
|(l, s, li), (idx, &size)| |(l, s, li), (idx, &size)|
if size > l { if size > l {
(size, l, idx) (size, l, idx)
} else if size > s { } else if size > s {
(l, size, li) (l, size, li)
} else { } else {
(l, s, li) (l, s, li)
} }
); );
// we only warn if the largest variant is at least thrice as large as // we only warn if the largest variant is at least thrice as large as
// the second-largest. // the second-largest.
if largest > slargest * 3 && slargest > 0 { if largest > slargest * 3 && slargest > 0 {
lint::emit_lint(lvl, src, // Use lint::emit_lint rather than sess.add_lint because the lint-printing
format!("enum variant is more than three times larger \ // pass for the latter already ran.
({} bytes) than the next largest (ignoring padding)", lint::emit_lint(&ccx.tcx().sess, lint::builtin::variant_size_difference,
largest).as_slice(), lvlsrc, sp,
sp, lint::lint_to_str(lint::VariantSizeDifference), ccx.tcx()); format!("enum variant is more than three times larger \
({} bytes) than the next largest (ignoring padding)",
largest).as_slice());
ccx.sess().span_note(enum_def.variants.get(largest_index).span, ccx.sess().span_note(enum_def.variants.get(largest_index).span,
"this variant is the largest"); "this variant is the largest");
}
} }
} }
} }

View file

@ -368,7 +368,7 @@ pub struct ctxt {
pub dependency_formats: RefCell<dependency_format::Dependencies>, pub dependency_formats: RefCell<dependency_format::Dependencies>,
pub node_lint_levels: RefCell<HashMap<(ast::NodeId, lint::LintId), pub node_lint_levels: RefCell<HashMap<(ast::NodeId, lint::LintId),
(lint::Level, lint::LintSource)>>, lint::LevelSource>>,
/// The types that must be asserted to be the same size for `transmute` /// The types that must be asserted to be the same size for `transmute`
/// to be valid. We gather up these restrictions in the intrinsicck pass /// to be valid. We gather up these restrictions in the intrinsicck pass

View file

@ -79,7 +79,6 @@ type parameter).
use middle::const_eval; use middle::const_eval;
use middle::def; use middle::def;
use lint::UnreachableCode;
use middle::pat_util::pat_id_map; use middle::pat_util::pat_id_map;
use middle::pat_util; use middle::pat_util;
use middle::subst; use middle::subst;
@ -111,6 +110,7 @@ use middle::typeck::{require_same_types, vtable_map};
use middle::typeck::{MethodCall, MethodMap}; use middle::typeck::{MethodCall, MethodMap};
use middle::typeck::{TypeAndSubsts}; use middle::typeck::{TypeAndSubsts};
use middle::lang_items::TypeIdLangItem; use middle::lang_items::TypeIdLangItem;
use lint;
use util::common::{block_query, indenter, loop_query}; use util::common::{block_query, indenter, loop_query};
use util::ppaux; use util::ppaux;
use util::ppaux::{UserString, Repr}; use util::ppaux::{UserString, Repr};
@ -3416,7 +3416,7 @@ pub fn check_block_with_expected(fcx: &FnCtxt,
fcx.ccx fcx.ccx
.tcx .tcx
.sess .sess
.add_lint(UnreachableCode, .add_lint(lint::builtin::unreachable_code,
s_id, s_id,
s.span, s.span,
"unreachable statement".to_string()); "unreachable statement".to_string());
@ -3443,7 +3443,7 @@ pub fn check_block_with_expected(fcx: &FnCtxt,
fcx.ccx fcx.ccx
.tcx .tcx
.sess .sess
.add_lint(UnreachableCode, .add_lint(lint::builtin::unreachable_code,
e.id, e.id,
e.span, e.span,
"unreachable expression".to_string()); "unreachable expression".to_string());

View file

@ -75,11 +75,13 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet<Path>, cfgs: Vec<String>)
let input = FileInput(cpath.clone()); let input = FileInput(cpath.clone());
let warning_lint = lint::builtin::warnings.name.to_string();
let sessopts = driver::config::Options { let sessopts = driver::config::Options {
maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()),
addl_lib_search_paths: RefCell::new(libs), addl_lib_search_paths: RefCell::new(libs),
crate_types: vec!(driver::config::CrateTypeRlib), crate_types: vec!(driver::config::CrateTypeRlib),
lint_opts: vec!((lint::Warnings, lint::Allow)), lint_opts: vec!((warning_lint, lint::Allow)),
..rustc::driver::config::basic_options().clone() ..rustc::driver::config::basic_options().clone()
}; };