From 395d205b82c7d8aae7e2d28df10e192c881596f1 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Sun, 1 Jun 2014 15:58:06 -0700 Subject: [PATCH 01/24] Move lint.rs out of middle We're going to have more modules under lint, and the paths get unwieldy. We also plan to have lints run at multiple points in the compilation pipeline. --- src/librustc/driver/config.rs | 2 +- src/librustc/driver/driver.rs | 3 ++- src/librustc/driver/mod.rs | 2 +- src/librustc/driver/session.rs | 2 +- src/librustc/front/feature_gate.rs | 2 +- src/librustc/lib.rs | 3 ++- src/librustc/{middle/lint.rs => lint/mod.rs} | 0 src/librustc/middle/dead.rs | 2 +- src/librustc/middle/liveness.rs | 2 +- src/librustc/middle/privacy.rs | 2 +- src/librustc/middle/resolve.rs | 2 +- src/librustc/middle/trans/base.rs | 2 +- src/librustc/middle/ty.rs | 2 +- src/librustc/middle/typeck/check/mod.rs | 2 +- src/librustdoc/core.rs | 2 +- 15 files changed, 16 insertions(+), 14 deletions(-) rename src/librustc/{middle/lint.rs => lint/mod.rs} (100%) diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index 186db839e33d8..0c8b261ac9b89 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -20,7 +20,7 @@ use back::link; use back::target_strs; use back::{arm, x86, x86_64, mips}; use metadata; -use middle::lint; +use lint; use syntax::abi; use syntax::ast; diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 45e9c7b562d0f..fc9934dea9431 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -20,12 +20,13 @@ use metadata::common::LinkMeta; use metadata::creader; use middle::cfg; use middle::cfg::graphviz::LabelledCFG; -use middle::{trans, freevars, kind, ty, typeck, lint, reachable}; +use middle::{trans, freevars, kind, ty, typeck, reachable}; use middle::dependency_format; use middle; use plugin::load::Plugins; use plugin::registry::Registry; use plugin; +use lint; use util::common::time; use util::ppaux; use util::nodemap::{NodeSet}; diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs index 59f53986af9fc..f1847f257808c 100644 --- a/src/librustc/driver/mod.rs +++ b/src/librustc/driver/mod.rs @@ -13,7 +13,7 @@ pub use syntax::diagnostic; use back::link; use driver::driver::{Input, FileInput, StrInput}; use driver::session::{Session, build_session}; -use middle::lint; +use lint; use metadata; use std::any::AnyRefExt; diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 773b9e6e0aac4..7e80a057ba2df 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -14,7 +14,7 @@ use driver::driver; use front; use metadata::cstore::CStore; use metadata::filesearch; -use middle::lint; +use lint; use util::nodemap::NodeMap; use syntax::ast::NodeId; diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index cd47232123762..f6f52a3052a19 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -18,7 +18,7 @@ //! Features are enabled in programs via the crate-level attributes of //! #![feature(...)] with a comma-separated list of features. -use middle::lint; +use lint; use syntax::ast; use syntax::attr; diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 4ac4e3a5a9ffb..ca0083bb6bb72 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -63,7 +63,6 @@ pub mod middle { pub mod check_match; pub mod check_const; pub mod check_static; - pub mod lint; pub mod borrowck; pub mod dataflow; pub mod mem_categorization; @@ -116,6 +115,8 @@ pub mod driver; pub mod plugin; +pub mod lint; + pub mod util { pub mod common; pub mod ppaux; diff --git a/src/librustc/middle/lint.rs b/src/librustc/lint/mod.rs similarity index 100% rename from src/librustc/middle/lint.rs rename to src/librustc/lint/mod.rs diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 46899ae19ca09..4f39616510da9 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -13,7 +13,7 @@ // from live codes are live, and everything else is dead. use middle::def; -use middle::lint::{Allow, contains_lint, DeadCode}; +use lint::{Allow, contains_lint, DeadCode}; use middle::privacy; use middle::ty; use middle::typeck; diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index a7fd81e9c9a83..fd1803240ab5c 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -104,10 +104,10 @@ use middle::def::*; use middle::freevars; -use middle::lint::{UnusedVariable, DeadAssignment}; use middle::mem_categorization::Typer; use middle::pat_util; use middle::ty; +use lint::{UnusedVariable, DeadAssignment}; use util::nodemap::NodeMap; use std::mem::transmute; diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 50cbf6bbaf0c5..fa9980b566baa 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -16,7 +16,7 @@ use std::mem::replace; use metadata::csearch; use middle::def; -use middle::lint; +use lint; use middle::resolve; use middle::ty; use middle::typeck::{MethodCall, MethodMap, MethodOrigin, MethodParam}; diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index b0a322a9eb7ed..9bf87da8c4e2c 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -15,7 +15,7 @@ use metadata::csearch; use metadata::decoder::{DefLike, DlDef, DlField, DlImpl}; use middle::def::*; use middle::lang_items::LanguageItems; -use middle::lint::{UnnecessaryQualification, UnusedImports}; +use lint::{UnnecessaryQualification, UnusedImports}; use middle::pat_util::pat_bindings; use util::nodemap::{NodeMap, DefIdSet, FnvHashMap}; diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index fc2e87f72bb49..7c8de832ea30b 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -36,7 +36,7 @@ use lib::llvm::{ModuleRef, ValueRef, BasicBlockRef}; use lib::llvm::{llvm, Vector}; use lib; use metadata::{csearch, encoder}; -use middle::lint; +use lint; use middle::astencode; use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; use middle::weak_lang_items; diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 6275abdc8ab20..3b11a7a97b86d 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -14,7 +14,7 @@ use back::svh::Svh; use driver::session::Session; use metadata::csearch; use mc = middle::mem_categorization; -use middle::lint; +use lint; use middle::const_eval; use middle::def; use middle::dependency_format; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index be19c2ef19953..0889020bfd999 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -81,7 +81,7 @@ use middle::const_eval; use middle::def; use middle::lang_items::{ExchangeHeapLangItem, GcLangItem}; use middle::lang_items::{ManagedHeapLangItem}; -use middle::lint::UnreachableCode; +use lint::UnreachableCode; use middle::pat_util::pat_id_map; use middle::pat_util; use middle::subst; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index f848c5224b7c8..c07a902d06700 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -11,7 +11,7 @@ use rustc; use rustc::{driver, middle}; use rustc::middle::privacy; -use rustc::middle::lint; +use rustc::lint; use syntax::ast; use syntax::parse::token; From 8a50032cbdc5c7485369b5f92f8180b3d96dfb52 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Sun, 1 Jun 2014 16:16:00 -0700 Subject: [PATCH 02/24] Move lint infrastructure and individual lints into separate files --- src/librustc/lint/builtin.rs | 1081 +++++++++++++++++++++++++++++++ src/librustc/lint/mod.rs | 1169 ++-------------------------------- 2 files changed, 1143 insertions(+), 1107 deletions(-) create mode 100644 src/librustc/lint/builtin.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs new file mode 100644 index 0000000000000..3065645db2798 --- /dev/null +++ b/src/librustc/lint/builtin.rs @@ -0,0 +1,1081 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Lints built in to rustc. + +use metadata::csearch; +use middle::def; +use middle::def::*; +use middle::pat_util; +use middle::trans::adt; // for `adt::is_ffi_safe` +use middle::ty; +use middle::typeck::astconv::{ast_ty_to_ty, AstConv}; +use middle::typeck::infer; +use middle::typeck; +use util::ppaux::{ty_to_str}; +use lint::Context; +use lint; + +use std::cmp; +use std::collections::HashMap; +use std::i16; +use std::i32; +use std::i64; +use std::i8; +use std::u16; +use std::u32; +use std::u64; +use std::u8; +use syntax::abi; +use syntax::ast_map; +use syntax::attr::AttrMetaMethods; +use syntax::attr; +use syntax::codemap::Span; +use syntax::parse::token; +use syntax::visit::Visitor; +use syntax::{ast, ast_util, visit}; + +pub fn check_while_true_expr(cx: &Context, e: &ast::Expr) { + match e.node { + ast::ExprWhile(cond, _) => { + match cond.node { + ast::ExprLit(lit) => { + match lit.node { + ast::LitBool(true) => { + cx.span_lint(lint::WhileTrue, + e.span, + "denote infinite loops with loop \ + { ... }"); + } + _ => {} + } + } + _ => () + } + } + _ => () + } +} + +pub fn check_unused_casts(cx: &Context, e: &ast::Expr) { + return match e.node { + ast::ExprCast(expr, 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 { + cx.span_lint(lint::UnnecessaryTypecast, ty.span, + "unnecessary type cast"); + } + } + _ => () + }; +} + +pub fn check_type_limits(cx: &Context, e: &ast::Expr) { + return match e.node { + ast::ExprUnary(ast::UnNeg, ex) => { + match ex.node { + ast::ExprLit(lit) => { + match lit.node { + ast::LitUint(..) => { + cx.span_lint(lint::UnsignedNegate, e.span, + "negation of unsigned int literal may be unintentional"); + }, + _ => () + } + }, + _ => { + let t = ty::expr_ty(cx.tcx, ex); + match ty::get(t).sty { + ty::ty_uint(_) => { + cx.span_lint(lint::UnsignedNegate, e.span, + "negation of unsigned int variable may be unintentional"); + }, + _ => () + } + } + } + }, + ast::ExprBinary(binop, l, r) => { + if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) { + cx.span_lint(lint::TypeLimits, e.span, + "comparison is useless due to type limits"); + } + }, + ast::ExprLit(lit) => { + match ty::get(ty::expr_ty(cx.tcx, e)).sty { + ty::ty_int(t) => { + let int_type = if t == ast::TyI { + cx.tcx.sess.targ_cfg.int_type + } else { t }; + let (min, max) = int_ty_range(int_type); + let mut lit_val: i64 = match lit.node { + ast::LitInt(v, _) => v, + ast::LitUint(v, _) => v as i64, + ast::LitIntUnsuffixed(v) => v, + _ => fail!() + }; + if cx.negated_expr_id == e.id { + lit_val *= -1; + } + if lit_val < min || lit_val > max { + cx.span_lint(lint::TypeOverflow, e.span, + "literal out of range for its type"); + } + }, + ty::ty_uint(t) => { + let uint_type = if t == ast::TyU { + cx.tcx.sess.targ_cfg.uint_type + } else { t }; + let (min, max) = uint_ty_range(uint_type); + let lit_val: u64 = match lit.node { + ast::LitInt(v, _) => v as u64, + ast::LitUint(v, _) => v, + ast::LitIntUnsuffixed(v) => v as u64, + _ => fail!() + }; + if lit_val < min || lit_val > max { + cx.span_lint(lint::TypeOverflow, e.span, + "literal out of range for its type"); + } + }, + + _ => () + }; + }, + _ => () + }; + + fn is_valid(binop: ast::BinOp, v: T, + min: T, max: T) -> bool { + match binop { + ast::BiLt => v > min && v <= max, + ast::BiLe => v >= min && v < max, + ast::BiGt => v >= min && v < max, + ast::BiGe => v > min && v <= max, + ast::BiEq | ast::BiNe => v >= min && v <= max, + _ => fail!() + } + } + + fn rev_binop(binop: ast::BinOp) -> ast::BinOp { + match binop { + ast::BiLt => ast::BiGt, + ast::BiLe => ast::BiGe, + ast::BiGt => ast::BiLt, + ast::BiGe => ast::BiLe, + _ => binop + } + } + + // for int & uint, be conservative with the warnings, so that the + // warnings are consistent between 32- and 64-bit platforms + fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) { + match int_ty { + ast::TyI => (i64::MIN, i64::MAX), + ast::TyI8 => (i8::MIN as i64, i8::MAX as i64), + ast::TyI16 => (i16::MIN as i64, i16::MAX as i64), + ast::TyI32 => (i32::MIN as i64, i32::MAX as i64), + ast::TyI64 => (i64::MIN, i64::MAX) + } + } + + fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) { + match uint_ty { + ast::TyU => (u64::MIN, u64::MAX), + ast::TyU8 => (u8::MIN as u64, u8::MAX as u64), + ast::TyU16 => (u16::MIN as u64, u16::MAX as u64), + ast::TyU32 => (u32::MIN as u64, u32::MAX as u64), + ast::TyU64 => (u64::MIN, u64::MAX) + } + } + + fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp, + l: &ast::Expr, r: &ast::Expr) -> bool { + let (lit, expr, swap) = match (&l.node, &r.node) { + (&ast::ExprLit(_), _) => (l, r, true), + (_, &ast::ExprLit(_)) => (r, l, false), + _ => return true + }; + // Normalize the binop so that the literal is always on the RHS in + // the comparison + let norm_binop = if swap { rev_binop(binop) } else { binop }; + match ty::get(ty::expr_ty(tcx, expr)).sty { + ty::ty_int(int_ty) => { + let (min, max) = int_ty_range(int_ty); + let lit_val: i64 = match lit.node { + ast::ExprLit(li) => match li.node { + ast::LitInt(v, _) => v, + ast::LitUint(v, _) => v as i64, + ast::LitIntUnsuffixed(v) => v, + _ => return true + }, + _ => fail!() + }; + is_valid(norm_binop, lit_val, min, max) + } + ty::ty_uint(uint_ty) => { + let (min, max): (u64, u64) = uint_ty_range(uint_ty); + let lit_val: u64 = match lit.node { + ast::ExprLit(li) => match li.node { + ast::LitInt(v, _) => v as u64, + ast::LitUint(v, _) => v, + ast::LitIntUnsuffixed(v) => v as u64, + _ => return true + }, + _ => fail!() + }; + is_valid(norm_binop, lit_val, min, max) + } + _ => true + } + } + + fn is_comparison(binop: ast::BinOp) -> bool { + match binop { + ast::BiEq | ast::BiLt | ast::BiLe | + ast::BiNe | ast::BiGe | ast::BiGt => true, + _ => false + } + } +} + +pub fn check_item_ctypes(cx: &Context, it: &ast::Item) { + fn check_ty(cx: &Context, ty: &ast::Ty) { + match ty.node { + ast::TyPath(_, _, id) => { + match cx.tcx.def_map.borrow().get_copy(&id) { + def::DefPrimTy(ast::TyInt(ast::TyI)) => { + cx.span_lint(lint::CTypes, ty.span, + "found rust type `int` in foreign module, while \ + libc::c_int or libc::c_long should be used"); + } + def::DefPrimTy(ast::TyUint(ast::TyU)) => { + cx.span_lint(lint::CTypes, ty.span, + "found rust type `uint` in foreign module, while \ + libc::c_uint or libc::c_ulong should be used"); + } + def::DefTy(def_id) => { + if !adt::is_ffi_safe(cx.tcx, def_id) { + cx.span_lint(lint::CTypes, ty.span, + "found enum type without foreign-function-safe \ + representation annotation in foreign module"); + // hmm... this message could be more helpful + } + } + _ => () + } + } + ast::TyPtr(ref mt) => { check_ty(cx, mt.ty) } + _ => {} + } + } + + fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) { + for input in decl.inputs.iter() { + check_ty(cx, input.ty); + } + check_ty(cx, decl.output) + } + + match it.node { + ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => { + for ni in nmod.items.iter() { + match ni.node { + ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl), + ast::ForeignItemStatic(t, _) => check_ty(cx, t) + } + } + } + _ => {/* nothing to do */ } + } +} + +pub fn check_heap_type(cx: &Context, span: Span, ty: ty::t) { + let xs = [lint::ManagedHeapMemory, lint::OwnedHeapMemory, lint::HeapMemory]; + for &lint in xs.iter() { + if cx.get_level(lint) == lint::Allow { continue } + + 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(_) | + ty::ty_trait(box ty::TyTrait { + store: ty::UniqTraitStore, .. + }) | + ty::ty_closure(box ty::ClosureTy { + store: ty::UniqTraitStore, + .. + }) => { + n_uniq += 1; + } + + _ => () + }; + t + }); + + if n_uniq > 0 && lint != lint::ManagedHeapMemory { + let s = ty_to_str(cx.tcx, ty); + let m = format!("type uses owned (Box type) pointers: {}", s); + cx.span_lint(lint, 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()); + } + } +} + +pub fn check_heap_item(cx: &Context, it: &ast::Item) { + match it.node { + ast::ItemFn(..) | + ast::ItemTy(..) | + ast::ItemEnum(..) | + ast::ItemStruct(..) => check_heap_type(cx, it.span, + ty::node_id_to_type(cx.tcx, + it.id)), + _ => () + } + + // If it's a struct, we also have to check the fields' types + match it.node { + ast::ItemStruct(struct_def, _) => { + for struct_field in struct_def.fields.iter() { + check_heap_type(cx, struct_field.span, + ty::node_id_to_type(cx.tcx, + struct_field.node.id)); + } + } + _ => () + } +} + +struct RawPtrDerivingVisitor<'a> { + cx: &'a Context<'a> +} + +impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> { + fn visit_ty(&mut self, ty: &ast::Ty, _: ()) { + static MSG: &'static str = "use of `#[deriving]` with a raw pointer"; + match ty.node { + ast::TyPtr(..) => self.cx.span_lint(lint::RawPointerDeriving, ty.span, MSG), + _ => {} + } + visit::walk_ty(self, ty, ()); + } + // explicit override to a no-op to reduce code bloat + fn visit_expr(&mut self, _: &ast::Expr, _: ()) {} + fn visit_block(&mut self, _: &ast::Block, _: ()) {} +} + +pub fn check_raw_ptr_deriving(cx: &mut Context, item: &ast::Item) { + if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") { + return + } + let did = match item.node { + ast::ItemImpl(..) => { + match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty { + ty::ty_enum(did, _) => did, + ty::ty_struct(did, _) => did, + _ => return, + } + } + _ => return, + }; + if !ast_util::is_local(did) { return } + let item = match cx.tcx.map.find(did.node) { + Some(ast_map::NodeItem(item)) => item, + _ => return, + }; + if !cx.checked_raw_pointers.insert(item.id) { return } + match item.node { + ast::ItemStruct(..) | ast::ItemEnum(..) => { + let mut visitor = RawPtrDerivingVisitor { cx: cx }; + visit::walk_item(&mut visitor, item, ()); + } + _ => {} + } +} + +pub fn check_unused_attribute(cx: &Context, attr: &ast::Attribute) { + static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [ + // FIXME: #14408 whitelist docs since rustdoc looks at them + "doc", + + // FIXME: #14406 these are processed in trans, which happens after the + // lint pass + "address_insignificant", + "cold", + "inline", + "link", + "link_name", + "link_section", + "no_builtins", + "no_mangle", + "no_split_stack", + "packed", + "static_assert", + "thread_local", + + // not used anywhere (!?) but apparently we want to keep them around + "comment", + "desc", + "license", + + // FIXME: #14407 these are only looked at on-demand so we can't + // guarantee they'll have already been checked + "deprecated", + "experimental", + "frozen", + "locked", + "must_use", + "stable", + "unstable", + ]; + + static CRATE_ATTRS: &'static [&'static str] = &'static [ + "crate_type", + "feature", + "no_start", + "no_main", + "no_std", + "crate_id", + "desc", + "comment", + "license", + "copyright", + "no_builtins", + ]; + + for &name in ATTRIBUTE_WHITELIST.iter() { + if attr.check_name(name) { + break; + } + } + + if !attr::is_used(attr) { + cx.span_lint(lint::UnusedAttribute, attr.span, "unused attribute"); + if CRATE_ATTRS.contains(&attr.name().get()) { + let msg = match attr.node.style { + ast::AttrOuter => "crate-level attribute should be an inner \ + attribute: add an exclamation mark: #![foo]", + ast::AttrInner => "crate-level attribute should be in the \ + root module", + }; + cx.span_lint(lint::UnusedAttribute, attr.span, msg); + } + } +} + +pub fn check_heap_expr(cx: &Context, e: &ast::Expr) { + let ty = ty::expr_ty(cx.tcx, e); + check_heap_type(cx, e.span, ty); +} + +pub fn check_path_statement(cx: &Context, s: &ast::Stmt) { + match s.node { + ast::StmtSemi(expr, _) => { + match expr.node { + ast::ExprPath(_) => { + cx.span_lint(lint::PathStatement, + s.span, + "path statement with no effect"); + } + _ => {} + } + } + _ => () + } +} + +pub fn check_unused_result(cx: &Context, s: &ast::Stmt) { + let expr = match s.node { + ast::StmtSemi(expr, _) => expr, + _ => return + }; + let t = ty::expr_ty(cx.tcx, expr); + match ty::get(t).sty { + ty::ty_nil | ty::ty_bot | ty::ty_bool => return, + _ => {} + } + match expr.node { + ast::ExprRet(..) => return, + _ => {} + } + + let t = ty::expr_ty(cx.tcx, expr); + let mut warned = false; + match ty::get(t).sty { + ty::ty_struct(did, _) | + ty::ty_enum(did, _) => { + if ast_util::is_local(did) { + match cx.tcx.map.get(did.node) { + ast_map::NodeItem(it) => { + if attr::contains_name(it.attrs.as_slice(), + "must_use") { + cx.span_lint(lint::UnusedMustUse, s.span, + "unused result which must be used"); + warned = true; + } + } + _ => {} + } + } else { + csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| { + if attr::contains_name(attrs.as_slice(), "must_use") { + cx.span_lint(lint::UnusedMustUse, s.span, + "unused result which must be used"); + warned = true; + } + }); + } + } + _ => {} + } + if !warned { + cx.span_lint(lint::UnusedResult, s.span, "unused result"); + } +} + +pub fn check_deprecated_owned_vector(cx: &Context, e: &ast::Expr) { + let t = ty::expr_ty(cx.tcx, e); + match ty::get(t).sty { + ty::ty_uniq(t) => match ty::get(t).sty { + ty::ty_vec(_, None) => { + cx.span_lint(lint::DeprecatedOwnedVector, e.span, + "use of deprecated `~[]` vector; replaced by `std::vec::Vec`") + } + _ => {} + }, + _ => {} + } +} + +pub fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) { + fn is_camel_case(ident: ast::Ident) -> bool { + let ident = token::get_ident(ident); + assert!(!ident.get().is_empty()); + let ident = ident.get().trim_chars('_'); + + // start with a non-lowercase letter rather than non-uppercase + // ones (some scripts don't have a concept of upper/lowercase) + !ident.char_at(0).is_lowercase() && !ident.contains_char('_') + } + + fn to_camel_case(s: &str) -> String { + s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)| + if i == 0 { c.to_uppercase() } + else { c } + )).collect() + } + + fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) { + let s = token::get_ident(ident); + + if !is_camel_case(ident) { + cx.span_lint(lint:: + NonCamelCaseTypes, span, + format!("{} `{}` should have a camel case name such as `{}`", + sort, s, to_camel_case(s.get())).as_slice()); + } + } + + match it.node { + ast::ItemTy(..) | ast::ItemStruct(..) => { + check_case(cx, "type", it.ident, it.span) + } + ast::ItemTrait(..) => { + check_case(cx, "trait", it.ident, it.span) + } + ast::ItemEnum(ref enum_definition, _) => { + check_case(cx, "type", it.ident, it.span); + for variant in enum_definition.variants.iter() { + check_case(cx, "variant", variant.node.name, variant.span); + } + } + _ => () + } +} + +pub fn check_snake_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) { + fn is_snake_case(ident: ast::Ident) -> bool { + let ident = token::get_ident(ident); + assert!(!ident.get().is_empty()); + let ident = ident.get().trim_chars('_'); + + let mut allow_underscore = true; + ident.chars().all(|c| { + allow_underscore = match c { + c if c.is_lowercase() || c.is_digit() => true, + '_' if allow_underscore => false, + _ => return false, + }; + true + }) + } + + fn to_snake_case(str: &str) -> String { + let mut words = vec![]; + for s in str.split('_') { + let mut buf = String::new(); + if s.is_empty() { continue; } + for ch in s.chars() { + if !buf.is_empty() && ch.is_uppercase() { + words.push(buf); + buf = String::new(); + } + buf.push_char(ch.to_lowercase()); + } + words.push(buf); + } + words.connect("_") + } + + let s = token::get_ident(ident); + + if !is_snake_case(ident) { + cx.span_lint(lint::NonSnakeCaseFunctions, span, + format!("{} `{}` should have a snake case name such as `{}`", + sort, s, to_snake_case(s.get())).as_slice()); + } +} + +pub fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) { + match it.node { + // only check static constants + ast::ItemStatic(_, ast::MutImmutable, _) => { + let s = token::get_ident(it.ident); + // check for lowercase letters rather than non-uppercase + // ones (some scripts don't have a concept of + // upper/lowercase) + if s.get().chars().any(|c| c.is_lowercase()) { + cx.span_lint(lint::NonUppercaseStatics, it.span, + format!("static constant `{}` should have an uppercase name \ + such as `{}`", s.get(), + s.get().chars().map(|c| c.to_uppercase()) + .collect::().as_slice()).as_slice()); + } + } + _ => {} + } +} + +pub fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) { + // Lint for constants that look like binding identifiers (#7526) + match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) { + (&ast::PatIdent(_, ref path, _), Some(&def::DefStatic(_, false))) => { + // last identifier alone is right choice for this lint. + let ident = path.segments.last().unwrap().identifier; + let s = token::get_ident(ident); + if s.get().chars().any(|c| c.is_lowercase()) { + cx.span_lint(lint::NonUppercasePatternStatics, path.span, + format!("static constant in pattern `{}` should have an uppercase \ + name such as `{}`", s.get(), + s.get().chars().map(|c| c.to_uppercase()) + .collect::().as_slice()).as_slice()); + } + } + _ => {} + } +} + +pub fn check_pat_uppercase_variable(cx: &Context, p: &ast::Pat) { + match &p.node { + &ast::PatIdent(_, ref path, _) => { + match cx.tcx.def_map.borrow().find(&p.id) { + Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) | + Some(&def::DefArg(_, _)) => { + // last identifier alone is right choice for this lint. + let ident = path.segments.last().unwrap().identifier; + let s = token::get_ident(ident); + if s.get().len() > 0 && s.get().char_at(0).is_uppercase() { + cx.span_lint(lint:: + UppercaseVariables, + path.span, + "variable names should start with a lowercase character"); + } + } + _ => {} + } + } + _ => {} + } +} + +pub fn check_struct_uppercase_variable(cx: &Context, s: &ast::StructDef) { + for sf in s.fields.iter() { + match sf.node { + ast::StructField_ { kind: ast::NamedField(ident, _), .. } => { + let s = token::get_ident(ident); + if s.get().char_at(0).is_uppercase() { + cx.span_lint(lint:: + UppercaseVariables, + sf.span, + "structure field names should start with a lowercase character"); + } + } + _ => {} + } + } +} + +pub fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) { + match value.node { + ast::ExprParen(_) => { + cx.span_lint(lint::UnnecessaryParens, value.span, + format!("unnecessary parentheses around {}", + msg).as_slice()) + } + _ => {} + } +} + +pub fn check_unnecessary_parens_expr(cx: &Context, e: &ast::Expr) { + let (value, msg) = match e.node { + ast::ExprIf(cond, _, _) => (cond, "`if` condition"), + ast::ExprWhile(cond, _) => (cond, "`while` condition"), + ast::ExprMatch(head, _) => (head, "`match` head expression"), + ast::ExprRet(Some(value)) => (value, "`return` value"), + ast::ExprAssign(_, value) => (value, "assigned value"), + ast::ExprAssignOp(_, _, value) => (value, "assigned value"), + _ => return + }; + check_unnecessary_parens_core(cx, value, msg); +} + +pub fn check_unnecessary_parens_stmt(cx: &Context, s: &ast::Stmt) { + let (value, msg) = match s.node { + ast::StmtDecl(decl, _) => match decl.node { + ast::DeclLocal(local) => match local.init { + Some(value) => (value, "assigned value"), + None => return + }, + _ => return + }, + _ => return + }; + check_unnecessary_parens_core(cx, value, msg); +} + +pub fn check_unused_unsafe(cx: &Context, e: &ast::Expr) { + match e.node { + // Don't warn about generated blocks, that'll just pollute the output. + ast::ExprBlock(ref blk) => { + if blk.rules == ast::UnsafeBlock(ast::UserProvided) && + !cx.tcx.used_unsafe.borrow().contains(&blk.id) { + cx.span_lint(lint::UnusedUnsafe, blk.span, + "unnecessary `unsafe` block"); + } + } + _ => () + } +} + +pub fn check_unsafe_block(cx: &Context, e: &ast::Expr) { + match e.node { + // Don't warn about generated blocks, that'll just pollute the output. + ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => { + cx.span_lint(lint::UnsafeBlock, blk.span, "usage of an `unsafe` block"); + } + _ => () + } +} + +pub fn check_unused_mut_pat(cx: &Context, pats: &[@ast::Pat]) { + // collect all mutable pattern and group their NodeIDs by their Identifier to + // avoid false warnings in match arms with multiple patterns + let mut mutables = HashMap::new(); + for &p in pats.iter() { + pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path| { + match mode { + ast::BindByValue(ast::MutMutable) => { + if path.segments.len() != 1 { + cx.tcx.sess.span_bug(p.span, + "mutable binding that doesn't consist \ + of exactly one segment"); + } + let ident = path.segments.get(0).identifier; + if !token::get_ident(ident).get().starts_with("_") { + mutables.insert_or_update_with(ident.name as uint, vec!(id), |_, old| { + old.push(id); + }); + } + } + _ => { + } + } + }); + } + + let used_mutables = cx.tcx.used_mut_nodes.borrow(); + for (_, v) in mutables.iter() { + if !v.iter().any(|e| used_mutables.contains(e)) { + cx.span_lint(lint::UnusedMut, cx.tcx.map.span(*v.get(0)), + "variable does not need to be mutable"); + } + } +} + +enum Allocation { + VectorAllocation, + BoxAllocation +} + +pub fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) { + // Warn if string and vector literals with sigils, or boxing expressions, + // are immediately borrowed. + let allocation = match e.node { + ast::ExprVstore(e2, ast::ExprVstoreUniq) => { + match e2.node { + ast::ExprLit(lit) if ast_util::lit_is_str(lit) => { + VectorAllocation + } + ast::ExprVec(..) => VectorAllocation, + _ => return + } + } + ast::ExprUnary(ast::UnUniq, _) | + ast::ExprUnary(ast::UnBox, _) => BoxAllocation, + + _ => return + }; + + let report = |msg| { + cx.span_lint(lint::UnnecessaryAllocation, e.span, msg); + }; + + match cx.tcx.adjustments.borrow().find(&e.id) { + Some(adjustment) => { + match *adjustment { + ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { + match (allocation, autoref) { + (VectorAllocation, Some(ty::AutoBorrowVec(..))) => { + report("unnecessary allocation, the sigil can be \ + removed"); + } + (BoxAllocation, + Some(ty::AutoPtr(_, ast::MutImmutable))) => { + report("unnecessary allocation, use & instead"); + } + (BoxAllocation, + Some(ty::AutoPtr(_, ast::MutMutable))) => { + report("unnecessary allocation, use &mut \ + instead"); + } + _ => () + } + } + _ => {} + } + } + + _ => () + } +} + +pub fn check_missing_doc_attrs(cx: &Context, + id: Option, + attrs: &[ast::Attribute], + sp: Span, + desc: &'static str) { + // If we're building a test harness, then warning about + // documentation is probably not really relevant right now. + if cx.tcx.sess.opts.test { return } + + // `#[doc(hidden)]` disables missing_doc check. + if cx.is_doc_hidden { return } + + // Only check publicly-visible items, using the result from the privacy pass. It's an option so + // the crate root can also use this function (it doesn't have a NodeId). + match id { + Some(ref id) if !cx.exported_items.contains(id) => return, + _ => () + } + + let has_doc = attrs.iter().any(|a| { + match a.node.value.node { + ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true, + _ => false + } + }); + if !has_doc { + cx.span_lint(lint::MissingDoc, + sp, + format!("missing documentation for {}", + desc).as_slice()); + } +} + +pub fn check_missing_doc_item(cx: &Context, it: &ast::Item) { + let desc = match it.node { + ast::ItemFn(..) => "a function", + ast::ItemMod(..) => "a module", + ast::ItemEnum(..) => "an enum", + ast::ItemStruct(..) => "a struct", + ast::ItemTrait(..) => "a trait", + _ => return + }; + check_missing_doc_attrs(cx, + Some(it.id), + it.attrs.as_slice(), + it.span, + desc); +} + +pub fn check_missing_doc_method(cx: &Context, m: &ast::Method) { + // If the method is an impl for a trait, don't doc. + if lint::method_context(cx, m) == lint::TraitImpl { return; } + + // Otherwise, doc according to privacy. This will also check + // doc for default methods defined on traits. + check_missing_doc_attrs(cx, + Some(m.id), + m.attrs.as_slice(), + m.span, + "a method"); +} + +pub fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) { + check_missing_doc_attrs(cx, + Some(tm.id), + tm.attrs.as_slice(), + tm.span, + "a type method"); +} + +pub fn check_missing_doc_struct_field(cx: &Context, sf: &ast::StructField) { + match sf.node.kind { + ast::NamedField(_, vis) if vis == ast::Public => + check_missing_doc_attrs(cx, + Some(cx.cur_struct_def_id), + sf.node.attrs.as_slice(), + sf.span, + "a struct field"), + _ => {} + } +} + +pub fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) { + check_missing_doc_attrs(cx, + Some(v.node.id), + v.node.attrs.as_slice(), + v.span, + "a variant"); +} + +/// Checks for use of items with #[deprecated], #[experimental] and +/// #[unstable] (or none of them) attributes. +pub fn check_stability(cx: &Context, e: &ast::Expr) { + let id = match e.node { + ast::ExprPath(..) | ast::ExprStruct(..) => { + match cx.tcx.def_map.borrow().find(&e.id) { + Some(&def) => def.def_id(), + None => return + } + } + ast::ExprMethodCall(..) => { + let method_call = typeck::MethodCall::expr(e.id); + match cx.tcx.method_map.borrow().find(&method_call) { + Some(method) => { + match method.origin { + typeck::MethodStatic(def_id) => { + // If this implements a trait method, get def_id + // of the method inside trait definition. + // Otherwise, use the current def_id (which refers + // to the method inside impl). + ty::trait_method_of_method( + cx.tcx, def_id).unwrap_or(def_id) + } + typeck::MethodParam(typeck::MethodParam { + trait_id: trait_id, + method_num: index, + .. + }) + | typeck::MethodObject(typeck::MethodObject { + trait_id: trait_id, + method_num: index, + .. + }) => ty::trait_method(cx.tcx, trait_id, index).def_id + } + } + None => return + } + } + _ => return + }; + + let stability = if ast_util::is_local(id) { + // this crate + let s = cx.tcx.map.with_attrs(id.node, |attrs| { + attrs.map(|a| attr::find_stability(a.as_slice())) + }); + match s { + Some(s) => s, + + // no possibility of having attributes + // (e.g. it's a local variable), so just + // ignore it. + None => return + } + } else { + // cross-crate + + let mut s = None; + // run through all the attributes and take the first + // stability one. + csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| { + if s.is_none() { + s = attr::find_stability(attrs.as_slice()) + } + }); + s + }; + + let (lint, label) = match stability { + // no stability attributes == Unstable + None => (lint::Unstable, "unmarked"), + Some(attr::Stability { level: attr::Unstable, .. }) => + (lint::Unstable, "unstable"), + Some(attr::Stability { level: attr::Experimental, .. }) => + (lint::Experimental, "experimental"), + Some(attr::Stability { level: attr::Deprecated, .. }) => + (lint::Deprecated, "deprecated"), + _ => return + }; + + let msg = match stability { + Some(attr::Stability { text: Some(ref s), .. }) => { + format!("use of {} item: {}", label, *s) + } + _ => format!("use of {} item", label) + }; + + cx.span_lint(lint, e.span, msg.as_slice()); +} + +pub fn check_enum_variant_sizes(cx: &mut 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.node_levels.insert((it.id, lint::VariantSizeDifference), (lvl, src)); + }, + _ => { } + } + }, + _ => { } + } +} diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index a3ee8003a455f..0e9f0fd1be96a 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -36,44 +36,27 @@ #![allow(non_camel_case_types)] use driver::session; -use metadata::csearch; use middle::dead::DEAD_CODE_LINT_STR; -use middle::def; -use middle::def::*; -use middle::pat_util; use middle::privacy; -use middle::trans::adt; // for `adt::is_ffi_safe` use middle::ty; -use middle::typeck::astconv::{ast_ty_to_ty, AstConv}; +use middle::typeck::astconv::AstConv; use middle::typeck::infer; -use middle::typeck; -use util::ppaux::{ty_to_str}; use util::nodemap::NodeSet; -use std::cmp; use std::collections::HashMap; -use std::i16; -use std::i32; -use std::i64; -use std::i8; use std::rc::Rc; use std::to_str::ToStr; -use std::u16; -use std::u32; -use std::u64; -use std::u8; use std::collections::SmallIntMap; -use syntax::abi; -use syntax::ast_map; use syntax::ast_util::IdVisitingOperation; use syntax::attr::AttrMetaMethods; use syntax::attr; use syntax::codemap::Span; use syntax::parse::token::InternedString; -use syntax::parse::token; use syntax::visit::Visitor; use syntax::{ast, ast_util, visit}; +mod builtin; + #[deriving(Clone, Show, PartialEq, PartialOrd, Eq, Ord, Hash)] pub enum Lint { CTypes, @@ -92,6 +75,7 @@ pub enum Lint { TypeOverflow, UnusedUnsafe, UnsafeBlock, + AttributeUsage, UnusedAttribute, UnknownFeatures, UnknownCrateType, @@ -293,6 +277,13 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[ default: Allow }), + ("attribute_usage", + LintSpec { + lint: AttributeUsage, + desc: "detects bad use of attributes", + default: Warn + }), + ("unused_attribute", LintSpec { lint: UnusedAttribute, @@ -704,915 +695,6 @@ pub fn contains_lint(attrs: &[ast::Attribute], false } -fn check_while_true_expr(cx: &Context, e: &ast::Expr) { - match e.node { - ast::ExprWhile(cond, _) => { - match cond.node { - ast::ExprLit(lit) => { - match lit.node { - ast::LitBool(true) => { - cx.span_lint(WhileTrue, - e.span, - "denote infinite loops with loop \ - { ... }"); - } - _ => {} - } - } - _ => () - } - } - _ => () - } -} -impl<'a> AstConv for Context<'a>{ - fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx } - - fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty { - ty::lookup_item_type(self.tcx, id) - } - - fn get_trait_def(&self, id: ast::DefId) -> Rc { - ty::lookup_trait_def(self.tcx, id) - } - - fn ty_infer(&self, _span: Span) -> ty::t { - infer::new_infer_ctxt(self.tcx).next_ty_var() - } -} - - -fn check_unused_casts(cx: &Context, e: &ast::Expr) { - return match e.node { - ast::ExprCast(expr, 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 { - cx.span_lint(UnnecessaryTypecast, ty.span, - "unnecessary type cast"); - } - } - _ => () - }; -} - -fn check_type_limits(cx: &Context, e: &ast::Expr) { - return match e.node { - ast::ExprUnary(ast::UnNeg, ex) => { - match ex.node { - ast::ExprLit(lit) => { - match lit.node { - ast::LitUint(..) => { - cx.span_lint(UnsignedNegate, e.span, - "negation of unsigned int literal may be unintentional"); - }, - _ => () - } - }, - _ => { - let t = ty::expr_ty(cx.tcx, ex); - match ty::get(t).sty { - ty::ty_uint(_) => { - cx.span_lint(UnsignedNegate, e.span, - "negation of unsigned int variable may be unintentional"); - }, - _ => () - } - } - } - }, - ast::ExprBinary(binop, l, r) => { - if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) { - cx.span_lint(TypeLimits, e.span, - "comparison is useless due to type limits"); - } - }, - ast::ExprLit(lit) => { - match ty::get(ty::expr_ty(cx.tcx, e)).sty { - ty::ty_int(t) => { - let int_type = if t == ast::TyI { - cx.tcx.sess.targ_cfg.int_type - } else { t }; - let (min, max) = int_ty_range(int_type); - let mut lit_val: i64 = match lit.node { - ast::LitInt(v, _) => v, - ast::LitUint(v, _) => v as i64, - ast::LitIntUnsuffixed(v) => v, - _ => fail!() - }; - if cx.negated_expr_id == e.id { - lit_val *= -1; - } - if lit_val < min || lit_val > max { - cx.span_lint(TypeOverflow, e.span, - "literal out of range for its type"); - } - }, - ty::ty_uint(t) => { - let uint_type = if t == ast::TyU { - cx.tcx.sess.targ_cfg.uint_type - } else { t }; - let (min, max) = uint_ty_range(uint_type); - let lit_val: u64 = match lit.node { - ast::LitInt(v, _) => v as u64, - ast::LitUint(v, _) => v, - ast::LitIntUnsuffixed(v) => v as u64, - _ => fail!() - }; - if lit_val < min || lit_val > max { - cx.span_lint(TypeOverflow, e.span, - "literal out of range for its type"); - } - }, - - _ => () - }; - }, - _ => () - }; - - fn is_valid(binop: ast::BinOp, v: T, - min: T, max: T) -> bool { - match binop { - ast::BiLt => v > min && v <= max, - ast::BiLe => v >= min && v < max, - ast::BiGt => v >= min && v < max, - ast::BiGe => v > min && v <= max, - ast::BiEq | ast::BiNe => v >= min && v <= max, - _ => fail!() - } - } - - fn rev_binop(binop: ast::BinOp) -> ast::BinOp { - match binop { - ast::BiLt => ast::BiGt, - ast::BiLe => ast::BiGe, - ast::BiGt => ast::BiLt, - ast::BiGe => ast::BiLe, - _ => binop - } - } - - // for int & uint, be conservative with the warnings, so that the - // warnings are consistent between 32- and 64-bit platforms - fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) { - match int_ty { - ast::TyI => (i64::MIN, i64::MAX), - ast::TyI8 => (i8::MIN as i64, i8::MAX as i64), - ast::TyI16 => (i16::MIN as i64, i16::MAX as i64), - ast::TyI32 => (i32::MIN as i64, i32::MAX as i64), - ast::TyI64 => (i64::MIN, i64::MAX) - } - } - - fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) { - match uint_ty { - ast::TyU => (u64::MIN, u64::MAX), - ast::TyU8 => (u8::MIN as u64, u8::MAX as u64), - ast::TyU16 => (u16::MIN as u64, u16::MAX as u64), - ast::TyU32 => (u32::MIN as u64, u32::MAX as u64), - ast::TyU64 => (u64::MIN, u64::MAX) - } - } - - fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp, - l: &ast::Expr, r: &ast::Expr) -> bool { - let (lit, expr, swap) = match (&l.node, &r.node) { - (&ast::ExprLit(_), _) => (l, r, true), - (_, &ast::ExprLit(_)) => (r, l, false), - _ => return true - }; - // Normalize the binop so that the literal is always on the RHS in - // the comparison - let norm_binop = if swap { rev_binop(binop) } else { binop }; - match ty::get(ty::expr_ty(tcx, expr)).sty { - ty::ty_int(int_ty) => { - let (min, max) = int_ty_range(int_ty); - let lit_val: i64 = match lit.node { - ast::ExprLit(li) => match li.node { - ast::LitInt(v, _) => v, - ast::LitUint(v, _) => v as i64, - ast::LitIntUnsuffixed(v) => v, - _ => return true - }, - _ => fail!() - }; - is_valid(norm_binop, lit_val, min, max) - } - ty::ty_uint(uint_ty) => { - let (min, max): (u64, u64) = uint_ty_range(uint_ty); - let lit_val: u64 = match lit.node { - ast::ExprLit(li) => match li.node { - ast::LitInt(v, _) => v as u64, - ast::LitUint(v, _) => v, - ast::LitIntUnsuffixed(v) => v as u64, - _ => return true - }, - _ => fail!() - }; - is_valid(norm_binop, lit_val, min, max) - } - _ => true - } - } - - fn is_comparison(binop: ast::BinOp) -> bool { - match binop { - ast::BiEq | ast::BiLt | ast::BiLe | - ast::BiNe | ast::BiGe | ast::BiGt => true, - _ => false - } - } -} - -fn check_item_ctypes(cx: &Context, it: &ast::Item) { - fn check_ty(cx: &Context, ty: &ast::Ty) { - match ty.node { - ast::TyPath(_, _, id) => { - match cx.tcx.def_map.borrow().get_copy(&id) { - def::DefPrimTy(ast::TyInt(ast::TyI)) => { - cx.span_lint(CTypes, ty.span, - "found rust type `int` in foreign module, while \ - libc::c_int or libc::c_long should be used"); - } - def::DefPrimTy(ast::TyUint(ast::TyU)) => { - cx.span_lint(CTypes, ty.span, - "found rust type `uint` in foreign module, while \ - libc::c_uint or libc::c_ulong should be used"); - } - def::DefTy(def_id) => { - if !adt::is_ffi_safe(cx.tcx, def_id) { - cx.span_lint(CTypes, ty.span, - "found enum type without foreign-function-safe \ - representation annotation in foreign module"); - // hmm... this message could be more helpful - } - } - _ => () - } - } - ast::TyPtr(ref mt) => { check_ty(cx, mt.ty) } - _ => {} - } - } - - fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) { - for input in decl.inputs.iter() { - check_ty(cx, input.ty); - } - check_ty(cx, decl.output) - } - - match it.node { - ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => { - for ni in nmod.items.iter() { - match ni.node { - ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl), - ast::ForeignItemStatic(t, _) => check_ty(cx, t) - } - } - } - _ => {/* nothing to do */ } - } -} - -fn check_heap_type(cx: &Context, span: Span, ty: ty::t) { - let xs = [ManagedHeapMemory, OwnedHeapMemory, HeapMemory]; - for &lint in xs.iter() { - if cx.get_level(lint) == Allow { continue } - - 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(_) | - ty::ty_trait(box ty::TyTrait { - store: ty::UniqTraitStore, .. - }) | - ty::ty_closure(box ty::ClosureTy { - store: ty::UniqTraitStore, - .. - }) => { - n_uniq += 1; - } - - _ => () - }; - t - }); - - if n_uniq > 0 && lint != ManagedHeapMemory { - let s = ty_to_str(cx.tcx, ty); - let m = format!("type uses owned (Box type) pointers: {}", s); - cx.span_lint(lint, span, m.as_slice()); - } - - if n_box > 0 && 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()); - } - } -} - -fn check_heap_item(cx: &Context, it: &ast::Item) { - match it.node { - ast::ItemFn(..) | - ast::ItemTy(..) | - ast::ItemEnum(..) | - ast::ItemStruct(..) => check_heap_type(cx, it.span, - ty::node_id_to_type(cx.tcx, - it.id)), - _ => () - } - - // If it's a struct, we also have to check the fields' types - match it.node { - ast::ItemStruct(struct_def, _) => { - for struct_field in struct_def.fields.iter() { - check_heap_type(cx, struct_field.span, - ty::node_id_to_type(cx.tcx, - struct_field.node.id)); - } - } - _ => () - } -} - -struct RawPtrDerivingVisitor<'a> { - cx: &'a Context<'a> -} - -impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> { - fn visit_ty(&mut self, ty: &ast::Ty, _: ()) { - static MSG: &'static str = "use of `#[deriving]` with a raw pointer"; - match ty.node { - ast::TyPtr(..) => self.cx.span_lint(RawPointerDeriving, ty.span, MSG), - _ => {} - } - visit::walk_ty(self, ty, ()); - } - // explicit override to a no-op to reduce code bloat - fn visit_expr(&mut self, _: &ast::Expr, _: ()) {} - fn visit_block(&mut self, _: &ast::Block, _: ()) {} -} - -fn check_raw_ptr_deriving(cx: &mut Context, item: &ast::Item) { - if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") { - return - } - let did = match item.node { - ast::ItemImpl(..) => { - match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty { - ty::ty_enum(did, _) => did, - ty::ty_struct(did, _) => did, - _ => return, - } - } - _ => return, - }; - if !ast_util::is_local(did) { return } - let item = match cx.tcx.map.find(did.node) { - Some(ast_map::NodeItem(item)) => item, - _ => return, - }; - if !cx.checked_raw_pointers.insert(item.id) { return } - match item.node { - ast::ItemStruct(..) | ast::ItemEnum(..) => { - let mut visitor = RawPtrDerivingVisitor { cx: cx }; - visit::walk_item(&mut visitor, item, ()); - } - _ => {} - } -} - -fn check_unused_attribute(cx: &Context, attr: &ast::Attribute) { - static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [ - // FIXME: #14408 whitelist docs since rustdoc looks at them - "doc", - - // FIXME: #14406 these are processed in trans, which happens after the - // lint pass - "address_insignificant", - "cold", - "inline", - "link", - "link_name", - "link_section", - "no_builtins", - "no_mangle", - "no_split_stack", - "packed", - "static_assert", - "thread_local", - - // not used anywhere (!?) but apparently we want to keep them around - "comment", - "desc", - "license", - - // FIXME: #14407 these are only looked at on-demand so we can't - // guarantee they'll have already been checked - "deprecated", - "experimental", - "frozen", - "locked", - "must_use", - "stable", - "unstable", - ]; - - static CRATE_ATTRS: &'static [&'static str] = &'static [ - "crate_type", - "feature", - "no_start", - "no_main", - "no_std", - "crate_id", - "desc", - "comment", - "license", - "copyright", - "no_builtins", - ]; - - for &name in ATTRIBUTE_WHITELIST.iter() { - if attr.check_name(name) { - break; - } - } - - if !attr::is_used(attr) { - cx.span_lint(UnusedAttribute, attr.span, "unused attribute"); - if CRATE_ATTRS.contains(&attr.name().get()) { - let msg = match attr.node.style { - ast::AttrOuter => "crate-level attribute should be an inner \ - attribute: add an exclamation mark: #![foo]", - ast::AttrInner => "crate-level attribute should be in the \ - root module", - }; - cx.span_lint(UnusedAttribute, attr.span, msg); - } - } -} - -fn check_heap_expr(cx: &Context, e: &ast::Expr) { - let ty = ty::expr_ty(cx.tcx, e); - check_heap_type(cx, e.span, ty); -} - -fn check_path_statement(cx: &Context, s: &ast::Stmt) { - match s.node { - ast::StmtSemi(expr, _) => { - match expr.node { - ast::ExprPath(_) => { - cx.span_lint(PathStatement, - s.span, - "path statement with no effect"); - } - _ => {} - } - } - _ => () - } -} - -fn check_unused_result(cx: &Context, s: &ast::Stmt) { - let expr = match s.node { - ast::StmtSemi(expr, _) => expr, - _ => return - }; - let t = ty::expr_ty(cx.tcx, expr); - match ty::get(t).sty { - ty::ty_nil | ty::ty_bot | ty::ty_bool => return, - _ => {} - } - match expr.node { - ast::ExprRet(..) => return, - _ => {} - } - - let t = ty::expr_ty(cx.tcx, expr); - let mut warned = false; - match ty::get(t).sty { - ty::ty_struct(did, _) | - ty::ty_enum(did, _) => { - if ast_util::is_local(did) { - match cx.tcx.map.get(did.node) { - ast_map::NodeItem(it) => { - if attr::contains_name(it.attrs.as_slice(), - "must_use") { - cx.span_lint(UnusedMustUse, s.span, - "unused result which must be used"); - warned = true; - } - } - _ => {} - } - } else { - csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| { - if attr::contains_name(attrs.as_slice(), "must_use") { - cx.span_lint(UnusedMustUse, s.span, - "unused result which must be used"); - warned = true; - } - }); - } - } - _ => {} - } - if !warned { - cx.span_lint(UnusedResult, s.span, "unused result"); - } -} - -fn check_deprecated_owned_vector(cx: &Context, e: &ast::Expr) { - let t = ty::expr_ty(cx.tcx, e); - match ty::get(t).sty { - ty::ty_uniq(t) => match ty::get(t).sty { - ty::ty_vec(_, None) => { - cx.span_lint(DeprecatedOwnedVector, e.span, - "use of deprecated `~[]` vector; replaced by `std::vec::Vec`") - } - _ => {} - }, - _ => {} - } -} - -fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) { - fn is_camel_case(ident: ast::Ident) -> bool { - let ident = token::get_ident(ident); - assert!(!ident.get().is_empty()); - let ident = ident.get().trim_chars('_'); - - // start with a non-lowercase letter rather than non-uppercase - // ones (some scripts don't have a concept of upper/lowercase) - !ident.char_at(0).is_lowercase() && !ident.contains_char('_') - } - - fn to_camel_case(s: &str) -> String { - s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)| - if i == 0 { c.to_uppercase() } - else { c } - )).collect() - } - - fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) { - let s = token::get_ident(ident); - - if !is_camel_case(ident) { - cx.span_lint( - NonCamelCaseTypes, span, - format!("{} `{}` should have a camel case name such as `{}`", - sort, s, to_camel_case(s.get())).as_slice()); - } - } - - match it.node { - ast::ItemTy(..) | ast::ItemStruct(..) => { - check_case(cx, "type", it.ident, it.span) - } - ast::ItemTrait(..) => { - check_case(cx, "trait", it.ident, it.span) - } - ast::ItemEnum(ref enum_definition, _) => { - check_case(cx, "type", it.ident, it.span); - for variant in enum_definition.variants.iter() { - check_case(cx, "variant", variant.node.name, variant.span); - } - } - _ => () - } -} - -fn check_snake_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) { - fn is_snake_case(ident: ast::Ident) -> bool { - let ident = token::get_ident(ident); - assert!(!ident.get().is_empty()); - let ident = ident.get().trim_chars('_'); - - let mut allow_underscore = true; - ident.chars().all(|c| { - allow_underscore = match c { - c if c.is_lowercase() || c.is_digit() => true, - '_' if allow_underscore => false, - _ => return false, - }; - true - }) - } - - fn to_snake_case(str: &str) -> String { - let mut words = vec![]; - for s in str.split('_') { - let mut buf = String::new(); - if s.is_empty() { continue; } - for ch in s.chars() { - if !buf.is_empty() && ch.is_uppercase() { - words.push(buf); - buf = String::new(); - } - buf.push_char(ch.to_lowercase()); - } - words.push(buf); - } - words.connect("_") - } - - let s = token::get_ident(ident); - - if !is_snake_case(ident) { - cx.span_lint(NonSnakeCaseFunctions, span, - format!("{} `{}` should have a snake case name such as `{}`", - sort, s, to_snake_case(s.get())).as_slice()); - } -} - -fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) { - match it.node { - // only check static constants - ast::ItemStatic(_, ast::MutImmutable, _) => { - let s = token::get_ident(it.ident); - // check for lowercase letters rather than non-uppercase - // ones (some scripts don't have a concept of - // upper/lowercase) - if s.get().chars().any(|c| c.is_lowercase()) { - cx.span_lint(NonUppercaseStatics, it.span, - format!("static constant `{}` should have an uppercase name \ - such as `{}`", s.get(), - s.get().chars().map(|c| c.to_uppercase()) - .collect::().as_slice()).as_slice()); - } - } - _ => {} - } -} - -fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) { - // Lint for constants that look like binding identifiers (#7526) - match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) { - (&ast::PatIdent(_, ref path, _), Some(&def::DefStatic(_, false))) => { - // last identifier alone is right choice for this lint. - let ident = path.segments.last().unwrap().identifier; - let s = token::get_ident(ident); - if s.get().chars().any(|c| c.is_lowercase()) { - cx.span_lint(NonUppercasePatternStatics, path.span, - format!("static constant in pattern `{}` should have an uppercase \ - name such as `{}`", s.get(), - s.get().chars().map(|c| c.to_uppercase()) - .collect::().as_slice()).as_slice()); - } - } - _ => {} - } -} - -fn check_pat_uppercase_variable(cx: &Context, p: &ast::Pat) { - match &p.node { - &ast::PatIdent(_, ref path, _) => { - match cx.tcx.def_map.borrow().find(&p.id) { - Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) | - Some(&def::DefArg(_, _)) => { - // last identifier alone is right choice for this lint. - let ident = path.segments.last().unwrap().identifier; - let s = token::get_ident(ident); - if s.get().len() > 0 && s.get().char_at(0).is_uppercase() { - cx.span_lint( - UppercaseVariables, - path.span, - "variable names should start with a lowercase character"); - } - } - _ => {} - } - } - _ => {} - } -} - -fn check_struct_uppercase_variable(cx: &Context, s: &ast::StructDef) { - for sf in s.fields.iter() { - match sf.node { - ast::StructField_ { kind: ast::NamedField(ident, _), .. } => { - let s = token::get_ident(ident); - if s.get().char_at(0).is_uppercase() { - cx.span_lint( - UppercaseVariables, - sf.span, - "structure field names should start with a lowercase character"); - } - } - _ => {} - } - } -} - -fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) { - match value.node { - ast::ExprParen(_) => { - cx.span_lint(UnnecessaryParens, value.span, - format!("unnecessary parentheses around {}", - msg).as_slice()) - } - _ => {} - } -} - -fn check_unnecessary_parens_expr(cx: &Context, e: &ast::Expr) { - let (value, msg) = match e.node { - ast::ExprIf(cond, _, _) => (cond, "`if` condition"), - ast::ExprWhile(cond, _) => (cond, "`while` condition"), - ast::ExprMatch(head, _) => (head, "`match` head expression"), - ast::ExprRet(Some(value)) => (value, "`return` value"), - ast::ExprAssign(_, value) => (value, "assigned value"), - ast::ExprAssignOp(_, _, value) => (value, "assigned value"), - _ => return - }; - check_unnecessary_parens_core(cx, value, msg); -} - -fn check_unnecessary_parens_stmt(cx: &Context, s: &ast::Stmt) { - let (value, msg) = match s.node { - ast::StmtDecl(decl, _) => match decl.node { - ast::DeclLocal(local) => match local.init { - Some(value) => (value, "assigned value"), - None => return - }, - _ => return - }, - _ => return - }; - check_unnecessary_parens_core(cx, value, msg); -} - -fn check_unused_unsafe(cx: &Context, e: &ast::Expr) { - match e.node { - // Don't warn about generated blocks, that'll just pollute the output. - ast::ExprBlock(ref blk) => { - if blk.rules == ast::UnsafeBlock(ast::UserProvided) && - !cx.tcx.used_unsafe.borrow().contains(&blk.id) { - cx.span_lint(UnusedUnsafe, blk.span, - "unnecessary `unsafe` block"); - } - } - _ => () - } -} - -fn check_unsafe_block(cx: &Context, e: &ast::Expr) { - match e.node { - // Don't warn about generated blocks, that'll just pollute the output. - ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => { - cx.span_lint(UnsafeBlock, blk.span, "usage of an `unsafe` block"); - } - _ => () - } -} - -fn check_unused_mut_pat(cx: &Context, pats: &[@ast::Pat]) { - // collect all mutable pattern and group their NodeIDs by their Identifier to - // avoid false warnings in match arms with multiple patterns - let mut mutables = HashMap::new(); - for &p in pats.iter() { - pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path| { - match mode { - ast::BindByValue(ast::MutMutable) => { - if path.segments.len() != 1 { - cx.tcx.sess.span_bug(p.span, - "mutable binding that doesn't consist \ - of exactly one segment"); - } - let ident = path.segments.get(0).identifier; - if !token::get_ident(ident).get().starts_with("_") { - mutables.insert_or_update_with(ident.name as uint, vec!(id), |_, old| { - old.push(id); - }); - } - } - _ => { - } - } - }); - } - - let used_mutables = cx.tcx.used_mut_nodes.borrow(); - for (_, v) in mutables.iter() { - if !v.iter().any(|e| used_mutables.contains(e)) { - cx.span_lint(UnusedMut, cx.tcx.map.span(*v.get(0)), - "variable does not need to be mutable"); - } - } -} - -enum Allocation { - VectorAllocation, - BoxAllocation -} - -fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) { - // Warn if string and vector literals with sigils, or boxing expressions, - // are immediately borrowed. - let allocation = match e.node { - ast::ExprVstore(e2, ast::ExprVstoreUniq) => { - match e2.node { - ast::ExprLit(lit) if ast_util::lit_is_str(lit) => { - VectorAllocation - } - ast::ExprVec(..) => VectorAllocation, - _ => return - } - } - ast::ExprUnary(ast::UnUniq, _) | - ast::ExprUnary(ast::UnBox, _) => BoxAllocation, - - _ => return - }; - - let report = |msg| { - cx.span_lint(UnnecessaryAllocation, e.span, msg); - }; - - match cx.tcx.adjustments.borrow().find(&e.id) { - Some(adjustment) => { - match *adjustment { - ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { - match (allocation, autoref) { - (VectorAllocation, Some(ty::AutoBorrowVec(..))) => { - report("unnecessary allocation, the sigil can be \ - removed"); - } - (BoxAllocation, - Some(ty::AutoPtr(_, ast::MutImmutable))) => { - report("unnecessary allocation, use & instead"); - } - (BoxAllocation, - Some(ty::AutoPtr(_, ast::MutMutable))) => { - report("unnecessary allocation, use &mut \ - instead"); - } - _ => () - } - } - _ => {} - } - } - - _ => () - } -} - -fn check_missing_doc_attrs(cx: &Context, - id: Option, - attrs: &[ast::Attribute], - sp: Span, - desc: &'static str) { - // If we're building a test harness, then warning about - // documentation is probably not really relevant right now. - if cx.tcx.sess.opts.test { return } - - // `#[doc(hidden)]` disables missing_doc check. - if cx.is_doc_hidden { return } - - // Only check publicly-visible items, using the result from the privacy pass. It's an option so - // the crate root can also use this function (it doesn't have a NodeId). - match id { - Some(ref id) if !cx.exported_items.contains(id) => return, - _ => () - } - - let has_doc = attrs.iter().any(|a| { - match a.node.value.node { - ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true, - _ => false - } - }); - if !has_doc { - cx.span_lint(MissingDoc, - sp, - format!("missing documentation for {}", - desc).as_slice()); - } -} - -fn check_missing_doc_item(cx: &Context, it: &ast::Item) { - let desc = match it.node { - ast::ItemFn(..) => "a function", - ast::ItemMod(..) => "a module", - ast::ItemEnum(..) => "an enum", - ast::ItemStruct(..) => "a struct", - ast::ItemTrait(..) => "a trait", - _ => return - }; - check_missing_doc_attrs(cx, - Some(it.id), - it.attrs.as_slice(), - it.span, - desc); -} - #[deriving(PartialEq)] enum MethodContext { TraitDefaultImpl, @@ -1620,19 +702,6 @@ enum MethodContext { PlainImpl } -fn check_missing_doc_method(cx: &Context, m: &ast::Method) { - // If the method is an impl for a trait, don't doc. - if method_context(cx, m) == TraitImpl { return; } - - // Otherwise, doc according to privacy. This will also check - // doc for default methods defined on traits. - check_missing_doc_attrs(cx, - Some(m.id), - m.attrs.as_slice(), - m.span, - "a method"); -} - fn method_context(cx: &Context, m: &ast::Method) -> MethodContext { let did = ast::DefId { krate: ast::LOCAL_CRATE, @@ -1655,148 +724,32 @@ fn method_context(cx: &Context, m: &ast::Method) -> MethodContext { } } -fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) { - check_missing_doc_attrs(cx, - Some(tm.id), - tm.attrs.as_slice(), - tm.span, - "a type method"); -} +impl<'a> AstConv for Context<'a>{ + fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx } -fn check_missing_doc_struct_field(cx: &Context, sf: &ast::StructField) { - match sf.node.kind { - ast::NamedField(_, vis) if vis == ast::Public => - check_missing_doc_attrs(cx, - Some(cx.cur_struct_def_id), - sf.node.attrs.as_slice(), - sf.span, - "a struct field"), - _ => {} + fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty { + ty::lookup_item_type(self.tcx, id) } -} - -fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) { - check_missing_doc_attrs(cx, - Some(v.node.id), - v.node.attrs.as_slice(), - v.span, - "a variant"); -} - -/// Checks for use of items with #[deprecated], #[experimental] and -/// #[unstable] (or none of them) attributes. -fn check_stability(cx: &Context, e: &ast::Expr) { - let id = match e.node { - ast::ExprPath(..) | ast::ExprStruct(..) => { - match cx.tcx.def_map.borrow().find(&e.id) { - Some(&def) => def.def_id(), - None => return - } - } - ast::ExprMethodCall(..) => { - let method_call = typeck::MethodCall::expr(e.id); - match cx.tcx.method_map.borrow().find(&method_call) { - Some(method) => { - match method.origin { - typeck::MethodStatic(def_id) => { - // If this implements a trait method, get def_id - // of the method inside trait definition. - // Otherwise, use the current def_id (which refers - // to the method inside impl). - ty::trait_method_of_method( - cx.tcx, def_id).unwrap_or(def_id) - } - typeck::MethodParam(typeck::MethodParam { - trait_id: trait_id, - method_num: index, - .. - }) - | typeck::MethodObject(typeck::MethodObject { - trait_id: trait_id, - method_num: index, - .. - }) => ty::trait_method(cx.tcx, trait_id, index).def_id - } - } - None => return - } - } - _ => return - }; - let stability = if ast_util::is_local(id) { - // this crate - let s = cx.tcx.map.with_attrs(id.node, |attrs| { - attrs.map(|a| attr::find_stability(a.as_slice())) - }); - match s { - Some(s) => s, - - // no possibility of having attributes - // (e.g. it's a local variable), so just - // ignore it. - None => return - } - } else { - // cross-crate - - let mut s = None; - // run through all the attributes and take the first - // stability one. - csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| { - if s.is_none() { - s = attr::find_stability(attrs.as_slice()) - } - }); - s - }; - - let (lint, label) = match stability { - // no stability attributes == Unstable - None => (Unstable, "unmarked"), - Some(attr::Stability { level: attr::Unstable, .. }) => - (Unstable, "unstable"), - Some(attr::Stability { level: attr::Experimental, .. }) => - (Experimental, "experimental"), - Some(attr::Stability { level: attr::Deprecated, .. }) => - (Deprecated, "deprecated"), - _ => return - }; - - let msg = match stability { - Some(attr::Stability { text: Some(ref s), .. }) => { - format!("use of {} item: {}", label, *s) - } - _ => format!("use of {} item", label) - }; - - cx.span_lint(lint, e.span, msg.as_slice()); -} + fn get_trait_def(&self, id: ast::DefId) -> Rc { + ty::lookup_trait_def(self.tcx, id) + } -fn check_enum_variant_sizes(cx: &mut Context, it: &ast::Item) { - match it.node { - ast::ItemEnum(..) => { - match cx.cur.find(&(VariantSizeDifference as uint)) { - Some(&(lvl, src)) if lvl != Allow => { - cx.node_levels.insert((it.id, VariantSizeDifference), (lvl, src)); - }, - _ => { } - } - }, - _ => { } + fn ty_infer(&self, _span: Span) -> ty::t { + infer::new_infer_ctxt(self.tcx).next_ty_var() } } impl<'a> Visitor<()> for Context<'a> { fn visit_item(&mut self, it: &ast::Item, _: ()) { self.with_lint_attrs(it.attrs.as_slice(), |cx| { - check_enum_variant_sizes(cx, it); - check_item_ctypes(cx, it); - check_item_non_camel_case_types(cx, it); - check_item_non_uppercase_statics(cx, it); - check_heap_item(cx, it); - check_missing_doc_item(cx, it); - check_raw_ptr_deriving(cx, it); + builtin::check_enum_variant_sizes(cx, it); + builtin::check_item_ctypes(cx, it); + builtin::check_item_non_camel_case_types(cx, it); + builtin::check_item_non_uppercase_statics(cx, it); + builtin::check_heap_item(cx, it); + builtin::check_missing_doc_item(cx, it); + builtin::check_raw_ptr_deriving(cx, it); cx.visit_ids(|v| v.visit_item(it, ())); @@ -1819,8 +772,8 @@ impl<'a> Visitor<()> for Context<'a> { } fn visit_pat(&mut self, p: &ast::Pat, _: ()) { - check_pat_non_uppercase_statics(self, p); - check_pat_uppercase_variable(self, p); + builtin::check_pat_non_uppercase_statics(self, p); + builtin::check_pat_uppercase_variable(self, p); visit::walk_pat(self, p, ()); } @@ -1838,37 +791,37 @@ impl<'a> Visitor<()> for Context<'a> { }, ast::ExprMatch(_, ref arms) => { for a in arms.iter() { - check_unused_mut_pat(self, a.pats.as_slice()); + builtin::check_unused_mut_pat(self, a.pats.as_slice()); } }, _ => () }; - check_while_true_expr(self, e); - check_stability(self, e); - check_unnecessary_parens_expr(self, e); - check_unused_unsafe(self, e); - check_unsafe_block(self, e); - check_unnecessary_allocation(self, e); - check_heap_expr(self, e); + builtin::check_while_true_expr(self, e); + builtin::check_stability(self, e); + builtin::check_unnecessary_parens_expr(self, e); + builtin::check_unused_unsafe(self, e); + builtin::check_unsafe_block(self, e); + builtin::check_unnecessary_allocation(self, e); + builtin::check_heap_expr(self, e); - check_type_limits(self, e); - check_unused_casts(self, e); - check_deprecated_owned_vector(self, e); + builtin::check_type_limits(self, e); + builtin::check_unused_casts(self, e); + builtin::check_deprecated_owned_vector(self, e); visit::walk_expr(self, e, ()); } fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) { - check_path_statement(self, s); - check_unused_result(self, s); - check_unnecessary_parens_stmt(self, s); + builtin::check_path_statement(self, s); + builtin::check_unused_result(self, s); + builtin::check_unnecessary_parens_stmt(self, s); match s.node { ast::StmtDecl(d, _) => { match d.node { ast::DeclLocal(l) => { - check_unused_mut_pat(self, &[l.pat]); + builtin::check_unused_mut_pat(self, &[l.pat]); }, _ => {} } @@ -1886,17 +839,19 @@ impl<'a> Visitor<()> for Context<'a> { }; for a in decl.inputs.iter(){ - check_unused_mut_pat(self, &[a.pat]); + builtin::check_unused_mut_pat(self, &[a.pat]); } match *fk { visit::FkMethod(ident, _, m) => { self.with_lint_attrs(m.attrs.as_slice(), |cx| { - check_missing_doc_method(cx, m); + builtin::check_missing_doc_method(cx, m); match method_context(cx, m) { - PlainImpl => check_snake_case(cx, "method", ident, span), - TraitDefaultImpl => check_snake_case(cx, "trait method", ident, span), + PlainImpl + => builtin::check_snake_case(cx, "method", ident, span), + TraitDefaultImpl + => builtin::check_snake_case(cx, "trait method", ident, span), _ => (), } @@ -1907,7 +862,7 @@ impl<'a> Visitor<()> for Context<'a> { }) }, visit::FkItemFn(ident, _, _, _) => { - check_snake_case(self, "function", ident, span); + builtin::check_snake_case(self, "function", ident, span); recurse(self); } _ => recurse(self), @@ -1916,8 +871,8 @@ impl<'a> Visitor<()> for Context<'a> { fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) { self.with_lint_attrs(t.attrs.as_slice(), |cx| { - check_missing_doc_ty_method(cx, t); - check_snake_case(cx, "trait method", t.ident, t.span); + builtin::check_missing_doc_ty_method(cx, t); + builtin::check_snake_case(cx, "trait method", t.ident, t.span); visit::walk_ty_method(cx, t, ()); }) @@ -1929,7 +884,7 @@ impl<'a> Visitor<()> for Context<'a> { _: &ast::Generics, id: ast::NodeId, _: ()) { - check_struct_uppercase_variable(self, s); + builtin::check_struct_uppercase_variable(self, s); let old_id = self.cur_struct_def_id; self.cur_struct_def_id = id; @@ -1939,7 +894,7 @@ impl<'a> Visitor<()> for Context<'a> { fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) { self.with_lint_attrs(s.node.attrs.as_slice(), |cx| { - check_missing_doc_struct_field(cx, s); + builtin::check_missing_doc_struct_field(cx, s); visit::walk_struct_field(cx, s, ()); }) @@ -1947,7 +902,7 @@ impl<'a> Visitor<()> for Context<'a> { fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) { self.with_lint_attrs(v.node.attrs.as_slice(), |cx| { - check_missing_doc_variant(cx, v); + builtin::check_missing_doc_variant(cx, v); visit::walk_variant(cx, v, g, ()); }) @@ -1957,7 +912,7 @@ impl<'a> Visitor<()> for Context<'a> { fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {} fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) { - check_unused_attribute(self, attr); + builtin::check_unused_attribute(self, attr); } } @@ -2009,11 +964,11 @@ pub fn check_crate(tcx: &ty::ctxt, // since the root module isn't visited as an item (because it isn't an item), warn for it // here. - check_missing_doc_attrs(cx, - None, - krate.attrs.as_slice(), - krate.span, - "crate"); + builtin::check_missing_doc_attrs(cx, + None, + krate.attrs.as_slice(), + krate.span, + "crate"); visit::walk_crate(cx, krate, ()); }); From 3d5801069a4ad113d8fe398d2a696ff80b3e9c18 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Sun, 1 Jun 2014 16:21:52 -0700 Subject: [PATCH 03/24] Rename lint::Lint to lint::LintId --- src/librustc/driver/config.rs | 2 +- src/librustc/driver/session.rs | 4 ++-- src/librustc/lint/mod.rs | 20 ++++++++++---------- src/librustc/middle/ty.rs | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index 0c8b261ac9b89..02b040a259637 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -70,7 +70,7 @@ pub struct Options { pub gc: bool, pub optimize: OptLevel, pub debuginfo: DebugInfoLevel, - pub lint_opts: Vec<(lint::Lint, lint::Level)> , + pub lint_opts: Vec<(lint::LintId, lint::Level)> , pub output_types: Vec , // This was mutable for rustpkg, which updates search paths based on the // parsed code. It remains mutable in case its replacements wants to use diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 7e80a057ba2df..7db1363460368 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -42,7 +42,7 @@ pub struct Session { // expected to be absolute. `None` means that there is no source file. pub local_crate_source_file: Option, pub working_dir: Path, - pub lints: RefCell>>, + pub lints: RefCell>>, pub node_id: Cell, pub crate_types: RefCell>, pub features: front::feature_gate::Features, @@ -105,7 +105,7 @@ impl Session { self.diagnostic().handler().unimpl(msg) } pub fn add_lint(&self, - lint: lint::Lint, + lint: lint::LintId, id: ast::NodeId, sp: Span, msg: String) { diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 0e9f0fd1be96a..03de3f652ab50 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -58,7 +58,7 @@ use syntax::{ast, ast_util, visit}; mod builtin; #[deriving(Clone, Show, PartialEq, PartialOrd, Eq, Ord, Hash)] -pub enum Lint { +pub enum LintId { CTypes, UnusedImports, UnnecessaryQualification, @@ -128,7 +128,7 @@ pub enum Level { #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)] pub struct LintSpec { pub default: Level, - pub lint: Lint, + pub lint: LintId, pub desc: &'static str, } @@ -464,7 +464,7 @@ struct Context<'a> { /// 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: Vec<(Lint, Level, LintSource)>, + lint_stack: Vec<(LintId, Level, LintSource)>, /// Id of the last visited negated expression negated_expr_id: ast::NodeId, @@ -474,7 +474,7 @@ struct Context<'a> { /// Level of lints for certain NodeIds, stored here because the body of /// the lint needs to run in trans. - node_levels: HashMap<(ast::NodeId, Lint), (Level, LintSource)>, + node_levels: HashMap<(ast::NodeId, LintId), (Level, LintSource)>, } pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span, @@ -511,7 +511,7 @@ pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span, } } -pub fn lint_to_str(lint: Lint) -> &'static str { +pub fn lint_to_str(lint: LintId) -> &'static str { for &(name, lspec) in lint_table.iter() { if lspec.lint == lint { return name; @@ -522,21 +522,21 @@ pub fn lint_to_str(lint: Lint) -> &'static str { } impl<'a> Context<'a> { - fn get_level(&self, lint: Lint) -> Level { + fn get_level(&self, lint: LintId) -> Level { match self.cur.find(&(lint as uint)) { Some(&(lvl, _)) => lvl, None => Allow } } - fn get_source(&self, lint: Lint) -> LintSource { + fn get_source(&self, lint: LintId) -> LintSource { match self.cur.find(&(lint as uint)) { Some(&(_, src)) => src, None => Default } } - fn set_level(&mut self, lint: Lint, level: Level, src: LintSource) { + fn set_level(&mut self, lint: LintId, level: Level, src: LintSource) { if level == Allow { self.cur.remove(&(lint as uint)); } else { @@ -544,7 +544,7 @@ impl<'a> Context<'a> { } } - fn lint_to_str(&self, lint: Lint) -> &'static str { + fn lint_to_str(&self, lint: LintId) -> &'static str { for (k, v) in self.dict.iter() { if v.lint == lint { return *k; @@ -553,7 +553,7 @@ impl<'a> Context<'a> { fail!("unregistered lint {}", lint); } - fn span_lint(&self, lint: Lint, span: Span, msg: &str) { + 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), diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 3b11a7a97b86d..889a2b35f58d2 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -356,7 +356,7 @@ pub struct ctxt { pub dependency_formats: RefCell, - pub node_lint_levels: RefCell>, } From d79e62448b2886aa20f0e8f8c391eef31140e188 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Mon, 2 Jun 2014 15:27:15 -0700 Subject: [PATCH 04/24] Convert lints to a trait-based infrastructure The immediate benefits are * moving the state specific to a single lint out of Context, and * replacing the soup of function calls in the Visitor impl with more structured control flow But this is also a step towards loadable lints. --- src/librustc/lint/builtin.rs | 1917 +++++++++++++++++++--------------- src/librustc/lint/mod.rs | 399 +++---- 2 files changed, 1306 insertions(+), 1010 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 3065645db2798..2ca4d3eb4e857 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -11,16 +11,15 @@ //! Lints built in to rustc. use metadata::csearch; -use middle::def; use middle::def::*; -use middle::pat_util; use middle::trans::adt; // for `adt::is_ffi_safe` -use middle::ty; use middle::typeck::astconv::{ast_ty_to_ty, AstConv}; use middle::typeck::infer; -use middle::typeck; +use middle::privacy::ExportedItems; +use middle::{typeck, ty, def, pat_util}; use util::ppaux::{ty_to_str}; -use lint::Context; +use util::nodemap::NodeSet; +use lint::{Context, LintPass}; use lint; use std::cmp; @@ -33,333 +32,405 @@ use std::u16; use std::u32; use std::u64; use std::u8; +use std::default::Default; use syntax::abi; use syntax::ast_map; use syntax::attr::AttrMetaMethods; use syntax::attr; use syntax::codemap::Span; use syntax::parse::token; -use syntax::visit::Visitor; use syntax::{ast, ast_util, visit}; -pub fn check_while_true_expr(cx: &Context, e: &ast::Expr) { - match e.node { - ast::ExprWhile(cond, _) => { - match cond.node { - ast::ExprLit(lit) => { - match lit.node { - ast::LitBool(true) => { - cx.span_lint(lint::WhileTrue, - e.span, - "denote infinite loops with loop \ - { ... }"); +/// Doesn't actually warn; just gathers information for use by +/// checks in trans. +#[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)] +pub struct WhileTrue; + +impl LintPass for WhileTrue { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + match e.node { + ast::ExprWhile(cond, _) => { + match cond.node { + ast::ExprLit(lit) => { + match lit.node { + ast::LitBool(true) => { + cx.span_lint(lint::WhileTrue, + e.span, + "denote infinite loops with loop \ + { ... }"); + } + _ => {} } - _ => {} } + _ => () } - _ => () } + _ => () } - _ => () } } -pub fn check_unused_casts(cx: &Context, e: &ast::Expr) { - return match e.node { - ast::ExprCast(expr, 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 { - cx.span_lint(lint::UnnecessaryTypecast, ty.span, - "unnecessary type cast"); +#[deriving(Default)] +pub struct UnusedCasts; + +impl LintPass for UnusedCasts { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + match e.node { + ast::ExprCast(expr, 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 { + cx.span_lint(lint::UnnecessaryTypecast, ty.span, + "unnecessary type cast"); + } } + _ => () } - _ => () - }; + } } -pub fn check_type_limits(cx: &Context, e: &ast::Expr) { - return match e.node { - ast::ExprUnary(ast::UnNeg, ex) => { - match ex.node { - ast::ExprLit(lit) => { - match lit.node { - ast::LitUint(..) => { - cx.span_lint(lint::UnsignedNegate, e.span, - "negation of unsigned int literal may be unintentional"); - }, - _ => () - } - }, - _ => { - let t = ty::expr_ty(cx.tcx, ex); - match ty::get(t).sty { - ty::ty_uint(_) => { - cx.span_lint(lint::UnsignedNegate, e.span, - "negation of unsigned int variable may be unintentional"); - }, - _ => () +pub struct TypeLimits { + /// Id of the last visited negated expression + negated_expr_id: ast::NodeId, +} + +impl Default for TypeLimits { + fn default() -> TypeLimits { + TypeLimits { + negated_expr_id: -1, + } + } +} + +impl LintPass for TypeLimits { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + match e.node { + ast::ExprUnary(ast::UnNeg, expr) => { + match expr.node { + ast::ExprLit(lit) => { + match lit.node { + ast::LitUint(..) => { + cx.span_lint(lint::UnsignedNegate, e.span, + "negation of unsigned int literal may \ + be unintentional"); + }, + _ => () + } + }, + _ => { + let t = ty::expr_ty(cx.tcx, expr); + match ty::get(t).sty { + ty::ty_uint(_) => { + cx.span_lint(lint::UnsignedNegate, e.span, + "negation of unsigned int variable may \ + be unintentional"); + }, + _ => () + } } + }; + // propagate negation, if the negation itself isn't negated + if self.negated_expr_id != e.id { + self.negated_expr_id = expr.id; } - } - }, - ast::ExprBinary(binop, l, r) => { - if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) { - cx.span_lint(lint::TypeLimits, e.span, - "comparison is useless due to type limits"); - } - }, - ast::ExprLit(lit) => { - match ty::get(ty::expr_ty(cx.tcx, e)).sty { - ty::ty_int(t) => { - let int_type = if t == ast::TyI { - cx.tcx.sess.targ_cfg.int_type - } else { t }; - let (min, max) = int_ty_range(int_type); - let mut lit_val: i64 = match lit.node { - ast::LitInt(v, _) => v, - ast::LitUint(v, _) => v as i64, - ast::LitIntUnsuffixed(v) => v, - _ => fail!() - }; - if cx.negated_expr_id == e.id { - lit_val *= -1; - } - if lit_val < min || lit_val > max { - cx.span_lint(lint::TypeOverflow, e.span, - "literal out of range for its type"); - } - }, - ty::ty_uint(t) => { - let uint_type = if t == ast::TyU { - cx.tcx.sess.targ_cfg.uint_type - } else { t }; - let (min, max) = uint_ty_range(uint_type); - let lit_val: u64 = match lit.node { - ast::LitInt(v, _) => v as u64, - ast::LitUint(v, _) => v, - ast::LitIntUnsuffixed(v) => v as u64, - _ => fail!() - }; - if lit_val < min || lit_val > max { - cx.span_lint(lint::TypeOverflow, e.span, - "literal out of range for its type"); - } - }, + }, + ast::ExprParen(expr) if self.negated_expr_id == e.id => { + self.negated_expr_id = expr.id; + }, + ast::ExprBinary(binop, l, r) => { + if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) { + cx.span_lint(lint::TypeLimits, e.span, + "comparison is useless due to type limits"); + } + }, + ast::ExprLit(lit) => { + match ty::get(ty::expr_ty(cx.tcx, e)).sty { + ty::ty_int(t) => { + let int_type = if t == ast::TyI { + cx.tcx.sess.targ_cfg.int_type + } else { t }; + let (min, max) = int_ty_range(int_type); + let mut lit_val: i64 = match lit.node { + ast::LitInt(v, _) => v, + ast::LitUint(v, _) => v as i64, + ast::LitIntUnsuffixed(v) => v, + _ => fail!() + }; + if self.negated_expr_id == e.id { + lit_val *= -1; + } + if lit_val < min || lit_val > max { + cx.span_lint(lint::TypeOverflow, e.span, + "literal out of range for its type"); + } + }, + ty::ty_uint(t) => { + let uint_type = if t == ast::TyU { + cx.tcx.sess.targ_cfg.uint_type + } else { t }; + let (min, max) = uint_ty_range(uint_type); + let lit_val: u64 = match lit.node { + ast::LitInt(v, _) => v as u64, + ast::LitUint(v, _) => v, + ast::LitIntUnsuffixed(v) => v as u64, + _ => fail!() + }; + if lit_val < min || lit_val > max { + cx.span_lint(lint::TypeOverflow, e.span, + "literal out of range for its type"); + } + }, - _ => () - }; - }, - _ => () - }; + _ => () + }; + }, + _ => () + }; - fn is_valid(binop: ast::BinOp, v: T, - min: T, max: T) -> bool { - match binop { - ast::BiLt => v > min && v <= max, - ast::BiLe => v >= min && v < max, - ast::BiGt => v >= min && v < max, - ast::BiGe => v > min && v <= max, - ast::BiEq | ast::BiNe => v >= min && v <= max, - _ => fail!() + fn is_valid(binop: ast::BinOp, v: T, + min: T, max: T) -> bool { + match binop { + ast::BiLt => v > min && v <= max, + ast::BiLe => v >= min && v < max, + ast::BiGt => v >= min && v < max, + ast::BiGe => v > min && v <= max, + ast::BiEq | ast::BiNe => v >= min && v <= max, + _ => fail!() + } } - } - fn rev_binop(binop: ast::BinOp) -> ast::BinOp { - match binop { - ast::BiLt => ast::BiGt, - ast::BiLe => ast::BiGe, - ast::BiGt => ast::BiLt, - ast::BiGe => ast::BiLe, - _ => binop + fn rev_binop(binop: ast::BinOp) -> ast::BinOp { + match binop { + ast::BiLt => ast::BiGt, + ast::BiLe => ast::BiGe, + ast::BiGt => ast::BiLt, + ast::BiGe => ast::BiLe, + _ => binop + } } - } - // for int & uint, be conservative with the warnings, so that the - // warnings are consistent between 32- and 64-bit platforms - fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) { - match int_ty { - ast::TyI => (i64::MIN, i64::MAX), - ast::TyI8 => (i8::MIN as i64, i8::MAX as i64), - ast::TyI16 => (i16::MIN as i64, i16::MAX as i64), - ast::TyI32 => (i32::MIN as i64, i32::MAX as i64), - ast::TyI64 => (i64::MIN, i64::MAX) + // for int & uint, be conservative with the warnings, so that the + // warnings are consistent between 32- and 64-bit platforms + fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) { + match int_ty { + ast::TyI => (i64::MIN, i64::MAX), + ast::TyI8 => (i8::MIN as i64, i8::MAX as i64), + ast::TyI16 => (i16::MIN as i64, i16::MAX as i64), + ast::TyI32 => (i32::MIN as i64, i32::MAX as i64), + ast::TyI64 => (i64::MIN, i64::MAX) + } } - } - fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) { - match uint_ty { - ast::TyU => (u64::MIN, u64::MAX), - ast::TyU8 => (u8::MIN as u64, u8::MAX as u64), - ast::TyU16 => (u16::MIN as u64, u16::MAX as u64), - ast::TyU32 => (u32::MIN as u64, u32::MAX as u64), - ast::TyU64 => (u64::MIN, u64::MAX) + fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) { + match uint_ty { + ast::TyU => (u64::MIN, u64::MAX), + ast::TyU8 => (u8::MIN as u64, u8::MAX as u64), + ast::TyU16 => (u16::MIN as u64, u16::MAX as u64), + ast::TyU32 => (u32::MIN as u64, u32::MAX as u64), + ast::TyU64 => (u64::MIN, u64::MAX) + } } - } - fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp, - l: &ast::Expr, r: &ast::Expr) -> bool { - let (lit, expr, swap) = match (&l.node, &r.node) { - (&ast::ExprLit(_), _) => (l, r, true), - (_, &ast::ExprLit(_)) => (r, l, false), - _ => return true - }; - // Normalize the binop so that the literal is always on the RHS in - // the comparison - let norm_binop = if swap { rev_binop(binop) } else { binop }; - match ty::get(ty::expr_ty(tcx, expr)).sty { - ty::ty_int(int_ty) => { - let (min, max) = int_ty_range(int_ty); - let lit_val: i64 = match lit.node { - ast::ExprLit(li) => match li.node { - ast::LitInt(v, _) => v, - ast::LitUint(v, _) => v as i64, - ast::LitIntUnsuffixed(v) => v, - _ => return true - }, - _ => fail!() - }; - is_valid(norm_binop, lit_val, min, max) - } - ty::ty_uint(uint_ty) => { - let (min, max): (u64, u64) = uint_ty_range(uint_ty); - let lit_val: u64 = match lit.node { - ast::ExprLit(li) => match li.node { - ast::LitInt(v, _) => v as u64, - ast::LitUint(v, _) => v, - ast::LitIntUnsuffixed(v) => v as u64, - _ => return true - }, - _ => fail!() - }; - is_valid(norm_binop, lit_val, min, max) + fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp, + l: &ast::Expr, r: &ast::Expr) -> bool { + let (lit, expr, swap) = match (&l.node, &r.node) { + (&ast::ExprLit(_), _) => (l, r, true), + (_, &ast::ExprLit(_)) => (r, l, false), + _ => return true + }; + // Normalize the binop so that the literal is always on the RHS in + // the comparison + let norm_binop = if swap { rev_binop(binop) } else { binop }; + match ty::get(ty::expr_ty(tcx, expr)).sty { + ty::ty_int(int_ty) => { + let (min, max) = int_ty_range(int_ty); + let lit_val: i64 = match lit.node { + ast::ExprLit(li) => match li.node { + ast::LitInt(v, _) => v, + ast::LitUint(v, _) => v as i64, + ast::LitIntUnsuffixed(v) => v, + _ => return true + }, + _ => fail!() + }; + is_valid(norm_binop, lit_val, min, max) + } + ty::ty_uint(uint_ty) => { + let (min, max): (u64, u64) = uint_ty_range(uint_ty); + let lit_val: u64 = match lit.node { + ast::ExprLit(li) => match li.node { + ast::LitInt(v, _) => v as u64, + ast::LitUint(v, _) => v, + ast::LitIntUnsuffixed(v) => v as u64, + _ => return true + }, + _ => fail!() + }; + is_valid(norm_binop, lit_val, min, max) + } + _ => true } - _ => true } - } - fn is_comparison(binop: ast::BinOp) -> bool { - match binop { - ast::BiEq | ast::BiLt | ast::BiLe | - ast::BiNe | ast::BiGe | ast::BiGt => true, - _ => false + fn is_comparison(binop: ast::BinOp) -> bool { + match binop { + ast::BiEq | ast::BiLt | ast::BiLe | + ast::BiNe | ast::BiGe | ast::BiGt => true, + _ => false + } } } } -pub fn check_item_ctypes(cx: &Context, it: &ast::Item) { - fn check_ty(cx: &Context, ty: &ast::Ty) { - match ty.node { - ast::TyPath(_, _, id) => { - match cx.tcx.def_map.borrow().get_copy(&id) { - def::DefPrimTy(ast::TyInt(ast::TyI)) => { - cx.span_lint(lint::CTypes, ty.span, - "found rust type `int` in foreign module, while \ - libc::c_int or libc::c_long should be used"); - } - def::DefPrimTy(ast::TyUint(ast::TyU)) => { - cx.span_lint(lint::CTypes, ty.span, - "found rust type `uint` in foreign module, while \ - libc::c_uint or libc::c_ulong should be used"); - } - def::DefTy(def_id) => { - if !adt::is_ffi_safe(cx.tcx, def_id) { +#[deriving(Default)] +pub struct CTypes; + +impl LintPass for CTypes { + fn check_item(&mut self, cx: &Context, it: &ast::Item) { + fn check_ty(cx: &Context, ty: &ast::Ty) { + match ty.node { + ast::TyPath(_, _, id) => { + match cx.tcx.def_map.borrow().get_copy(&id) { + def::DefPrimTy(ast::TyInt(ast::TyI)) => { + cx.span_lint(lint::CTypes, ty.span, + "found rust type `int` in foreign module, while \ + libc::c_int or libc::c_long should be used"); + } + def::DefPrimTy(ast::TyUint(ast::TyU)) => { cx.span_lint(lint::CTypes, ty.span, - "found enum type without foreign-function-safe \ - representation annotation in foreign module"); - // hmm... this message could be more helpful + "found rust type `uint` in foreign module, while \ + libc::c_uint or libc::c_ulong should be used"); + } + def::DefTy(def_id) => { + if !adt::is_ffi_safe(cx.tcx, def_id) { + cx.span_lint(lint::CTypes, ty.span, + "found enum type without foreign-function-safe \ + representation annotation in foreign module"); + // hmm... this message could be more helpful + } } + _ => () } - _ => () } + ast::TyPtr(ref mt) => { check_ty(cx, mt.ty) } + _ => {} } - ast::TyPtr(ref mt) => { check_ty(cx, mt.ty) } - _ => {} } - } - fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) { - for input in decl.inputs.iter() { - check_ty(cx, input.ty); + fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) { + for input in decl.inputs.iter() { + check_ty(cx, input.ty); + } + check_ty(cx, decl.output) } - check_ty(cx, decl.output) - } - match it.node { - ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => { - for ni in nmod.items.iter() { - match ni.node { - ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl), - ast::ForeignItemStatic(t, _) => check_ty(cx, t) + match it.node { + ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => { + for ni in nmod.items.iter() { + match ni.node { + ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl), + ast::ForeignItemStatic(t, _) => check_ty(cx, t) + } } + } + _ => {/* nothing to do */ } } - } - _ => {/* nothing to do */ } } } -pub fn check_heap_type(cx: &Context, span: Span, ty: ty::t) { - let xs = [lint::ManagedHeapMemory, lint::OwnedHeapMemory, lint::HeapMemory]; - for &lint in xs.iter() { - if cx.get_level(lint) == lint::Allow { continue } - - 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(_) | - ty::ty_trait(box ty::TyTrait { - store: ty::UniqTraitStore, .. - }) | - ty::ty_closure(box ty::ClosureTy { - store: ty::UniqTraitStore, - .. - }) => { - n_uniq += 1; - } +#[deriving(Default)] +pub struct HeapMemory; + +impl HeapMemory { + fn check_heap_type(&self, cx: &Context, span: Span, ty: ty::t) { + let xs = [lint::ManagedHeapMemory, lint::OwnedHeapMemory, lint::HeapMemory]; + for &lint in xs.iter() { + if cx.get_level(lint) == lint::Allow { continue } + + 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(_) | + ty::ty_trait(box ty::TyTrait { + store: ty::UniqTraitStore, .. + }) | + ty::ty_closure(box ty::ClosureTy { + store: ty::UniqTraitStore, + .. + }) => { + n_uniq += 1; + } - _ => () - }; - t - }); + _ => () + }; + t + }); - if n_uniq > 0 && lint != lint::ManagedHeapMemory { - let s = ty_to_str(cx.tcx, ty); - let m = format!("type uses owned (Box type) pointers: {}", s); - cx.span_lint(lint, span, m.as_slice()); - } + if n_uniq > 0 && lint != lint::ManagedHeapMemory { + let s = ty_to_str(cx.tcx, ty); + let m = format!("type uses owned (Box type) pointers: {}", s); + cx.span_lint(lint, 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()); + 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()); + } } } } -pub fn check_heap_item(cx: &Context, it: &ast::Item) { - match it.node { - ast::ItemFn(..) | - ast::ItemTy(..) | - ast::ItemEnum(..) | - ast::ItemStruct(..) => check_heap_type(cx, it.span, - ty::node_id_to_type(cx.tcx, - it.id)), - _ => () - } +impl LintPass for HeapMemory { + fn check_item(&mut self, cx: &Context, it: &ast::Item) { + match it.node { + ast::ItemFn(..) | + ast::ItemTy(..) | + ast::ItemEnum(..) | + ast::ItemStruct(..) => self.check_heap_type(cx, it.span, + ty::node_id_to_type(cx.tcx, + it.id)), + _ => () + } - // If it's a struct, we also have to check the fields' types - match it.node { - ast::ItemStruct(struct_def, _) => { - for struct_field in struct_def.fields.iter() { - check_heap_type(cx, struct_field.span, - ty::node_id_to_type(cx.tcx, - struct_field.node.id)); + // If it's a struct, we also have to check the fields' types + match it.node { + ast::ItemStruct(struct_def, _) => { + for struct_field in struct_def.fields.iter() { + self.check_heap_type(cx, struct_field.span, + ty::node_id_to_type(cx.tcx, + struct_field.node.id)); + } } + _ => () } - _ => () + } + + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + let ty = ty::expr_ty(cx.tcx, e); + self.check_heap_type(cx, e.span, ty); } } @@ -367,7 +438,7 @@ struct RawPtrDerivingVisitor<'a> { cx: &'a Context<'a> } -impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> { +impl<'a> visit::Visitor<()> for RawPtrDerivingVisitor<'a> { fn visit_ty(&mut self, ty: &ast::Ty, _: ()) { static MSG: &'static str = "use of `#[deriving]` with a raw pointer"; match ty.node { @@ -381,450 +452,608 @@ impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> { fn visit_block(&mut self, _: &ast::Block, _: ()) {} } -pub fn check_raw_ptr_deriving(cx: &mut Context, item: &ast::Item) { - if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") { - return +pub struct RawPointerDeriving { + checked_raw_pointers: NodeSet, +} + +impl Default for RawPointerDeriving { + fn default() -> RawPointerDeriving { + RawPointerDeriving { + checked_raw_pointers: NodeSet::new(), + } } - let did = match item.node { - ast::ItemImpl(..) => { - match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty { - ty::ty_enum(did, _) => did, - ty::ty_struct(did, _) => did, - _ => return, - } +} + +impl LintPass for RawPointerDeriving { + fn check_item(&mut self, cx: &Context, item: &ast::Item) { + if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") { + return } - _ => return, - }; - if !ast_util::is_local(did) { return } - let item = match cx.tcx.map.find(did.node) { - Some(ast_map::NodeItem(item)) => item, - _ => return, - }; - if !cx.checked_raw_pointers.insert(item.id) { return } - match item.node { - ast::ItemStruct(..) | ast::ItemEnum(..) => { - let mut visitor = RawPtrDerivingVisitor { cx: cx }; - visit::walk_item(&mut visitor, item, ()); + let did = match item.node { + ast::ItemImpl(..) => { + match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty { + ty::ty_enum(did, _) => did, + ty::ty_struct(did, _) => did, + _ => return, + } + } + _ => return, + }; + if !ast_util::is_local(did) { return } + let item = match cx.tcx.map.find(did.node) { + Some(ast_map::NodeItem(item)) => item, + _ => return, + }; + if !self.checked_raw_pointers.insert(item.id) { return } + match item.node { + ast::ItemStruct(..) | ast::ItemEnum(..) => { + let mut visitor = RawPtrDerivingVisitor { cx: cx }; + visit::walk_item(&mut visitor, item, ()); + } + _ => {} } - _ => {} } } -pub fn check_unused_attribute(cx: &Context, attr: &ast::Attribute) { - static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [ - // FIXME: #14408 whitelist docs since rustdoc looks at them - "doc", - - // FIXME: #14406 these are processed in trans, which happens after the - // lint pass - "address_insignificant", - "cold", - "inline", - "link", - "link_name", - "link_section", - "no_builtins", - "no_mangle", - "no_split_stack", - "packed", - "static_assert", - "thread_local", - - // not used anywhere (!?) but apparently we want to keep them around - "comment", - "desc", - "license", - - // FIXME: #14407 these are only looked at on-demand so we can't - // guarantee they'll have already been checked - "deprecated", - "experimental", - "frozen", - "locked", - "must_use", - "stable", - "unstable", - ]; - - static CRATE_ATTRS: &'static [&'static str] = &'static [ - "crate_type", - "feature", - "no_start", - "no_main", - "no_std", - "crate_id", - "desc", - "comment", - "license", - "copyright", - "no_builtins", - ]; - - for &name in ATTRIBUTE_WHITELIST.iter() { - if attr.check_name(name) { - break; +#[deriving(Default)] +pub struct UnusedAttribute; + +impl LintPass for UnusedAttribute { + fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) { + static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [ + // FIXME: #14408 whitelist docs since rustdoc looks at them + "doc", + + // FIXME: #14406 these are processed in trans, which happens after the + // lint pass + "address_insignificant", + "cold", + "inline", + "link", + "link_name", + "link_section", + "no_builtins", + "no_mangle", + "no_split_stack", + "packed", + "static_assert", + "thread_local", + + // not used anywhere (!?) but apparently we want to keep them around + "comment", + "desc", + "license", + + // FIXME: #14407 these are only looked at on-demand so we can't + // guarantee they'll have already been checked + "deprecated", + "experimental", + "frozen", + "locked", + "must_use", + "stable", + "unstable", + ]; + + static CRATE_ATTRS: &'static [&'static str] = &'static [ + "crate_type", + "feature", + "no_start", + "no_main", + "no_std", + "crate_id", + "desc", + "comment", + "license", + "copyright", + "no_builtins", + ]; + + for &name in ATTRIBUTE_WHITELIST.iter() { + if attr.check_name(name) { + break; + } } - } - if !attr::is_used(attr) { - cx.span_lint(lint::UnusedAttribute, attr.span, "unused attribute"); - if CRATE_ATTRS.contains(&attr.name().get()) { - let msg = match attr.node.style { - ast::AttrOuter => "crate-level attribute should be an inner \ - attribute: add an exclamation mark: #![foo]", - ast::AttrInner => "crate-level attribute should be in the \ - root module", - }; - cx.span_lint(lint::UnusedAttribute, attr.span, msg); + if !attr::is_used(attr) { + cx.span_lint(lint::UnusedAttribute, attr.span, "unused attribute"); + if CRATE_ATTRS.contains(&attr.name().get()) { + let msg = match attr.node.style { + ast::AttrOuter => "crate-level attribute should be an inner \ + attribute: add an exclamation mark: #![foo]", + ast::AttrInner => "crate-level attribute should be in the \ + root module", + }; + cx.span_lint(lint::UnusedAttribute, attr.span, msg); + } } } } -pub fn check_heap_expr(cx: &Context, e: &ast::Expr) { - let ty = ty::expr_ty(cx.tcx, e); - check_heap_type(cx, e.span, ty); -} - -pub fn check_path_statement(cx: &Context, s: &ast::Stmt) { - match s.node { - ast::StmtSemi(expr, _) => { - match expr.node { - ast::ExprPath(_) => { - cx.span_lint(lint::PathStatement, - s.span, - "path statement with no effect"); +#[deriving(Default)] +pub struct PathStatement; + +impl LintPass for PathStatement { + fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { + match s.node { + ast::StmtSemi(expr, _) => { + match expr.node { + ast::ExprPath(_) => { + cx.span_lint(lint::PathStatement, + s.span, + "path statement with no effect"); + } + _ => {} } - _ => {} } + _ => () } - _ => () } } -pub fn check_unused_result(cx: &Context, s: &ast::Stmt) { - let expr = match s.node { - ast::StmtSemi(expr, _) => expr, - _ => return - }; - let t = ty::expr_ty(cx.tcx, expr); - match ty::get(t).sty { - ty::ty_nil | ty::ty_bot | ty::ty_bool => return, - _ => {} - } - match expr.node { - ast::ExprRet(..) => return, - _ => {} - } +#[deriving(Default)] +pub struct UnusedMustUse; + +impl LintPass for UnusedMustUse { + fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { + let expr = match s.node { + ast::StmtSemi(expr, _) => expr, + _ => return + }; + let t = ty::expr_ty(cx.tcx, expr); + match ty::get(t).sty { + ty::ty_nil | ty::ty_bot | ty::ty_bool => return, + _ => {} + } + match expr.node { + ast::ExprRet(..) => return, + _ => {} + } - let t = ty::expr_ty(cx.tcx, expr); - let mut warned = false; - match ty::get(t).sty { - ty::ty_struct(did, _) | - ty::ty_enum(did, _) => { - if ast_util::is_local(did) { - match cx.tcx.map.get(did.node) { - ast_map::NodeItem(it) => { - if attr::contains_name(it.attrs.as_slice(), - "must_use") { + let t = ty::expr_ty(cx.tcx, expr); + let mut warned = false; + match ty::get(t).sty { + ty::ty_struct(did, _) | + ty::ty_enum(did, _) => { + if ast_util::is_local(did) { + match cx.tcx.map.get(did.node) { + ast_map::NodeItem(it) => { + if attr::contains_name(it.attrs.as_slice(), + "must_use") { + cx.span_lint(lint::UnusedMustUse, s.span, + "unused result which must be used"); + warned = true; + } + } + _ => {} + } + } else { + csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| { + if attr::contains_name(attrs.as_slice(), "must_use") { cx.span_lint(lint::UnusedMustUse, s.span, "unused result which must be used"); warned = true; } - } - _ => {} + }); } - } else { - csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| { - if attr::contains_name(attrs.as_slice(), "must_use") { - cx.span_lint(lint::UnusedMustUse, s.span, - "unused result which must be used"); - warned = true; - } - }); } + _ => {} + } + if !warned { + cx.span_lint(lint::UnusedResult, s.span, "unused result"); } - _ => {} - } - if !warned { - cx.span_lint(lint::UnusedResult, s.span, "unused result"); } } -pub fn check_deprecated_owned_vector(cx: &Context, e: &ast::Expr) { - let t = ty::expr_ty(cx.tcx, e); - match ty::get(t).sty { - ty::ty_uniq(t) => match ty::get(t).sty { - ty::ty_vec(_, None) => { - cx.span_lint(lint::DeprecatedOwnedVector, e.span, - "use of deprecated `~[]` vector; replaced by `std::vec::Vec`") - } +#[deriving(Default)] +pub struct DeprecatedOwnedVector; + +impl LintPass for DeprecatedOwnedVector { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + let t = ty::expr_ty(cx.tcx, e); + match ty::get(t).sty { + ty::ty_uniq(t) => match ty::get(t).sty { + ty::ty_vec(_, None) => { + cx.span_lint(lint::DeprecatedOwnedVector, e.span, + "use of deprecated `~[]` vector; replaced by `std::vec::Vec`") + } + _ => {} + }, _ => {} - }, - _ => {} + } } } -pub fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) { - fn is_camel_case(ident: ast::Ident) -> bool { - let ident = token::get_ident(ident); - assert!(!ident.get().is_empty()); - let ident = ident.get().trim_chars('_'); - - // start with a non-lowercase letter rather than non-uppercase - // ones (some scripts don't have a concept of upper/lowercase) - !ident.char_at(0).is_lowercase() && !ident.contains_char('_') - } +#[deriving(Default)] +pub struct NonCamelCaseTypes; - fn to_camel_case(s: &str) -> String { - s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)| - if i == 0 { c.to_uppercase() } - else { c } - )).collect() - } +impl LintPass for NonCamelCaseTypes { + fn check_item(&mut self, cx: &Context, it: &ast::Item) { + fn is_camel_case(ident: ast::Ident) -> bool { + let ident = token::get_ident(ident); + assert!(!ident.get().is_empty()); + let ident = ident.get().trim_chars('_'); - fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) { - let s = token::get_ident(ident); - - if !is_camel_case(ident) { - cx.span_lint(lint:: - NonCamelCaseTypes, span, - format!("{} `{}` should have a camel case name such as `{}`", - sort, s, to_camel_case(s.get())).as_slice()); + // start with a non-lowercase letter rather than non-uppercase + // ones (some scripts don't have a concept of upper/lowercase) + !ident.char_at(0).is_lowercase() && !ident.contains_char('_') } - } - match it.node { - ast::ItemTy(..) | ast::ItemStruct(..) => { - check_case(cx, "type", it.ident, it.span) + fn to_camel_case(s: &str) -> String { + s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)| + if i == 0 { c.to_uppercase() } + else { c } + )).collect() } - ast::ItemTrait(..) => { - check_case(cx, "trait", it.ident, it.span) + + fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) { + let s = token::get_ident(ident); + + if !is_camel_case(ident) { + cx.span_lint(lint:: + NonCamelCaseTypes, span, + format!("{} `{}` should have a camel case name such as `{}`", + sort, s, to_camel_case(s.get())).as_slice()); + } } - ast::ItemEnum(ref enum_definition, _) => { - check_case(cx, "type", it.ident, it.span); - for variant in enum_definition.variants.iter() { - check_case(cx, "variant", variant.node.name, variant.span); + + match it.node { + ast::ItemTy(..) | ast::ItemStruct(..) => { + check_case(cx, "type", it.ident, it.span) + } + ast::ItemTrait(..) => { + check_case(cx, "trait", it.ident, it.span) } + ast::ItemEnum(ref enum_definition, _) => { + check_case(cx, "type", it.ident, it.span); + for variant in enum_definition.variants.iter() { + check_case(cx, "variant", variant.node.name, variant.span); + } + } + _ => () } - _ => () } } -pub fn check_snake_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) { - fn is_snake_case(ident: ast::Ident) -> bool { - let ident = token::get_ident(ident); - assert!(!ident.get().is_empty()); - let ident = ident.get().trim_chars('_'); - - let mut allow_underscore = true; - ident.chars().all(|c| { - allow_underscore = match c { - c if c.is_lowercase() || c.is_digit() => true, - '_' if allow_underscore => false, - _ => return false, - }; - true - }) +#[deriving(PartialEq)] +enum MethodContext { + TraitDefaultImpl, + TraitImpl, + PlainImpl +} + +fn method_context(cx: &Context, m: &ast::Method) -> MethodContext { + let did = ast::DefId { + krate: ast::LOCAL_CRATE, + node: m.id + }; + + match cx.tcx.methods.borrow().find_copy(&did) { + None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"), + Some(md) => { + match md.container { + ty::TraitContainer(..) => TraitDefaultImpl, + ty::ImplContainer(cid) => { + match ty::impl_trait_ref(cx.tcx, cid) { + Some(..) => TraitImpl, + None => PlainImpl + } + } + } + } } +} + +#[deriving(Default)] +pub struct NonSnakeCaseFunctions; + +impl NonSnakeCaseFunctions { + fn check_snake_case(&self, cx: &Context, sort: &str, ident: ast::Ident, span: Span) { + fn is_snake_case(ident: ast::Ident) -> bool { + let ident = token::get_ident(ident); + assert!(!ident.get().is_empty()); + let ident = ident.get().trim_chars('_'); + + let mut allow_underscore = true; + ident.chars().all(|c| { + allow_underscore = match c { + c if c.is_lowercase() || c.is_digit() => true, + '_' if allow_underscore => false, + _ => return false, + }; + true + }) + } - fn to_snake_case(str: &str) -> String { - let mut words = vec![]; - for s in str.split('_') { - let mut buf = String::new(); - if s.is_empty() { continue; } - for ch in s.chars() { - if !buf.is_empty() && ch.is_uppercase() { - words.push(buf); - buf = String::new(); + fn to_snake_case(str: &str) -> String { + let mut words = vec![]; + for s in str.split('_') { + let mut buf = String::new(); + if s.is_empty() { continue; } + for ch in s.chars() { + if !buf.is_empty() && ch.is_uppercase() { + words.push(buf); + buf = String::new(); + } + buf.push_char(ch.to_lowercase()); } - buf.push_char(ch.to_lowercase()); + words.push(buf); } - words.push(buf); + words.connect("_") + } + + let s = token::get_ident(ident); + + if !is_snake_case(ident) { + cx.span_lint(lint::NonSnakeCaseFunctions, span, + format!("{} `{}` should have a snake case name such as `{}`", + sort, s, to_snake_case(s.get())).as_slice()); } - words.connect("_") } +} - let s = token::get_ident(ident); +impl LintPass for NonSnakeCaseFunctions { + fn check_fn(&mut self, cx: &Context, + fk: &visit::FnKind, _: &ast::FnDecl, + _: &ast::Block, span: Span, _: ast::NodeId) { + match *fk { + visit::FkMethod(ident, _, m) => match method_context(cx, m) { + PlainImpl + => self.check_snake_case(cx, "method", ident, span), + TraitDefaultImpl + => self.check_snake_case(cx, "trait method", ident, span), + _ => (), + }, + visit::FkItemFn(ident, _, _, _) + => self.check_snake_case(cx, "function", ident, span), + _ => (), + } + } - if !is_snake_case(ident) { - cx.span_lint(lint::NonSnakeCaseFunctions, span, - format!("{} `{}` should have a snake case name such as `{}`", - sort, s, to_snake_case(s.get())).as_slice()); + fn check_ty_method(&mut self, cx: &Context, t: &ast::TypeMethod) { + self.check_snake_case(cx, "trait method", t.ident, t.span); } } -pub fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) { - match it.node { - // only check static constants - ast::ItemStatic(_, ast::MutImmutable, _) => { - let s = token::get_ident(it.ident); - // check for lowercase letters rather than non-uppercase - // ones (some scripts don't have a concept of - // upper/lowercase) - if s.get().chars().any(|c| c.is_lowercase()) { - cx.span_lint(lint::NonUppercaseStatics, it.span, - format!("static constant `{}` should have an uppercase name \ - such as `{}`", s.get(), - s.get().chars().map(|c| c.to_uppercase()) - .collect::().as_slice()).as_slice()); +#[deriving(Default)] +pub struct NonUppercaseStatics; + +impl LintPass for NonUppercaseStatics { + fn check_item(&mut self, cx: &Context, it: &ast::Item) { + match it.node { + // only check static constants + ast::ItemStatic(_, ast::MutImmutable, _) => { + let s = token::get_ident(it.ident); + // check for lowercase letters rather than non-uppercase + // ones (some scripts don't have a concept of + // upper/lowercase) + if s.get().chars().any(|c| c.is_lowercase()) { + cx.span_lint(lint::NonUppercaseStatics, it.span, + format!("static constant `{}` should have an uppercase name \ + such as `{}`", s.get(), + s.get().chars().map(|c| c.to_uppercase()) + .collect::().as_slice()).as_slice()); + } } + _ => {} } - _ => {} } -} -pub fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) { - // Lint for constants that look like binding identifiers (#7526) - match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) { - (&ast::PatIdent(_, ref path, _), Some(&def::DefStatic(_, false))) => { - // last identifier alone is right choice for this lint. - let ident = path.segments.last().unwrap().identifier; - let s = token::get_ident(ident); - if s.get().chars().any(|c| c.is_lowercase()) { - cx.span_lint(lint::NonUppercasePatternStatics, path.span, - format!("static constant in pattern `{}` should have an uppercase \ - name such as `{}`", s.get(), - s.get().chars().map(|c| c.to_uppercase()) - .collect::().as_slice()).as_slice()); + fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { + // Lint for constants that look like binding identifiers (#7526) + match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) { + (&ast::PatIdent(_, ref path, _), Some(&def::DefStatic(_, false))) => { + // last identifier alone is right choice for this lint. + let ident = path.segments.last().unwrap().identifier; + let s = token::get_ident(ident); + if s.get().chars().any(|c| c.is_lowercase()) { + cx.span_lint(lint::NonUppercasePatternStatics, path.span, + format!("static constant in pattern `{}` should have an uppercase \ + name such as `{}`", s.get(), + s.get().chars().map(|c| c.to_uppercase()) + .collect::().as_slice()).as_slice()); + } } + _ => {} } - _ => {} } } -pub fn check_pat_uppercase_variable(cx: &Context, p: &ast::Pat) { - match &p.node { - &ast::PatIdent(_, ref path, _) => { - match cx.tcx.def_map.borrow().find(&p.id) { - Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) | - Some(&def::DefArg(_, _)) => { - // last identifier alone is right choice for this lint. - let ident = path.segments.last().unwrap().identifier; - let s = token::get_ident(ident); - if s.get().len() > 0 && s.get().char_at(0).is_uppercase() { - cx.span_lint(lint:: - UppercaseVariables, - path.span, - "variable names should start with a lowercase character"); +#[deriving(Default)] +pub struct UppercaseVariables; + +impl LintPass for UppercaseVariables { + fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { + match &p.node { + &ast::PatIdent(_, ref path, _) => { + match cx.tcx.def_map.borrow().find(&p.id) { + Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) | + Some(&def::DefArg(_, _)) => { + // last identifier alone is right choice for this lint. + let ident = path.segments.last().unwrap().identifier; + let s = token::get_ident(ident); + if s.get().len() > 0 && s.get().char_at(0).is_uppercase() { + cx.span_lint(lint:: + UppercaseVariables, + path.span, + "variable names should start with a lowercase character"); + } } + _ => {} } - _ => {} } + _ => {} } - _ => {} } -} -pub fn check_struct_uppercase_variable(cx: &Context, s: &ast::StructDef) { - for sf in s.fields.iter() { - match sf.node { - ast::StructField_ { kind: ast::NamedField(ident, _), .. } => { - let s = token::get_ident(ident); - if s.get().char_at(0).is_uppercase() { - cx.span_lint(lint:: - UppercaseVariables, - sf.span, - "structure field names should start with a lowercase character"); + fn check_struct_def(&mut self, cx: &Context, s: &ast::StructDef, + _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { + for sf in s.fields.iter() { + match sf.node { + ast::StructField_ { kind: ast::NamedField(ident, _), .. } => { + let s = token::get_ident(ident); + if s.get().char_at(0).is_uppercase() { + cx.span_lint(lint::UppercaseVariables, + sf.span, + "structure field names should start with a lowercase character"); + } } + _ => {} } - _ => {} } } } -pub fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) { - match value.node { - ast::ExprParen(_) => { - cx.span_lint(lint::UnnecessaryParens, value.span, - format!("unnecessary parentheses around {}", - msg).as_slice()) +#[deriving(Default)] +pub struct UnnecessaryParens; + +impl UnnecessaryParens { + fn check_unnecessary_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str) { + match value.node { + ast::ExprParen(_) => { + cx.span_lint(lint::UnnecessaryParens, value.span, + format!("unnecessary parentheses around {}", + msg).as_slice()) + } + _ => {} } - _ => {} } } -pub fn check_unnecessary_parens_expr(cx: &Context, e: &ast::Expr) { - let (value, msg) = match e.node { - ast::ExprIf(cond, _, _) => (cond, "`if` condition"), - ast::ExprWhile(cond, _) => (cond, "`while` condition"), - ast::ExprMatch(head, _) => (head, "`match` head expression"), - ast::ExprRet(Some(value)) => (value, "`return` value"), - ast::ExprAssign(_, value) => (value, "assigned value"), - ast::ExprAssignOp(_, _, value) => (value, "assigned value"), - _ => return - }; - check_unnecessary_parens_core(cx, value, msg); -} +impl LintPass for UnnecessaryParens { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + let (value, msg) = match e.node { + ast::ExprIf(cond, _, _) => (cond, "`if` condition"), + ast::ExprWhile(cond, _) => (cond, "`while` condition"), + ast::ExprMatch(head, _) => (head, "`match` head expression"), + ast::ExprRet(Some(value)) => (value, "`return` value"), + ast::ExprAssign(_, value) => (value, "assigned value"), + ast::ExprAssignOp(_, _, value) => (value, "assigned value"), + _ => return + }; + self.check_unnecessary_parens_core(cx, value, msg); + } -pub fn check_unnecessary_parens_stmt(cx: &Context, s: &ast::Stmt) { - let (value, msg) = match s.node { - ast::StmtDecl(decl, _) => match decl.node { - ast::DeclLocal(local) => match local.init { - Some(value) => (value, "assigned value"), - None => return + fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { + let (value, msg) = match s.node { + ast::StmtDecl(decl, _) => match decl.node { + ast::DeclLocal(local) => match local.init { + Some(value) => (value, "assigned value"), + None => return + }, + _ => return }, _ => return - }, - _ => return - }; - check_unnecessary_parens_core(cx, value, msg); + }; + self.check_unnecessary_parens_core(cx, value, msg); + } } -pub fn check_unused_unsafe(cx: &Context, e: &ast::Expr) { - match e.node { - // Don't warn about generated blocks, that'll just pollute the output. - ast::ExprBlock(ref blk) => { - if blk.rules == ast::UnsafeBlock(ast::UserProvided) && - !cx.tcx.used_unsafe.borrow().contains(&blk.id) { - cx.span_lint(lint::UnusedUnsafe, blk.span, - "unnecessary `unsafe` block"); +#[deriving(Default)] +pub struct UnusedUnsafe; + +impl LintPass for UnusedUnsafe { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + match e.node { + // Don't warn about generated blocks, that'll just pollute the output. + ast::ExprBlock(ref blk) => { + if blk.rules == ast::UnsafeBlock(ast::UserProvided) && + !cx.tcx.used_unsafe.borrow().contains(&blk.id) { + cx.span_lint(lint::UnusedUnsafe, blk.span, + "unnecessary `unsafe` block"); + } } + _ => () } - _ => () } } -pub fn check_unsafe_block(cx: &Context, e: &ast::Expr) { - match e.node { - // Don't warn about generated blocks, that'll just pollute the output. - ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => { - cx.span_lint(lint::UnsafeBlock, blk.span, "usage of an `unsafe` block"); +#[deriving(Default)] +pub struct UnsafeBlock; + +impl LintPass for UnsafeBlock { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + match e.node { + // Don't warn about generated blocks, that'll just pollute the output. + ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => { + cx.span_lint(lint::UnsafeBlock, blk.span, "usage of an `unsafe` block"); + } + _ => () } - _ => () } } -pub fn check_unused_mut_pat(cx: &Context, pats: &[@ast::Pat]) { - // collect all mutable pattern and group their NodeIDs by their Identifier to - // avoid false warnings in match arms with multiple patterns - let mut mutables = HashMap::new(); - for &p in pats.iter() { - pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path| { - match mode { - ast::BindByValue(ast::MutMutable) => { - if path.segments.len() != 1 { - cx.tcx.sess.span_bug(p.span, - "mutable binding that doesn't consist \ - of exactly one segment"); +#[deriving(Default)] +pub struct UnusedMut; + +impl UnusedMut { + fn check_unused_mut_pat(&self, cx: &Context, pats: &[@ast::Pat]) { + // collect all mutable pattern and group their NodeIDs by their Identifier to + // avoid false warnings in match arms with multiple patterns + let mut mutables = HashMap::new(); + for &p in pats.iter() { + pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path| { + match mode { + ast::BindByValue(ast::MutMutable) => { + if path.segments.len() != 1 { + cx.tcx.sess.span_bug(p.span, + "mutable binding that doesn't consist \ + of exactly one segment"); + } + let ident = path.segments.get(0).identifier; + if !token::get_ident(ident).get().starts_with("_") { + mutables.insert_or_update_with(ident.name as uint, vec!(id), |_, old| { + old.push(id); + }); + } } - let ident = path.segments.get(0).identifier; - if !token::get_ident(ident).get().starts_with("_") { - mutables.insert_or_update_with(ident.name as uint, vec!(id), |_, old| { - old.push(id); - }); + _ => { } } - _ => { + }); + } + + let used_mutables = cx.tcx.used_mut_nodes.borrow(); + for (_, v) in mutables.iter() { + if !v.iter().any(|e| used_mutables.contains(e)) { + cx.span_lint(lint::UnusedMut, cx.tcx.map.span(*v.get(0)), + "variable does not need to be mutable"); + } + } + } +} + +impl LintPass for UnusedMut { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + match e.node { + ast::ExprMatch(_, ref arms) => { + for a in arms.iter() { + self.check_unused_mut_pat(cx, a.pats.as_slice()) } } - }); + _ => {} + } + } + + fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { + match s.node { + ast::StmtDecl(d, _) => { + match d.node { + ast::DeclLocal(l) => { + self.check_unused_mut_pat(cx, &[l.pat]); + }, + _ => {} + } + }, + _ => {} + } } - let used_mutables = cx.tcx.used_mut_nodes.borrow(); - for (_, v) in mutables.iter() { - if !v.iter().any(|e| used_mutables.contains(e)) { - cx.span_lint(lint::UnusedMut, cx.tcx.map.span(*v.get(0)), - "variable does not need to be mutable"); + fn check_fn(&mut self, cx: &Context, + _: &visit::FnKind, decl: &ast::FnDecl, + _: &ast::Block, _: Span, _: ast::NodeId) { + for a in decl.inputs.iter() { + self.check_unused_mut_pat(cx, &[a.pat]); } } } @@ -834,248 +1063,302 @@ enum Allocation { BoxAllocation } -pub fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) { - // Warn if string and vector literals with sigils, or boxing expressions, - // are immediately borrowed. - let allocation = match e.node { - ast::ExprVstore(e2, ast::ExprVstoreUniq) => { - match e2.node { - ast::ExprLit(lit) if ast_util::lit_is_str(lit) => { - VectorAllocation +#[deriving(Default)] +pub struct UnnecessaryAllocation; + +impl LintPass for UnnecessaryAllocation { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + // Warn if string and vector literals with sigils, or boxing expressions, + // are immediately borrowed. + let allocation = match e.node { + ast::ExprVstore(e2, ast::ExprVstoreUniq) => { + match e2.node { + ast::ExprLit(lit) if ast_util::lit_is_str(lit) => { + VectorAllocation + } + ast::ExprVec(..) => VectorAllocation, + _ => return } - ast::ExprVec(..) => VectorAllocation, - _ => return } - } - ast::ExprUnary(ast::UnUniq, _) | - ast::ExprUnary(ast::UnBox, _) => BoxAllocation, + ast::ExprUnary(ast::UnUniq, _) | + ast::ExprUnary(ast::UnBox, _) => BoxAllocation, - _ => return - }; + _ => return + }; - let report = |msg| { - cx.span_lint(lint::UnnecessaryAllocation, e.span, msg); - }; + let report = |msg| { + cx.span_lint(lint::UnnecessaryAllocation, e.span, msg); + }; - match cx.tcx.adjustments.borrow().find(&e.id) { - Some(adjustment) => { - match *adjustment { - ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { - match (allocation, autoref) { - (VectorAllocation, Some(ty::AutoBorrowVec(..))) => { - report("unnecessary allocation, the sigil can be \ - removed"); - } - (BoxAllocation, - Some(ty::AutoPtr(_, ast::MutImmutable))) => { - report("unnecessary allocation, use & instead"); + match cx.tcx.adjustments.borrow().find(&e.id) { + Some(adjustment) => { + match *adjustment { + ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { + match (allocation, autoref) { + (VectorAllocation, Some(ty::AutoBorrowVec(..))) => { + report("unnecessary allocation, the sigil can be \ + removed"); + } + (BoxAllocation, + Some(ty::AutoPtr(_, ast::MutImmutable))) => { + report("unnecessary allocation, use & instead"); + } + (BoxAllocation, + Some(ty::AutoPtr(_, ast::MutMutable))) => { + report("unnecessary allocation, use &mut \ + instead"); + } + _ => () } - (BoxAllocation, - Some(ty::AutoPtr(_, ast::MutMutable))) => { - report("unnecessary allocation, use &mut \ - instead"); - } - _ => () } + _ => {} } - _ => {} } + _ => () } + } +} + +pub struct MissingDoc { + /// Set of nodes exported from this module. + exported_items: Option, + + /// Stack of IDs of struct definitions. + struct_def_stack: Vec, + + /// Stack of whether #[doc(hidden)] is set + /// at each level which has lint attributes. + doc_hidden_stack: Vec, +} - _ => () +impl Default for MissingDoc { + fn default() -> MissingDoc { + MissingDoc { + exported_items: None, + struct_def_stack: vec!(), + doc_hidden_stack: vec!(false), + } } } -pub fn check_missing_doc_attrs(cx: &Context, - id: Option, - attrs: &[ast::Attribute], - sp: Span, - desc: &'static str) { - // If we're building a test harness, then warning about - // documentation is probably not really relevant right now. - if cx.tcx.sess.opts.test { return } - - // `#[doc(hidden)]` disables missing_doc check. - if cx.is_doc_hidden { return } - - // Only check publicly-visible items, using the result from the privacy pass. It's an option so - // the crate root can also use this function (it doesn't have a NodeId). - match id { - Some(ref id) if !cx.exported_items.contains(id) => return, - _ => () +impl MissingDoc { + fn doc_hidden(&self) -> bool { + *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") } - let has_doc = attrs.iter().any(|a| { - match a.node.value.node { - ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true, - _ => false + fn check_missing_doc_attrs(&self, + cx: &Context, + id: Option, + attrs: &[ast::Attribute], + sp: Span, + desc: &'static str) { + // If we're building a test harness, then warning about + // documentation is probably not really relevant right now. + if cx.tcx.sess.opts.test { return } + + // `#[doc(hidden)]` disables missing_doc check. + if self.doc_hidden() { return } + + // Only check publicly-visible items, using the result from the privacy pass. + // It's an option so the crate root can also use this function (it doesn't + // have a NodeId). + let exported = self.exported_items.as_ref().expect("exported_items not set"); + match id { + Some(ref id) if !exported.contains(id) => return, + _ => () + } + + let has_doc = attrs.iter().any(|a| { + match a.node.value.node { + ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true, + _ => false + } + }); + if !has_doc { + cx.span_lint(lint::MissingDoc, + sp, + format!("missing documentation for {}", + desc).as_slice()); } - }); - if !has_doc { - cx.span_lint(lint::MissingDoc, - sp, - format!("missing documentation for {}", - desc).as_slice()); } } -pub fn check_missing_doc_item(cx: &Context, it: &ast::Item) { - let desc = match it.node { - ast::ItemFn(..) => "a function", - ast::ItemMod(..) => "a module", - ast::ItemEnum(..) => "an enum", - ast::ItemStruct(..) => "a struct", - ast::ItemTrait(..) => "a trait", - _ => return - }; - check_missing_doc_attrs(cx, - Some(it.id), - it.attrs.as_slice(), - it.span, - desc); -} +impl LintPass for MissingDoc { + fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) { + let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| { + attr.check_name("doc") && match attr.meta_item_list() { + None => false, + Some(l) => attr::contains_name(l.as_slice(), "hidden"), + } + }); + self.doc_hidden_stack.push(doc_hidden); + } -pub fn check_missing_doc_method(cx: &Context, m: &ast::Method) { - // If the method is an impl for a trait, don't doc. - if lint::method_context(cx, m) == lint::TraitImpl { return; } - - // Otherwise, doc according to privacy. This will also check - // doc for default methods defined on traits. - check_missing_doc_attrs(cx, - Some(m.id), - m.attrs.as_slice(), - m.span, - "a method"); -} + fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { + self.doc_hidden_stack.pop().expect("empty doc_hidden_stack"); + } -pub fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) { - check_missing_doc_attrs(cx, - Some(tm.id), - tm.attrs.as_slice(), - tm.span, - "a type method"); -} + fn check_struct_def(&mut self, _: &Context, + _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, id: ast::NodeId) { + self.struct_def_stack.push(id); + } -pub fn check_missing_doc_struct_field(cx: &Context, sf: &ast::StructField) { - match sf.node.kind { - ast::NamedField(_, vis) if vis == ast::Public => - check_missing_doc_attrs(cx, - Some(cx.cur_struct_def_id), - sf.node.attrs.as_slice(), - sf.span, - "a struct field"), - _ => {} + fn check_struct_def_post(&mut self, _: &Context, + _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, id: ast::NodeId) { + let popped = self.struct_def_stack.pop().expect("empty struct_def_stack"); + assert!(popped == id); } -} -pub fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) { - check_missing_doc_attrs(cx, - Some(v.node.id), - v.node.attrs.as_slice(), - v.span, - "a variant"); + fn check_crate(&mut self, cx: &Context, exported: &ExportedItems, krate: &ast::Crate) { + // FIXME: clone to avoid lifetime trickiness + self.exported_items = Some(exported.clone()); + + self.check_missing_doc_attrs(cx, None, krate.attrs.as_slice(), + krate.span, "crate"); + } + + fn check_item(&mut self, cx: &Context, it: &ast::Item) { + let desc = match it.node { + ast::ItemFn(..) => "a function", + ast::ItemMod(..) => "a module", + ast::ItemEnum(..) => "an enum", + ast::ItemStruct(..) => "a struct", + ast::ItemTrait(..) => "a trait", + _ => return + }; + self.check_missing_doc_attrs(cx, Some(it.id), it.attrs.as_slice(), + it.span, desc); + } + + fn check_fn(&mut self, cx: &Context, + fk: &visit::FnKind, _: &ast::FnDecl, _: &ast::Block, _: Span, _: ast::NodeId) { + match *fk { + visit::FkMethod(_, _, m) => { + // If the method is an impl for a trait, don't doc. + if method_context(cx, m) == TraitImpl { return; } + + // Otherwise, doc according to privacy. This will also check + // doc for default methods defined on traits. + self.check_missing_doc_attrs(cx, Some(m.id), m.attrs.as_slice(), + m.span, "a method"); + } + _ => {} + } + } + + fn check_ty_method(&mut self, cx: &Context, tm: &ast::TypeMethod) { + self.check_missing_doc_attrs(cx, Some(tm.id), tm.attrs.as_slice(), + tm.span, "a type method"); + } + + fn check_struct_field(&mut self, cx: &Context, sf: &ast::StructField) { + match sf.node.kind { + ast::NamedField(_, vis) if vis == ast::Public => { + let cur_struct_def = *self.struct_def_stack.last().expect("empty struct_def_stack"); + self.check_missing_doc_attrs(cx, Some(cur_struct_def), + sf.node.attrs.as_slice(), sf.span, "a struct field") + } + _ => {} + } + } + + fn check_variant(&mut self, cx: &Context, v: &ast::Variant, _: &ast::Generics) { + self.check_missing_doc_attrs(cx, Some(v.node.id), v.node.attrs.as_slice(), + v.span, "a variant"); + } } /// Checks for use of items with #[deprecated], #[experimental] and /// #[unstable] (or none of them) attributes. -pub fn check_stability(cx: &Context, e: &ast::Expr) { - let id = match e.node { - ast::ExprPath(..) | ast::ExprStruct(..) => { - match cx.tcx.def_map.borrow().find(&e.id) { - Some(&def) => def.def_id(), - None => return +#[deriving(Default)] +pub struct Stability; + +impl LintPass for Stability { + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { + let id = match e.node { + ast::ExprPath(..) | ast::ExprStruct(..) => { + match cx.tcx.def_map.borrow().find(&e.id) { + Some(&def) => def.def_id(), + None => return + } } - } - ast::ExprMethodCall(..) => { - let method_call = typeck::MethodCall::expr(e.id); - match cx.tcx.method_map.borrow().find(&method_call) { - Some(method) => { - match method.origin { - typeck::MethodStatic(def_id) => { - // If this implements a trait method, get def_id - // of the method inside trait definition. - // Otherwise, use the current def_id (which refers - // to the method inside impl). - ty::trait_method_of_method( - cx.tcx, def_id).unwrap_or(def_id) + ast::ExprMethodCall(..) => { + let method_call = typeck::MethodCall::expr(e.id); + match cx.tcx.method_map.borrow().find(&method_call) { + Some(method) => { + match method.origin { + typeck::MethodStatic(def_id) => { + // If this implements a trait method, get def_id + // of the method inside trait definition. + // Otherwise, use the current def_id (which refers + // to the method inside impl). + ty::trait_method_of_method( + cx.tcx, def_id).unwrap_or(def_id) + } + typeck::MethodParam(typeck::MethodParam { + trait_id: trait_id, + method_num: index, + .. + }) + | typeck::MethodObject(typeck::MethodObject { + trait_id: trait_id, + method_num: index, + .. + }) => ty::trait_method(cx.tcx, trait_id, index).def_id } - typeck::MethodParam(typeck::MethodParam { - trait_id: trait_id, - method_num: index, - .. - }) - | typeck::MethodObject(typeck::MethodObject { - trait_id: trait_id, - method_num: index, - .. - }) => ty::trait_method(cx.tcx, trait_id, index).def_id } + None => return } - None => return } - } - _ => return - }; - - let stability = if ast_util::is_local(id) { - // this crate - let s = cx.tcx.map.with_attrs(id.node, |attrs| { - attrs.map(|a| attr::find_stability(a.as_slice())) - }); - match s { - Some(s) => s, + _ => return + }; - // no possibility of having attributes - // (e.g. it's a local variable), so just - // ignore it. - None => return - } - } else { - // cross-crate - - let mut s = None; - // run through all the attributes and take the first - // stability one. - csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| { - if s.is_none() { - s = attr::find_stability(attrs.as_slice()) + let stability = if ast_util::is_local(id) { + // this crate + let s = cx.tcx.map.with_attrs(id.node, |attrs| { + attrs.map(|a| attr::find_stability(a.as_slice())) + }); + match s { + Some(s) => s, + + // no possibility of having attributes + // (e.g. it's a local variable), so just + // ignore it. + None => return } - }); - s - }; - - let (lint, label) = match stability { - // no stability attributes == Unstable - None => (lint::Unstable, "unmarked"), - Some(attr::Stability { level: attr::Unstable, .. }) => - (lint::Unstable, "unstable"), - Some(attr::Stability { level: attr::Experimental, .. }) => - (lint::Experimental, "experimental"), - Some(attr::Stability { level: attr::Deprecated, .. }) => - (lint::Deprecated, "deprecated"), - _ => return - }; - - let msg = match stability { - Some(attr::Stability { text: Some(ref s), .. }) => { - format!("use of {} item: {}", label, *s) - } - _ => format!("use of {} item", label) - }; + } else { + // cross-crate + + let mut s = None; + // run through all the attributes and take the first + // stability one. + csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| { + if s.is_none() { + s = attr::find_stability(attrs.as_slice()) + } + }); + s + }; - cx.span_lint(lint, e.span, msg.as_slice()); -} + let (lint, label) = match stability { + // no stability attributes == Unstable + None => (lint::Unstable, "unmarked"), + Some(attr::Stability { level: attr::Unstable, .. }) => + (lint::Unstable, "unstable"), + Some(attr::Stability { level: attr::Experimental, .. }) => + (lint::Experimental, "experimental"), + Some(attr::Stability { level: attr::Deprecated, .. }) => + (lint::Deprecated, "deprecated"), + _ => return + }; -pub fn check_enum_variant_sizes(cx: &mut 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.node_levels.insert((it.id, lint::VariantSizeDifference), (lvl, src)); - }, - _ => { } + let msg = match stability { + Some(attr::Stability { text: Some(ref s), .. }) => { + format!("use of {} item: {}", label, *s) } - }, - _ => { } + _ => format!("use of {} item", label) + }; + + cx.span_lint(lint, e.span, msg.as_slice()); } } diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 03de3f652ab50..317cbf3f7db9e 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -28,35 +28,93 @@ //! upon. As the ast is traversed, this keeps track of the current lint level //! for all lint attributes. //! -//! To add a new lint warning, all you need to do is to either invoke `add_lint` -//! on the session at the appropriate time, or write a few linting functions and -//! modify the Context visitor appropriately. If you're adding lints from the -//! Context itself, span_lint should be used instead of add_lint. +//! Most of the lints built into `rustc` are structs implementing `LintPass`, +//! and are defined within `builtin.rs`. To add a new lint you can define such +//! a struct and add it to the `builtin_lints!` macro invocation in this file. +//! `LintPass` itself is not a subtrait of `Default`, but the `builtin_lints!` +//! macro requires `Default` (usually via `deriving`). +//! +//! Some lints are defined elsewhere in the compiler and work by calling +//! `add_lint()` on the overall `Session` object. +//! +//! If you're adding lints to the `Context` infrastructure itself, defined in +//! this file, use `span_lint` instead of `add_lint`. #![allow(non_camel_case_types)] use driver::session; use middle::dead::DEAD_CODE_LINT_STR; -use middle::privacy; +use middle::privacy::ExportedItems; use middle::ty; use middle::typeck::astconv::AstConv; use middle::typeck::infer; -use util::nodemap::NodeSet; use std::collections::HashMap; use std::rc::Rc; use std::to_str::ToStr; +use std::cell::RefCell; +use std::default::Default; use std::collections::SmallIntMap; use syntax::ast_util::IdVisitingOperation; use syntax::attr::AttrMetaMethods; -use syntax::attr; use syntax::codemap::Span; use syntax::parse::token::InternedString; -use syntax::visit::Visitor; +use syntax::visit::{Visitor, FnKind}; use syntax::{ast, ast_util, visit}; mod builtin; +/// Trait for types providing lint checks. Each method checks a single syntax +/// node, and should not invoke methods recursively (unlike `Visitor`). Each +/// method has a default do-nothing implementation. The trait also contains a +/// few lint-specific methods with no equivalent in `Visitor`. +// +// FIXME: eliminate the duplication with `Visitor` +trait LintPass { + fn check_crate(&mut self, _: &Context, _: &ExportedItems, _: &ast::Crate) { } + fn check_ident(&mut self, _: &Context, _: Span, _: ast::Ident) { } + fn check_mod(&mut self, _: &Context, _: &ast::Mod, _: Span, _: ast::NodeId) { } + fn check_view_item(&mut self, _: &Context, _: &ast::ViewItem) { } + fn check_foreign_item(&mut self, _: &Context, _: &ast::ForeignItem) { } + fn check_item(&mut self, _: &Context, _: &ast::Item) { } + fn check_local(&mut self, _: &Context, _: &ast::Local) { } + fn check_block(&mut self, _: &Context, _: &ast::Block) { } + fn check_stmt(&mut self, _: &Context, _: &ast::Stmt) { } + fn check_arm(&mut self, _: &Context, _: &ast::Arm) { } + fn check_pat(&mut self, _: &Context, _: &ast::Pat) { } + fn check_decl(&mut self, _: &Context, _: &ast::Decl) { } + fn check_expr(&mut self, _: &Context, _: &ast::Expr) { } + fn check_expr_post(&mut self, _: &Context, _: &ast::Expr) { } + fn check_ty(&mut self, _: &Context, _: &ast::Ty) { } + fn check_generics(&mut self, _: &Context, _: &ast::Generics) { } + fn check_fn(&mut self, _: &Context, + _: &FnKind, _: &ast::FnDecl, _: &ast::Block, _: Span, _: ast::NodeId) { } + fn check_ty_method(&mut self, _: &Context, _: &ast::TypeMethod) { } + fn check_trait_method(&mut self, _: &Context, _: &ast::TraitMethod) { } + fn check_struct_def(&mut self, _: &Context, + _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { } + fn check_struct_def_post(&mut self, _: &Context, + _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { } + fn check_struct_field(&mut self, _: &Context, _: &ast::StructField) { } + fn check_variant(&mut self, _: &Context, _: &ast::Variant, _: &ast::Generics) { } + fn check_opt_lifetime_ref(&mut self, _: &Context, _: Span, _: &Option) { } + fn check_lifetime_ref(&mut self, _: &Context, _: &ast::Lifetime) { } + fn check_lifetime_decl(&mut self, _: &Context, _: &ast::Lifetime) { } + fn check_explicit_self(&mut self, _: &Context, _: &ast::ExplicitSelf) { } + fn check_mac(&mut self, _: &Context, _: &ast::Mac) { } + fn check_path(&mut self, _: &Context, _: &ast::Path, _: ast::NodeId) { } + fn check_attribute(&mut self, _: &Context, _: &ast::Attribute) { } + + /// Called when entering a syntax node that can have lint attributes such + /// as `#[allow(...)]`. Called with *all* the attributes of that node. + fn enter_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { } + + /// Counterpart to `enter_lint_attrs`. + fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { } +} + +type LintPassObject = Box; + #[deriving(Clone, Show, PartialEq, PartialOrd, Eq, Ord, Hash)] pub enum LintId { CTypes, @@ -75,7 +133,6 @@ pub enum LintId { TypeOverflow, UnusedUnsafe, UnsafeBlock, - AttributeUsage, UnusedAttribute, UnknownFeatures, UnknownCrateType, @@ -277,13 +334,6 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[ default: Allow }), - ("attribute_usage", - LintSpec { - lint: AttributeUsage, - desc: "detects bad use of attributes", - default: Warn - }), - ("unused_attribute", LintSpec { lint: UnusedAttribute, @@ -453,30 +503,27 @@ struct Context<'a> { cur: SmallIntMap<(Level, LintSource)>, /// Context we're checking in (used to access fields like sess) tcx: &'a ty::ctxt, - /// Items exported by the crate; used by the missing_doc lint. - exported_items: &'a privacy::ExportedItems, - /// The id of the current `ast::StructDef` being walked. - cur_struct_def_id: ast::NodeId, - /// Whether some ancestor of the current node was marked - /// #[doc(hidden)]. - is_doc_hidden: bool, /// 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: Vec<(LintId, Level, LintSource)>, - - /// Id of the last visited negated expression - negated_expr_id: ast::NodeId, - - /// Ids of structs/enums which have been checked for raw_pointer_deriving - checked_raw_pointers: NodeSet, + level_stack: Vec<(LintId, Level, LintSource)>, /// Level of lints for certain NodeIds, stored here because the body of /// the lint needs to run in trans. - node_levels: HashMap<(ast::NodeId, LintId), (Level, LintSource)>, + node_levels: RefCell>, + + /// Trait objects for each lint. + lints: Vec>, } +/// Convenience macro for calling a `LintPass` method on every lint in the context. +macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ( + for tl in $cx.lints.iter() { + tl.borrow_mut().$f($cx, $($args),*); + } +)) + pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span, lint_str: &str, tcx: &ty::ctxt) { if level == Allow { return } @@ -596,7 +643,7 @@ impl<'a> Context<'a> { lintname).as_slice()); } else if now != level { let src = self.get_source(lint); - self.lint_stack.push((lint, now, src)); + self.level_stack.push((lint, now, src)); pushed += 1; self.set_level(lint, level, Node(meta.span)); } @@ -605,26 +652,13 @@ impl<'a> Context<'a> { true }); - let old_is_doc_hidden = self.is_doc_hidden; - self.is_doc_hidden = - self.is_doc_hidden || - attrs.iter() - .any(|attr| { - attr.name().equiv(&("doc")) && - match attr.meta_item_list() { - None => false, - Some(l) => { - attr::contains_name(l.as_slice(), "hidden") - } - } - }); - + run_lints!(self, enter_lint_attrs, attrs); f(self); + run_lints!(self, exit_lint_attrs, attrs); // rollback - self.is_doc_hidden = old_is_doc_hidden; for _ in range(0, pushed) { - let (lint, lvl, src) = self.lint_stack.pop().unwrap(); + let (lint, lvl, src) = self.level_stack.pop().unwrap(); self.set_level(lint, lvl, src); } } @@ -637,6 +671,10 @@ impl<'a> Context<'a> { }; f(&mut v); } + + fn insert_node_level(&self, id: ast::NodeId, lint: LintId, lvl: Level, src: LintSource) { + self.node_levels.borrow_mut().insert((id, lint), (lvl, src)); + } } /// Check that every lint from the list of attributes satisfies `f`. @@ -695,35 +733,6 @@ pub fn contains_lint(attrs: &[ast::Attribute], false } -#[deriving(PartialEq)] -enum MethodContext { - TraitDefaultImpl, - TraitImpl, - PlainImpl -} - -fn method_context(cx: &Context, m: &ast::Method) -> MethodContext { - let did = ast::DefId { - krate: ast::LOCAL_CRATE, - node: m.id - }; - - match cx.tcx.methods.borrow().find_copy(&did) { - None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"), - Some(md) => { - match md.container { - ty::TraitContainer(..) => TraitDefaultImpl, - ty::ImplContainer(cid) => { - match ty::impl_trait_ref(cx.tcx, cid) { - Some(..) => TraitImpl, - None => PlainImpl - } - } - } - } - } -} - impl<'a> AstConv for Context<'a>{ fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx } @@ -743,176 +752,170 @@ impl<'a> AstConv for Context<'a>{ impl<'a> Visitor<()> for Context<'a> { fn visit_item(&mut self, it: &ast::Item, _: ()) { self.with_lint_attrs(it.attrs.as_slice(), |cx| { - builtin::check_enum_variant_sizes(cx, it); - builtin::check_item_ctypes(cx, it); - builtin::check_item_non_camel_case_types(cx, it); - builtin::check_item_non_uppercase_statics(cx, it); - builtin::check_heap_item(cx, it); - builtin::check_missing_doc_item(cx, it); - builtin::check_raw_ptr_deriving(cx, it); - + run_lints!(cx, check_item, it); cx.visit_ids(|v| v.visit_item(it, ())); - visit::walk_item(cx, it, ()); }) } fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) { self.with_lint_attrs(it.attrs.as_slice(), |cx| { + run_lints!(cx, check_foreign_item, it); visit::walk_foreign_item(cx, it, ()); }) } fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) { self.with_lint_attrs(i.attrs.as_slice(), |cx| { + run_lints!(cx, check_view_item, i); cx.visit_ids(|v| v.visit_view_item(i, ())); - visit::walk_view_item(cx, i, ()); }) } fn visit_pat(&mut self, p: &ast::Pat, _: ()) { - builtin::check_pat_non_uppercase_statics(self, p); - builtin::check_pat_uppercase_variable(self, p); - + run_lints!(self, check_pat, p); visit::walk_pat(self, p, ()); } fn visit_expr(&mut self, e: &ast::Expr, _: ()) { - match e.node { - ast::ExprUnary(ast::UnNeg, expr) => { - // propagate negation, if the negation itself isn't negated - if self.negated_expr_id != e.id { - self.negated_expr_id = expr.id; - } - }, - ast::ExprParen(expr) => if self.negated_expr_id == e.id { - self.negated_expr_id = expr.id - }, - ast::ExprMatch(_, ref arms) => { - for a in arms.iter() { - builtin::check_unused_mut_pat(self, a.pats.as_slice()); - } - }, - _ => () - }; - - builtin::check_while_true_expr(self, e); - builtin::check_stability(self, e); - builtin::check_unnecessary_parens_expr(self, e); - builtin::check_unused_unsafe(self, e); - builtin::check_unsafe_block(self, e); - builtin::check_unnecessary_allocation(self, e); - builtin::check_heap_expr(self, e); - - builtin::check_type_limits(self, e); - builtin::check_unused_casts(self, e); - builtin::check_deprecated_owned_vector(self, e); - + run_lints!(self, check_expr, e); visit::walk_expr(self, e, ()); } fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) { - builtin::check_path_statement(self, s); - builtin::check_unused_result(self, s); - builtin::check_unnecessary_parens_stmt(self, s); - - match s.node { - ast::StmtDecl(d, _) => { - match d.node { - ast::DeclLocal(l) => { - builtin::check_unused_mut_pat(self, &[l.pat]); - }, - _ => {} - } - }, - _ => {} - } - + run_lints!(self, check_stmt, s); visit::walk_stmt(self, s, ()); } - fn visit_fn(&mut self, fk: &visit::FnKind, decl: &ast::FnDecl, + fn visit_fn(&mut self, fk: &FnKind, decl: &ast::FnDecl, body: &ast::Block, span: Span, id: ast::NodeId, _: ()) { - let recurse = |this: &mut Context| { - visit::walk_fn(this, fk, decl, body, span, ()); - }; - - for a in decl.inputs.iter(){ - builtin::check_unused_mut_pat(self, &[a.pat]); - } - match *fk { - visit::FkMethod(ident, _, m) => { + visit::FkMethod(_, _, m) => { self.with_lint_attrs(m.attrs.as_slice(), |cx| { - builtin::check_missing_doc_method(cx, m); - - match method_context(cx, m) { - PlainImpl - => builtin::check_snake_case(cx, "method", ident, span), - TraitDefaultImpl - => builtin::check_snake_case(cx, "trait method", ident, span), - _ => (), - } - + run_lints!(cx, check_fn, fk, decl, body, span, id); cx.visit_ids(|v| { v.visit_fn(fk, decl, body, span, id, ()); }); - recurse(cx); + visit::walk_fn(cx, fk, decl, body, span, ()); }) }, - visit::FkItemFn(ident, _, _, _) => { - builtin::check_snake_case(self, "function", ident, span); - recurse(self); + _ => { + run_lints!(self, check_fn, fk, decl, body, span, id); + visit::walk_fn(self, fk, decl, body, span, ()); } - _ => recurse(self), } } fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) { self.with_lint_attrs(t.attrs.as_slice(), |cx| { - builtin::check_missing_doc_ty_method(cx, t); - builtin::check_snake_case(cx, "trait method", t.ident, t.span); - + run_lints!(cx, check_ty_method, t); visit::walk_ty_method(cx, t, ()); }) } fn visit_struct_def(&mut self, s: &ast::StructDef, - _: ast::Ident, - _: &ast::Generics, + ident: ast::Ident, + g: &ast::Generics, id: ast::NodeId, _: ()) { - builtin::check_struct_uppercase_variable(self, s); - - let old_id = self.cur_struct_def_id; - self.cur_struct_def_id = id; + run_lints!(self, check_struct_def, s, ident, g, id); visit::walk_struct_def(self, s, ()); - self.cur_struct_def_id = old_id; + run_lints!(self, check_struct_def_post, s, ident, g, id); } fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) { self.with_lint_attrs(s.node.attrs.as_slice(), |cx| { - builtin::check_missing_doc_struct_field(cx, s); - + run_lints!(cx, check_struct_field, s); visit::walk_struct_field(cx, s, ()); }) } fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) { self.with_lint_attrs(v.node.attrs.as_slice(), |cx| { - builtin::check_missing_doc_variant(cx, v); - + run_lints!(cx, check_variant, v, g); visit::walk_variant(cx, v, g, ()); }) } // FIXME(#10894) should continue recursing - fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {} + fn visit_ty(&mut self, t: &ast::Ty, _: ()) { + run_lints!(self, check_ty, t); + } + + fn visit_ident(&mut self, sp: Span, id: ast::Ident, _: ()) { + run_lints!(self, check_ident, sp, id); + } + + fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId, _: ()) { + run_lints!(self, check_mod, m, s, n); + visit::walk_mod(self, m, ()); + } + + fn visit_local(&mut self, l: &ast::Local, _: ()) { + run_lints!(self, check_local, l); + visit::walk_local(self, l, ()); + } + + fn visit_block(&mut self, b: &ast::Block, _: ()) { + run_lints!(self, check_block, b); + visit::walk_block(self, b, ()); + } + + fn visit_arm(&mut self, a: &ast::Arm, _: ()) { + run_lints!(self, check_arm, a); + visit::walk_arm(self, a, ()); + } + + fn visit_decl(&mut self, d: &ast::Decl, _: ()) { + run_lints!(self, check_decl, d); + visit::walk_decl(self, d, ()); + } + + fn visit_expr_post(&mut self, e: &ast::Expr, _: ()) { + run_lints!(self, check_expr_post, e); + } + + fn visit_generics(&mut self, g: &ast::Generics, _: ()) { + run_lints!(self, check_generics, g); + visit::walk_generics(self, g, ()); + } + + fn visit_trait_method(&mut self, m: &ast::TraitMethod, _: ()) { + run_lints!(self, check_trait_method, m); + visit::walk_trait_method(self, m, ()); + } + + fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option, _: ()) { + run_lints!(self, check_opt_lifetime_ref, sp, lt); + } + + fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime, _: ()) { + run_lints!(self, check_lifetime_ref, lt); + } + + fn visit_lifetime_decl(&mut self, lt: &ast::Lifetime, _: ()) { + run_lints!(self, check_lifetime_decl, lt); + } + + fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf, _: ()) { + run_lints!(self, check_explicit_self, es); + visit::walk_explicit_self(self, es, ()); + } + + fn visit_mac(&mut self, mac: &ast::Mac, _: ()) { + run_lints!(self, check_mac, mac); + visit::walk_mac(self, mac, ()); + } + + fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId, _: ()) { + run_lints!(self, check_path, p, id); + visit::walk_path(self, p, ()); + } fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) { - builtin::check_unused_attribute(self, attr); + run_lints!(self, check_attribute, attr); } } @@ -930,19 +933,33 @@ impl<'a> IdVisitingOperation for Context<'a> { } pub fn check_crate(tcx: &ty::ctxt, - exported_items: &privacy::ExportedItems, + exported_items: &ExportedItems, krate: &ast::Crate) { + macro_rules! builtin_lints (( $($name:ident),*, ) => ( + vec!($( + { + let obj: builtin::$name = Default::default(); + RefCell::new(box obj as LintPassObject) + } + ),*) + )) + + let builtin_lints = builtin_lints!( + GatherNodeLevels, WhileTrue, UnusedCasts, TypeLimits, CTypes, + HeapMemory, RawPointerDeriving, UnusedAttribute, + PathStatement, UnusedMustUse, DeprecatedOwnedVector, + NonCamelCaseTypes, NonSnakeCaseFunctions, NonUppercaseStatics, + UppercaseVariables, UnnecessaryParens, UnusedUnsafe, UnsafeBlock, + UnusedMut, UnnecessaryAllocation, MissingDoc, Stability, + ); + let mut cx = Context { dict: get_lint_dict(), cur: SmallIntMap::new(), tcx: tcx, - exported_items: exported_items, - cur_struct_def_id: -1, - is_doc_hidden: false, - lint_stack: Vec::new(), - negated_expr_id: -1, - checked_raw_pointers: NodeSet::new(), - node_levels: HashMap::new(), + level_stack: Vec::new(), + node_levels: RefCell::new(HashMap::new()), + lints: builtin_lints, }; // Install default lint levels, followed by the command line levels, and @@ -962,13 +979,9 @@ pub fn check_crate(tcx: &ty::ctxt, visit::walk_crate(v, krate, ()); }); - // since the root module isn't visited as an item (because it isn't an item), warn for it - // here. - builtin::check_missing_doc_attrs(cx, - None, - krate.attrs.as_slice(), - krate.span, - "crate"); + // since the root module isn't visited as an item (because it isn't an + // item), warn for it here. + run_lints!(cx, check_crate, exported_items, krate); visit::walk_crate(cx, krate, ()); }); @@ -983,5 +996,5 @@ pub fn check_crate(tcx: &ty::ctxt, } tcx.sess.abort_if_errors(); - *tcx.node_lint_levels.borrow_mut() = cx.node_levels; + *tcx.node_lint_levels.borrow_mut() = cx.node_levels.unwrap(); } From 4c50f8b868dd8fe319b4bd9ef0a63f99b70d5549 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Wed, 4 Jun 2014 14:35:58 -0700 Subject: [PATCH 05/24] Replace enum LintId with an extensible alternative --- src/librustc/driver/config.rs | 38 +- src/librustc/driver/driver.rs | 4 +- src/librustc/driver/mod.rs | 61 +- src/librustc/driver/session.rs | 7 +- src/librustc/front/feature_gate.rs | 2 +- src/librustc/lib.rs | 9 + src/librustc/lint/builtin.rs | 467 ++++++++++---- src/librustc/lint/mod.rs | 800 ++++++++---------------- src/librustc/middle/dead.rs | 27 +- src/librustc/middle/liveness.rs | 8 +- src/librustc/middle/privacy.rs | 2 +- src/librustc/middle/resolve.rs | 8 +- src/librustc/middle/trans/base.rs | 82 +-- src/librustc/middle/ty.rs | 2 +- src/librustc/middle/typeck/check/mod.rs | 6 +- src/librustdoc/core.rs | 4 +- 16 files changed, 751 insertions(+), 776 deletions(-) diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index 02b040a259637..020b76c366f80 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -70,7 +70,8 @@ pub struct Options { pub gc: bool, pub optimize: OptLevel, 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 , // This was mutable for rustpkg, which updates search paths based on the // parsed code. It remains mutable in case its replacements wants to use @@ -104,6 +105,7 @@ pub fn basic_options() -> Options { optimize: No, debuginfo: NoDebugInfo, lint_opts: Vec::new(), + describe_lints: false, output_types: Vec::new(), addl_lib_search_paths: RefCell::new(HashSet::new()), maybe_sysroot: None, @@ -588,30 +590,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let no_trans = matches.opt_present("no-trans"); let no_analysis = matches.opt_present("no-analysis"); - let lint_levels = [lint::Allow, lint::Warn, - lint::Deny, lint::Forbid]; - 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); - let level_short = level_short.to_ascii().to_upper().into_str(); - let flags = matches.opt_strs(level_short.as_slice()) - .move_iter() - .collect::>() - .append(matches.opt_strs(level_name).as_slice()); - 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)); - } + let mut lint_opts = vec!(); + let mut describe_lints = false; + + for &level in [lint::Allow, lint::Warn, lint::Deny, lint::Forbid].iter() { + for lint_name in matches.opt_strs(level.as_str()).move_iter() { + if lint_name.as_slice() == "help" { + describe_lints = true; + } else { + lint_opts.push((lint_name.replace("-", "_").into_string(), level)); } } } @@ -755,6 +742,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { optimize: opt_level, debuginfo: debuginfo, lint_opts: lint_opts, + describe_lints: describe_lints, output_types: output_types, addl_lib_search_paths: RefCell::new(addl_lib_search_paths), maybe_sysroot: sysroot_opt, diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index fc9934dea9431..5c6a6eee50c4d 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -749,7 +749,7 @@ pub fn collect_crate_types(session: &Session, } Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable), Some(_) => { - session.add_lint(lint::UnknownCrateType, + session.add_lint(lint::builtin::unknown_crate_type, ast::CRATE_NODE_ID, a.span, "invalid `crate_type` \ @@ -757,7 +757,7 @@ pub fn collect_crate_types(session: &Session, None } _ => { - session.add_lint(lint::UnknownCrateType, + session.add_lint(lint::builtin::unknown_crate_type, ast::CRATE_NODE_ID, a.span, "`crate_type` requires a \ diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs index f1847f257808c..e5c4657490292 100644 --- a/src/librustc/driver/mod.rs +++ b/src/librustc/driver/mod.rs @@ -17,7 +17,6 @@ use lint; use metadata; use std::any::AnyRefExt; -use std::cmp; use std::io; use std::os; use std::str; @@ -50,6 +49,12 @@ fn run_compiler(args: &[String]) { 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() { 0u => early_error("no input filename given"), 1u => { @@ -66,7 +71,6 @@ fn run_compiler(args: &[String]) { _ => early_error("multiple input filenames provided") }; - let sopts = config::build_session_options(&matches); let sess = build_session(sopts, input_file_path); let cfg = config::build_configuration(&sess); let odir = matches.opt_str("out-dir").map(|o| Path::new(o)); @@ -124,7 +128,7 @@ Additional help: config::optgroups().as_slice())); } -fn describe_warnings() { +fn describe_lints() { println!(" Available lint options: -W Warn about @@ -133,30 +137,32 @@ Available lint options: -F Forbid (deny, and deny all overrides) "); - let lint_dict = lint::get_lint_dict(); - let mut lint_dict = lint_dict.move_iter() - .map(|(k, v)| (v, k)) - .collect:: >(); - lint_dict.as_mut_slice().sort(); + let mut builtin_specs = lint::builtin_lint_specs(); + builtin_specs.sort_by(|x, y| { + match x.default_level.cmp(&y.default_level) { + Equal => x.name.cmp(&y.name), + 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!(" {} {:7.7s} {}", - padded(max_key, "name"), "default", "meaning"); - println!(" {} {:7.7s} {}\n", - padded(max_key, "----"), "-------", "-------"); - for (spec, name) in lint_dict.move_iter() { - let name = name.replace("_", "-"); + println!(" {} {:7.7s} {}", padded("name"), "default", "meaning"); + println!(" {} {:7.7s} {}", padded("----"), "-------", "-------"); + println!(""); + + for spec in builtin_specs.move_iter() { + let name = spec.name.replace("_", "-"); println!(" {} {:7.7s} {}", - padded(max_key, name.as_slice()), - lint::level_to_str(spec.default), - spec.desc); + padded(name.as_slice()), spec.default_level.as_str(), spec.desc); } println!(""); } @@ -214,12 +220,7 @@ pub fn handle_options(mut args: Vec) -> Option { return None; } - let lint_flags = matches.opt_strs("W").move_iter().collect::>().append( - matches.opt_strs("warn").as_slice()); - if lint_flags.iter().any(|x| x.as_slice() == "help") { - describe_warnings(); - return None; - } + // Don't handle -W help here, because we might first load plugins. let r = matches.opt_strs("Z"); if r.iter().any(|x| x.as_slice() == "help") { diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 7db1363460368..bbb145c9f151c 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -105,16 +105,17 @@ impl Session { self.diagnostic().handler().unimpl(msg) } pub fn add_lint(&self, - lint: lint::LintId, + lint: &'static lint::Lint, id: ast::NodeId, sp: Span, msg: String) { + let lint_id = lint::LintId::of(lint); let mut lints = self.lints.borrow_mut(); match lints.find_mut(&id) { - Some(arr) => { arr.push((lint, sp, msg)); return; } + Some(arr) => { arr.push((lint_id, sp, msg)); return; } None => {} } - lints.insert(id, vec!((lint, sp, msg))); + lints.insert(id, vec!((lint_id, sp, msg))); } pub fn next_node_id(&self) -> ast::NodeId { self.reserve_node_ids(1) diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index f6f52a3052a19..edb379e03b526 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -366,7 +366,7 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) { directive not necessary"); } None => { - sess.add_lint(lint::UnknownFeatures, + sess.add_lint(lint::builtin::unknown_features, ast::CRATE_NODE_ID, mi.span, "unknown feature".to_string()); diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index ca0083bb6bb72..108a12fc76bdc 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -130,6 +130,15 @@ pub mod lib { 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() { let args = std::os::args().iter() .map(|x| x.to_string()) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 2ca4d3eb4e857..135cffdc86fc2 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -19,7 +19,7 @@ use middle::privacy::ExportedItems; use middle::{typeck, ty, def, pat_util}; use util::ppaux::{ty_to_str}; use util::nodemap::NodeSet; -use lint::{Context, LintPass}; +use lint::{Context, LintPass, LintArray}; use lint; use std::cmp; @@ -41,31 +41,17 @@ use syntax::codemap::Span; use syntax::parse::token; use syntax::{ast, ast_util, visit}; -/// Doesn't actually warn; just gathers information for use by -/// checks in trans. -#[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); - }, - _ => { } - } - }, - _ => { } - } - } -} +declare_lint!(while_true, Warn, + "suggest using `loop { }` instead of `while true { }`") #[deriving(Default)] pub struct WhileTrue; impl LintPass for WhileTrue { + fn get_lints(&self) -> LintArray { + lint_array!(while_true) + } + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { match e.node { ast::ExprWhile(cond, _) => { @@ -73,8 +59,7 @@ impl LintPass for WhileTrue { ast::ExprLit(lit) => { match lit.node { ast::LitBool(true) => { - cx.span_lint(lint::WhileTrue, - e.span, + cx.span_lint(while_true, e.span, "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)] pub struct UnusedCasts; impl LintPass for UnusedCasts { + fn get_lints(&self) -> LintArray { + lint_array!(unnecessary_typecast) + } + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { match e.node { ast::ExprCast(expr, 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 { - cx.span_lint(lint::UnnecessaryTypecast, ty.span, - "unnecessary type cast"); + cx.span_lint(unnecessary_typecast, ty.span, "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 { /// Id of the last visited negated expression negated_expr_id: ast::NodeId, @@ -121,6 +121,10 @@ impl Default 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) { match e.node { ast::ExprUnary(ast::UnNeg, expr) => { @@ -128,7 +132,7 @@ impl LintPass for TypeLimits { ast::ExprLit(lit) => { match lit.node { ast::LitUint(..) => { - cx.span_lint(lint::UnsignedNegate, e.span, + cx.span_lint(unsigned_negate, e.span, "negation of unsigned int literal may \ be unintentional"); }, @@ -139,7 +143,7 @@ impl LintPass for TypeLimits { let t = ty::expr_ty(cx.tcx, expr); match ty::get(t).sty { ty::ty_uint(_) => { - cx.span_lint(lint::UnsignedNegate, e.span, + cx.span_lint(unsigned_negate, e.span, "negation of unsigned int variable may \ be unintentional"); }, @@ -157,7 +161,7 @@ impl LintPass for TypeLimits { }, ast::ExprBinary(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"); } }, @@ -178,7 +182,7 @@ impl LintPass for TypeLimits { lit_val *= -1; } 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"); } }, @@ -194,7 +198,7 @@ impl LintPass for TypeLimits { _ => fail!() }; 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"); } }, @@ -300,30 +304,37 @@ impl LintPass for TypeLimits { } } +declare_lint!(ctypes, Warn, + "proper use of libc types in foreign modules") + #[deriving(Default)] pub struct 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_ty(cx: &Context, ty: &ast::Ty) { match ty.node { ast::TyPath(_, _, id) => { match cx.tcx.def_map.borrow().get_copy(&id) { def::DefPrimTy(ast::TyInt(ast::TyI)) => { - cx.span_lint(lint::CTypes, ty.span, - "found rust type `int` in foreign module, while \ - libc::c_int or libc::c_long should be used"); + cx.span_lint(ctypes, ty.span, + "found rust type `int` in foreign module, while \ + libc::c_int or libc::c_long should be used"); } def::DefPrimTy(ast::TyUint(ast::TyU)) => { - cx.span_lint(lint::CTypes, ty.span, - "found rust type `uint` in foreign module, while \ - libc::c_uint or libc::c_ulong should be used"); + cx.span_lint(ctypes, ty.span, + "found rust type `uint` in foreign module, while \ + libc::c_uint or libc::c_ulong should be used"); } def::DefTy(def_id) => { if !adt::is_ffi_safe(cx.tcx, def_id) { - cx.span_lint(lint::CTypes, ty.span, - "found enum type without foreign-function-safe \ - representation annotation in foreign module"); + cx.span_lint(ctypes, ty.span, + "found enum type without foreign-function-safe \ + representation annotation in foreign module"); // hmm... this message could be more helpful } } @@ -356,54 +367,66 @@ 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)] pub struct HeapMemory; impl HeapMemory { fn check_heap_type(&self, cx: &Context, span: Span, ty: ty::t) { - let xs = [lint::ManagedHeapMemory, lint::OwnedHeapMemory, lint::HeapMemory]; - for &lint in xs.iter() { - if cx.get_level(lint) == lint::Allow { continue } - - 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(_) | - ty::ty_trait(box ty::TyTrait { - store: ty::UniqTraitStore, .. - }) | - ty::ty_closure(box ty::ClosureTy { - store: ty::UniqTraitStore, - .. - }) => { - n_uniq += 1; - } + // FIXME: skip the fold for performance when all are allowed? + + 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(_) | + ty::ty_trait(box ty::TyTrait { + store: ty::UniqTraitStore, .. + }) | + ty::ty_closure(box ty::ClosureTy { + store: ty::UniqTraitStore, + .. + }) => { + n_uniq += 1; + } - _ => () - }; - t - }); + _ => () + }; + t + }); - if n_uniq > 0 && lint != lint::ManagedHeapMemory { - let s = ty_to_str(cx.tcx, ty); - let m = format!("type uses owned (Box type) pointers: {}", s); - cx.span_lint(lint, span, m.as_slice()); - } + if n_uniq > 0 { + let s = ty_to_str(cx.tcx, ty); + 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_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()); - } + if n_box > 0 { + let s = ty_to_str(cx.tcx, ty); + let m = format!("type uses managed (@ type) pointers: {}", s); + cx.span_lint(managed_heap_memory, span, m.as_slice()); + cx.span_lint(heap_memory, span, m.as_slice()); } } } 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) { match it.node { ast::ItemFn(..) | @@ -434,6 +457,9 @@ impl LintPass for HeapMemory { } } +declare_lint!(raw_pointer_deriving, Warn, + "uses of #[deriving] with raw pointers are rarely correct") + struct RawPtrDerivingVisitor<'a> { cx: &'a Context<'a> } @@ -442,7 +468,7 @@ impl<'a> visit::Visitor<()> for RawPtrDerivingVisitor<'a> { fn visit_ty(&mut self, ty: &ast::Ty, _: ()) { static MSG: &'static str = "use of `#[deriving]` with a raw pointer"; 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, ()); @@ -465,6 +491,10 @@ impl Default 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) { if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") { return @@ -495,10 +525,17 @@ impl LintPass for RawPointerDeriving { } } +declare_lint!(unused_attribute, Warn, + "detects attributes that were not used by the compiler") + #[deriving(Default)] pub struct UnusedAttribute; impl LintPass for UnusedAttribute { + fn get_lints(&self) -> LintArray { + lint_array!(unused_attribute) + } + fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) { static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [ // FIXME: #14408 whitelist docs since rustdoc looks at them @@ -556,7 +593,7 @@ impl LintPass for UnusedAttribute { } 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()) { let msg = match attr.node.style { ast::AttrOuter => "crate-level attribute should be an inner \ @@ -564,26 +601,30 @@ impl LintPass for UnusedAttribute { ast::AttrInner => "crate-level attribute should be in the \ 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)] pub struct PathStatement; impl LintPass for PathStatement { + fn get_lints(&self) -> LintArray { + lint_array!(path_statement) + } + fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { match s.node { ast::StmtSemi(expr, _) => { match expr.node { - ast::ExprPath(_) => { - cx.span_lint(lint::PathStatement, - s.span, - "path statement with no effect"); - } - _ => {} + ast::ExprPath(_) => cx.span_lint(path_statement, s.span, + "path statement with no effect"), + _ => () } } _ => () @@ -591,10 +632,20 @@ impl LintPass for PathStatement { } } +declare_lint!(unused_must_use, Warn, + "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 UnusedMustUse; +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) { let expr = match s.node { ast::StmtSemi(expr, _) => expr, @@ -620,7 +671,7 @@ impl LintPass for UnusedMustUse { ast_map::NodeItem(it) => { if attr::contains_name(it.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"); warned = true; } @@ -630,7 +681,7 @@ impl LintPass for UnusedMustUse { } else { csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| { 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"); warned = true; } @@ -640,21 +691,28 @@ impl LintPass for UnusedMustUse { _ => {} } 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)] pub struct 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) { let t = ty::expr_ty(cx.tcx, e); match ty::get(t).sty { ty::ty_uniq(t) => match ty::get(t).sty { 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`") } _ => {} @@ -664,10 +722,17 @@ impl LintPass for DeprecatedOwnedVector { } } +declare_lint!(non_camel_case_types, Warn, + "types, variants and traits should have camel case names") + #[deriving(Default)] pub struct 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 is_camel_case(ident: ast::Ident) -> bool { let ident = token::get_ident(ident); @@ -690,8 +755,7 @@ impl LintPass for NonCamelCaseTypes { let s = token::get_ident(ident); if !is_camel_case(ident) { - cx.span_lint(lint:: - NonCamelCaseTypes, span, + cx.span_lint(non_camel_case_types, span, format!("{} `{}` should have a camel case name such as `{}`", sort, s, to_camel_case(s.get())).as_slice()); } @@ -744,6 +808,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)] pub struct NonSnakeCaseFunctions; @@ -785,7 +852,7 @@ impl NonSnakeCaseFunctions { let s = token::get_ident(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 `{}`", sort, s, to_snake_case(s.get())).as_slice()); } @@ -793,6 +860,10 @@ impl NonSnakeCaseFunctions { } impl LintPass for NonSnakeCaseFunctions { + fn get_lints(&self) -> LintArray { + lint_array!(non_snake_case_functions) + } + fn check_fn(&mut self, cx: &Context, fk: &visit::FnKind, _: &ast::FnDecl, _: &ast::Block, span: Span, _: ast::NodeId) { @@ -815,10 +886,17 @@ impl LintPass for NonSnakeCaseFunctions { } } +declare_lint!(non_uppercase_statics, Allow, + "static constants should have uppercase identifiers") + #[deriving(Default)] pub struct 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) { match it.node { // only check static constants @@ -828,7 +906,7 @@ impl LintPass for NonUppercaseStatics { // ones (some scripts don't have a concept of // upper/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 \ such as `{}`", s.get(), s.get().chars().map(|c| c.to_uppercase()) @@ -838,6 +916,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) { // Lint for constants that look like binding identifiers (#7526) @@ -847,7 +937,7 @@ impl LintPass for NonUppercaseStatics { let ident = path.segments.last().unwrap().identifier; let s = token::get_ident(ident); 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 \ name such as `{}`", s.get(), s.get().chars().map(|c| c.to_uppercase()) @@ -859,10 +949,17 @@ impl LintPass for NonUppercaseStatics { } } +declare_lint!(uppercase_variables, Warn, + "variable and structure field names should start with a lowercase character") + #[deriving(Default)] pub struct UppercaseVariables; impl LintPass for UppercaseVariables { + fn get_lints(&self) -> LintArray { + lint_array!(uppercase_variables) + } + fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { match &p.node { &ast::PatIdent(_, ref path, _) => { @@ -873,9 +970,7 @@ impl LintPass for UppercaseVariables { let ident = path.segments.last().unwrap().identifier; let s = token::get_ident(ident); if s.get().len() > 0 && s.get().char_at(0).is_uppercase() { - cx.span_lint(lint:: - UppercaseVariables, - path.span, + cx.span_lint(uppercase_variables, path.span, "variable names should start with a lowercase character"); } } @@ -893,8 +988,7 @@ impl LintPass for UppercaseVariables { ast::StructField_ { kind: ast::NamedField(ident, _), .. } => { let s = token::get_ident(ident); if s.get().char_at(0).is_uppercase() { - cx.span_lint(lint::UppercaseVariables, - sf.span, + cx.span_lint(uppercase_variables, sf.span, "structure field names should start with a lowercase character"); } } @@ -904,6 +998,9 @@ impl LintPass for UppercaseVariables { } } +declare_lint!(unnecessary_parens, Warn, + "`if`, `match`, `while` and `return` do not need parentheses") + #[deriving(Default)] pub struct UnnecessaryParens; @@ -911,9 +1008,8 @@ impl UnnecessaryParens { fn check_unnecessary_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str) { match value.node { ast::ExprParen(_) => { - cx.span_lint(lint::UnnecessaryParens, value.span, - format!("unnecessary parentheses around {}", - msg).as_slice()) + cx.span_lint(unnecessary_parens, value.span, + format!("unnecessary parentheses around {}", msg).as_slice()) } _ => {} } @@ -921,6 +1017,10 @@ impl UnnecessaryParens { } impl LintPass for UnnecessaryParens { + fn get_lints(&self) -> LintArray { + lint_array!(unnecessary_parens) + } + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { let (value, msg) = match e.node { ast::ExprIf(cond, _, _) => (cond, "`if` condition"), @@ -949,18 +1049,24 @@ impl LintPass for UnnecessaryParens { } } +declare_lint!(unused_unsafe, Warn, + "unnecessary use of an `unsafe` block") + #[deriving(Default)] pub struct UnusedUnsafe; impl LintPass for UnusedUnsafe { + fn get_lints(&self) -> LintArray { + lint_array!(unused_unsafe) + } + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { match e.node { // Don't warn about generated blocks, that'll just pollute the output. ast::ExprBlock(ref blk) => { if blk.rules == ast::UnsafeBlock(ast::UserProvided) && !cx.tcx.used_unsafe.borrow().contains(&blk.id) { - cx.span_lint(lint::UnusedUnsafe, blk.span, - "unnecessary `unsafe` block"); + cx.span_lint(unused_unsafe, blk.span, "unnecessary `unsafe` block"); } } _ => () @@ -968,21 +1074,31 @@ impl LintPass for UnusedUnsafe { } } +declare_lint!(unsafe_block, Allow, + "usage of an `unsafe` block") + #[deriving(Default)] pub struct UnsafeBlock; impl LintPass for UnsafeBlock { + fn get_lints(&self) -> LintArray { + lint_array!(unsafe_block) + } + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { match e.node { // Don't warn about generated blocks, that'll just pollute the output. 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)] pub struct UnusedMut; @@ -1016,14 +1132,18 @@ impl UnusedMut { let used_mutables = cx.tcx.used_mut_nodes.borrow(); for (_, v) in mutables.iter() { if !v.iter().any(|e| used_mutables.contains(e)) { - cx.span_lint(lint::UnusedMut, cx.tcx.map.span(*v.get(0)), - "variable does not need to be mutable"); + cx.span_lint(unused_mut, cx.tcx.map.span(*v.get(0)), + "variable does not need to be mutable"); } } } } impl LintPass for UnusedMut { + fn get_lints(&self) -> LintArray { + lint_array!(unused_mut) + } + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { match e.node { ast::ExprMatch(_, ref arms) => { @@ -1063,10 +1183,17 @@ enum Allocation { BoxAllocation } +declare_lint!(unnecessary_allocation, Warn, + "detects unnecessary allocations that can be eliminated") + #[deriving(Default)] pub struct UnnecessaryAllocation; impl LintPass for UnnecessaryAllocation { + fn get_lints(&self) -> LintArray { + lint_array!(unnecessary_allocation) + } + fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { // Warn if string and vector literals with sigils, or boxing expressions, // are immediately borrowed. @@ -1086,27 +1213,24 @@ impl LintPass for UnnecessaryAllocation { _ => return }; - let report = |msg| { - cx.span_lint(lint::UnnecessaryAllocation, e.span, msg); - }; - match cx.tcx.adjustments.borrow().find(&e.id) { Some(adjustment) => { match *adjustment { ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { match (allocation, autoref) { (VectorAllocation, Some(ty::AutoBorrowVec(..))) => { - report("unnecessary allocation, the sigil can be \ - removed"); + cx.span_lint(unnecessary_allocation, e.span, + "unnecessary allocation, the sigil can be removed"); } (BoxAllocation, Some(ty::AutoPtr(_, ast::MutImmutable))) => { - report("unnecessary allocation, use & instead"); + cx.span_lint(unnecessary_allocation, e.span, + "unnecessary allocation, use & instead"); } (BoxAllocation, Some(ty::AutoPtr(_, ast::MutMutable))) => { - report("unnecessary allocation, use &mut \ - instead"); + cx.span_lint(unnecessary_allocation, e.span, + "unnecessary allocation, use &mut instead"); } _ => () } @@ -1119,6 +1243,9 @@ impl LintPass for UnnecessaryAllocation { } } +declare_lint!(missing_doc, Allow, + "detects missing documentation for public members") + pub struct MissingDoc { /// Set of nodes exported from this module. exported_items: Option, @@ -1175,15 +1302,17 @@ impl MissingDoc { } }); if !has_doc { - cx.span_lint(lint::MissingDoc, - sp, - format!("missing documentation for {}", - desc).as_slice()); + cx.span_lint(missing_doc, sp, + format!("missing documentation for {}", desc).as_slice()); } } } impl LintPass for MissingDoc { + fn get_lints(&self) -> LintArray { + lint_array!(missing_doc) + } + fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) { let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| { attr.check_name("doc") && match attr.meta_item_list() { @@ -1268,12 +1397,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 /// #[unstable] (or none of them) attributes. #[deriving(Default)] pub struct 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) { let id = match e.node { ast::ExprPath(..) | ast::ExprStruct(..) => { @@ -1342,13 +1484,13 @@ impl LintPass for Stability { let (lint, label) = match stability { // no stability attributes == Unstable - None => (lint::Unstable, "unmarked"), + None => (unstable, "unmarked"), Some(attr::Stability { level: attr::Unstable, .. }) => - (lint::Unstable, "unstable"), + (unstable, "unstable"), Some(attr::Stability { level: attr::Experimental, .. }) => - (lint::Experimental, "experimental"), + (experimental, "experimental"), Some(attr::Stability { level: attr::Deprecated, .. }) => - (lint::Deprecated, "deprecated"), + (deprecated, "deprecated"), _ => return }; @@ -1362,3 +1504,80 @@ impl LintPass for Stability { 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) + } +} diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 317cbf3f7db9e..68433258715f6 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -41,36 +41,95 @@ //! this file, use `span_lint` instead of `add_lint`. #![allow(non_camel_case_types)] +#![macro_escape] -use driver::session; -use middle::dead::DEAD_CODE_LINT_STR; use middle::privacy::ExportedItems; use middle::ty; use middle::typeck::astconv::AstConv; use middle::typeck::infer; +use driver::session::Session; use std::collections::HashMap; use std::rc::Rc; -use std::to_str::ToStr; use std::cell::RefCell; 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::attr::AttrMetaMethods; +use syntax::attr; use syntax::codemap::Span; -use syntax::parse::token::InternedString; use syntax::visit::{Visitor, FnKind}; 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, + } + ) +) + +#[macro_export] +macro_rules! declare_lint ( + // FIXME(#14660): deduplicate + (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 method checks a single syntax -/// node, and should not invoke methods recursively (unlike `Visitor`). Each -/// method has a default do-nothing implementation. The trait also contains a -/// few lint-specific methods with no equivalent in `Visitor`. +/// 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 { + /// 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_ident(&mut self, _: &Context, _: Span, _: ast::Ident) { } fn check_mod(&mut self, _: &Context, _: &ast::Mod, _: Span, _: ast::NodeId) { } @@ -115,65 +174,41 @@ trait LintPass { type LintPassObject = Box; -#[deriving(Clone, Show, PartialEq, PartialOrd, Eq, Ord, Hash)] -pub enum LintId { - CTypes, - UnusedImports, - UnnecessaryQualification, - 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, - - DeprecatedOwnedVector, - - Warnings, - - RawPointerDeriving, +/// Identifies a lint known to the compiler. +#[deriving(Clone)] +pub struct LintId { + // Identity is based on pointer equality of this field. + lint: &'static Lint, } -pub fn level_to_str(lv: Level) -> &'static str { - match lv { - Allow => "allow", - Warn => "warn", - Deny => "deny", - Forbid => "forbid" +impl PartialEq for LintId { + fn eq(&self, other: &LintId) -> bool { + (self.lint as *Lint) == (other.lint as *Lint) + } + + fn ne(&self, other: &LintId) -> bool { + (self.lint as *Lint) != (other.lint as *Lint) + } +} + +impl Eq for LintId { } + +impl Hash 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 } } @@ -182,14 +217,26 @@ pub enum Level { Allow, Warn, Deny, Forbid } -#[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)] -pub struct LintSpec { - pub default: Level, - pub lint: LintId, - pub desc: &'static str, -} +impl Level { + pub fn as_str(self) -> &'static str { + match self { + Allow => "allow", + Warn => "warn", + Deny => "deny", + Forbid => "forbid", + } + } -pub type LintDict = HashMap<&'static str, LintSpec>; + pub fn from_str(x: &str) -> Option { + 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 #[deriving(PartialEq)] @@ -199,415 +246,102 @@ pub enum LintSource { CommandLine } -static lint_table: &'static [(&'static str, LintSpec)] = &[ - ("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", - default: Warn - }), - - ("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, - }), - - ("deprecated_owned_vector", - LintSpec { - lint: DeprecatedOwnedVector, - desc: "use of a `~[T]` vector", - 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() -} +pub type LevelSource = (Level, LintSource); struct Context<'a> { - /// All known lint modes (string versions) - dict: LintDict, - /// Current levels of each lint warning - cur: SmallIntMap<(Level, LintSource)>, - /// Context we're checking in (used to access fields like sess) + /// Trait objects for each lint pass. + lint_objects: Vec>, + + /// Lints indexed by name. + lints_by_name: HashMap<&'static str, LintId>, + + /// Current levels of each lint, and where they were set. + levels: HashMap, + + /// Context we're checking in (used to access fields like sess). tcx: &'a 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. - level_stack: Vec<(LintId, Level, LintSource)>, + level_stack: Vec<(LintId, LevelSource)>, /// Level of lints for certain NodeIds, stored here because the body of /// the lint needs to run in trans. - node_levels: RefCell>, - - /// Trait objects for each lint. - lints: Vec>, + node_levels: RefCell>, } -/// 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),*) => ( - for tl in $cx.lints.iter() { - tl.borrow_mut().$f($cx, $($args),*); + for obj in $cx.lint_objects.iter() { + obj.borrow_mut().$f($cx, $($args),*); } )) -pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span, - lint_str: &str, tcx: &ty::ctxt) { +/// Emit a lint as a `span_warn` or `span_err` (or not at all) +/// 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 } let mut note = None; - let msg = match src { + let msg = match source { Default => { format!("{}, \\#[{}({})] on by default", msg, - level_to_str(level), lint_str) + level.as_str(), lint.name) }, CommandLine => { format!("{} [-{} {}]", msg, match level { Warn => 'W', Deny => 'D', Forbid => 'F', Allow => fail!() - }, lint_str.replace("_", "-")) + }, lint.name.replace("_", "-")) }, Node(src) => { note = Some(src); - msg.to_str() + msg.to_string() } }; match level { - Warn => { tcx.sess.span_warn(span, msg.as_slice()); } - Deny | Forbid => { tcx.sess.span_err(span, msg.as_slice()); } + Warn => { sess.span_warn(span, msg.as_slice()); } + Deny | Forbid => { sess.span_err(span, msg.as_slice()); } Allow => fail!(), } - for &span in note.iter() { - tcx.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; - } + for span in note.move_iter() { + sess.span_note(span, "lint level defined here"); } - - fail!("unrecognized lint: {}", lint); } impl<'a> Context<'a> { - fn get_level(&self, lint: LintId) -> Level { - match self.cur.find(&(lint as uint)) { - Some(&(lvl, _)) => lvl, - None => Allow - } - } - - fn get_source(&self, lint: LintId) -> LintSource { - match self.cur.find(&(lint as uint)) { - Some(&(_, src)) => src, - None => Default + fn get_level_source(&self, lint: LintId) -> LevelSource { + match self.levels.find(&lint) { + Some(&s) => s, + None => (Allow, Default), } } - fn set_level(&mut self, lint: LintId, level: Level, src: LintSource) { - if level == Allow { - self.cur.remove(&(lint as uint)); + fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) { + if lvlsrc.val0() == Allow { + self.levels.remove(&lint); } else { - self.cur.insert(lint as uint, (level, src)); + self.levels.insert(lint, lvlsrc); } } - fn lint_to_str(&self, lint: LintId) -> &'static str { - for (k, v) in self.dict.iter() { - if v.lint == lint { - return *k; - } - } - 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), + fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) { + let (level, src) = match self.levels.find(&LintId::of(lint)) { + None => return, + Some(&(Warn, src)) + => (self.get_level_source(LintId::of(builtin::warnings)).val0(), src), 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); } /** @@ -622,35 +356,22 @@ impl<'a> Context<'a> { // 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 lint_attrs = self.gather_lint_attrs(attrs); let mut pushed = 0u; - each_lint(&self.tcx.sess, attrs, |meta, level, lintname| { - match self.dict.find_equiv(&lintname) { - None => { - self.span_lint( - UnrecognizedLint, - meta.span, - format!("unknown `{}` attribute: `{}`", - level_to_str(level), lintname).as_slice()); - } - Some(lint) => { - let lint = lint.lint; - let now = self.get_level(lint); - 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)); - } - } + for (lint_id, level, span) in lint_attrs.move_iter() { + let now = self.get_level_source(lint_id).val0(); + if now == Forbid && level != Forbid { + let lint_name = lint_id.as_str(); + self.tcx.sess.span_err(span, + format!("{}({}) overruled by outer forbid({})", + level.as_str(), lint_name, lint_name).as_slice()); + } else if now != level { + let src = self.get_level_source(lint_id).val1(); + self.level_stack.push((lint_id, (now, src))); + pushed += 1; + self.set_level(lint_id, (level, Node(span))); } - true - }); + } run_lints!(self, enter_lint_attrs, attrs); f(self); @@ -658,8 +379,8 @@ impl<'a> Context<'a> { // rollback for _ in range(0, pushed) { - let (lint, lvl, src) = self.level_stack.pop().unwrap(); - self.set_level(lint, lvl, src); + let (lint, lvlsrc) = self.level_stack.pop().unwrap(); + self.set_level(lint, lvlsrc); } } @@ -672,65 +393,49 @@ impl<'a> Context<'a> { f(&mut v); } - fn insert_node_level(&self, id: ast::NodeId, lint: LintId, lvl: Level, src: LintSource) { - self.node_levels.borrow_mut().insert((id, lint), (lvl, src)); + fn insert_node_level(&self, id: ast::NodeId, lint: LintId, lvlsrc: LevelSource) { + self.node_levels.borrow_mut().insert((id, lint), lvlsrc); } -} -/// Check that every lint from the list of attributes satisfies `f`. -/// Return true if that's the case. Otherwise return false. -pub fn each_lint(sess: &session::Session, - attrs: &[ast::Attribute], - f: |@ast::MetaItem, Level, InternedString| -> bool) - -> bool { - let xs = [Allow, Warn, Deny, Forbid]; - for &level in xs.iter() { - let level_name = level_to_str(level); - for attr in attrs.iter().filter(|m| m.check_name(level_name)) { + fn gather_lint_attrs(&mut self, attrs: &[ast::Attribute]) -> Vec<(LintId, Level, Span)> { + // Doing this as an iterator is messy due to multiple borrowing. + // Allocating and copying these should be quick. + let mut out = vec!(); + for attr in attrs.iter() { + let level = match Level::from_str(attr.name().get()) { + None => continue, + Some(lvl) => lvl, + }; + + attr::mark_used(attr); + let meta = attr.node.value; let metas = match meta.node { ast::MetaList(_, ref metas) => metas, _ => { - sess.span_err(meta.span, "malformed lint attribute"); + self.tcx.sess.span_err(meta.span, "malformed lint attribute"); continue; } }; + for meta in metas.iter() { match meta.node { - ast::MetaWord(ref lintname) => { - if !f(*meta, level, (*lintname).clone()) { - return false; + ast::MetaWord(ref lint_name) => { + match self.lints_by_name.find_equiv(lint_name) { + 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()), } } - _ => { - sess.span_err(meta.span, "malformed lint attribute"); - } + _ => self.tcx.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>{ @@ -919,59 +624,90 @@ impl<'a> Visitor<()> for Context<'a> { } } +// Output any lints that were previously added to the session. impl<'a> IdVisitingOperation for Context<'a> { fn visit_id(&self, id: ast::NodeId) { match self.tcx.sess.lints.borrow_mut().pop(&id) { None => {} - Some(l) => { - for (lint, span, msg) in l.move_iter() { - self.span_lint(lint, span, msg.as_slice()) + Some(lints) => { + for (lint_id, span, msg) in lints.move_iter() { + self.span_lint(lint_id.lint, span, msg.as_slice()) } } } } } -pub fn check_crate(tcx: &ty::ctxt, - exported_items: &ExportedItems, - krate: &ast::Crate) { +fn builtin_lints() -> Vec> { macro_rules! builtin_lints (( $($name:ident),*, ) => ( vec!($( { let obj: builtin::$name = Default::default(); - RefCell::new(box obj as LintPassObject) + box obj as LintPassObject } ),*) )) - let builtin_lints = builtin_lints!( - GatherNodeLevels, WhileTrue, UnusedCasts, TypeLimits, CTypes, - HeapMemory, RawPointerDeriving, UnusedAttribute, - PathStatement, UnusedMustUse, DeprecatedOwnedVector, - NonCamelCaseTypes, NonSnakeCaseFunctions, NonUppercaseStatics, - UppercaseVariables, UnnecessaryParens, UnusedUnsafe, UnsafeBlock, - UnusedMut, UnnecessaryAllocation, MissingDoc, Stability, - ); + builtin_lints!( + WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory, + RawPointerDeriving, UnusedAttribute, PathStatement, + UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes, + NonSnakeCaseFunctions, NonUppercaseStatics, + NonUppercasePatternStatics, UppercaseVariables, + 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 { - dict: get_lint_dict(), - cur: SmallIntMap::new(), + lint_objects: lints, + lints_by_name: HashMap::new(), + levels: HashMap::new(), tcx: tcx, level_stack: Vec::new(), node_levels: RefCell::new(HashMap::new()), - lints: builtin_lints, }; - // Install default lint levels, followed by the command line levels, and - // then actually visit the whole crate. - for (_, spec) in cx.dict.iter() { - if spec.default != Allow { - cx.cur.insert(spec.lint as uint, (spec.default, Default)); + // Index the lints by name, and set the default levels. + for obj in cx.lint_objects.iter() { + for &lint in obj.borrow_mut().get_lints().iter() { + let id = LintId::of(lint); + 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.visit_id(ast::CRATE_NODE_ID); cx.visit_ids(|v| { @@ -990,8 +726,10 @@ pub fn check_crate(tcx: &ty::ctxt, // in the iteration code. for (id, v) in tcx.sess.lints.borrow().iter() { for &(lint, span, ref msg) in v.iter() { - tcx.sess.span_bug(span, format!("unprocessed lint {} at {}: {}", - lint, tcx.map.node_to_str(*id), *msg).as_slice()) + tcx.sess.span_bug(span, + format!("unprocessed lint {} at {}: {}", + lint.as_str(), tcx.map.node_to_str(*id), *msg) + .as_slice()) } } diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 4f39616510da9..2f2e147f069e1 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -13,7 +13,7 @@ // from live codes are live, and everything else is dead. use middle::def; -use lint::{Allow, contains_lint, DeadCode}; +use lint; use middle::privacy; use middle::ty; use middle::typeck; @@ -23,14 +23,13 @@ use std::collections::HashSet; use syntax::ast; use syntax::ast_map; use syntax::ast_util::{local_def, is_local}; +use syntax::attr::AttrMetaMethods; use syntax::attr; use syntax::codemap; use syntax::parse::token; use syntax::visit::Visitor; 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 // explored. For example, if it's a live NodeItem that is a // function, then we should explore its block to check for codes that @@ -248,8 +247,24 @@ impl<'a> Visitor<()> for MarkSymbolVisitor<'a> { } fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool { - contains_lint(attrs, Allow, DEAD_CODE_LINT_STR) - || attr::contains_name(attrs.as_slice(), "lang") + if 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 @@ -428,7 +443,7 @@ impl<'a> DeadVisitor<'a> { ident: ast::Ident) { self.tcx .sess - .add_lint(DeadCode, + .add_lint(lint::builtin::dead_code, id, span, format!("code is never used: `{}`", diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index fd1803240ab5c..cb67a465d5029 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -107,7 +107,7 @@ use middle::freevars; use middle::mem_categorization::Typer; use middle::pat_util; use middle::ty; -use lint::{UnusedVariable, DeadAssignment}; +use lint; use util::nodemap::NodeMap; use std::mem::transmute; @@ -1559,11 +1559,11 @@ impl<'a> Liveness<'a> { }; 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", *name)); } 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)); } } @@ -1581,7 +1581,7 @@ impl<'a> Liveness<'a> { if self.live_on_exit(ln, var).is_none() { let r = self.should_warn(var); 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)); } } diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index fa9980b566baa..f4cdd3a88f784 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -1392,7 +1392,7 @@ impl<'a> Visitor<()> for VisiblePrivateTypesVisitor<'a> { ast::TyPath(ref p, _, path_id) => { if self.path_is_private_type(path_id) { self.tcx.sess.add_lint( - lint::VisiblePrivateTypes, + lint::builtin::visible_private_types, path_id, p.span, "private type in exported type \ signature".to_string()); diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 9bf87da8c4e2c..d64336aa9feb9 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -15,8 +15,8 @@ use metadata::csearch; use metadata::decoder::{DefLike, DlDef, DlField, DlImpl}; use middle::def::*; use middle::lang_items::LanguageItems; -use lint::{UnnecessaryQualification, UnusedImports}; use middle::pat_util::pat_bindings; +use lint; use util::nodemap::{NodeMap, DefIdSet, FnvHashMap}; use syntax::ast::*; @@ -4592,7 +4592,7 @@ impl<'a> Resolver<'a> { match (def, unqualified_def) { (Some((d, _)), Some((ud, _))) if d == ud => { self.session - .add_lint(UnnecessaryQualification, + .add_lint(lint::builtin::unnecessary_qualification, id, path.span, "unnecessary qualification".to_string()); @@ -5427,7 +5427,7 @@ impl<'a> Resolver<'a> { if !self.used_imports.contains(&(id, TypeNS)) && !self.used_imports.contains(&(id, ValueNS)) { self.session - .add_lint(UnusedImports, + .add_lint(lint::builtin::unused_imports, id, p.span, "unused import".to_string()); @@ -5451,7 +5451,7 @@ impl<'a> Resolver<'a> { if !self.used_imports.contains(&(id, TypeNS)) && !self.used_imports.contains(&(id, ValueNS)) { - self.session.add_lint(UnusedImports, + self.session.add_lint(lint::builtin::unused_imports, id, span, "unused import".to_string()); diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 7c8de832ea30b..973f72feeaeb2 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1562,48 +1562,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) { let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully - let (lvl, src) = ccx.tcx.node_lint_levels.borrow() - .find(&(id, lint::VariantSizeDifference)) - .map_or((lint::Allow, lint::Default), |&(lvl,src)| (lvl, src)); - - if lvl != lint::Allow { - let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id)); - match *avar { - adt::General(_, ref variants) => { - for var in variants.iter() { - let mut size = 0; - for field in var.fields.iter().skip(1) { - // skip the discriminant - size += llsize_of_real(ccx, sizing_type_of(ccx, *field)); + let levels = ccx.tcx.node_lint_levels.borrow(); + match levels.find(&(id, lint::LintId::of(lint::builtin::variant_size_difference))) { + None | Some(&(lint::Allow, _)) => (), + Some(&lvlsrc) => { + let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id)); + match *avar { + adt::General(_, ref variants) => { + for var in variants.iter() { + let mut size = 0; + for field in var.fields.iter().skip(1) { + // skip the discriminant + 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), - |(l, s, li), (idx, &size)| - if size > l { - (size, l, idx) - } else if size > s { - (l, size, li) - } else { - (l, s, li) - } - ); - - // we only warn if the largest variant is at least thrice as large as - // the second-largest. - if largest > slargest * 3 && slargest > 0 { - lint::emit_lint(lvl, src, - format!("enum variant is more than three times larger \ - ({} bytes) than the next largest (ignoring padding)", - largest).as_slice(), - sp, lint::lint_to_str(lint::VariantSizeDifference), ccx.tcx()); - - ccx.sess().span_note(enum_def.variants.get(largest_index).span, - "this variant is the largest"); + let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0), + |(l, s, li), (idx, &size)| + if size > l { + (size, l, idx) + } else if size > s { + (l, size, li) + } else { + (l, s, li) + } + ); + + // we only warn if the largest variant is at least thrice as large as + // the second-largest. + if largest > slargest * 3 && slargest > 0 { + // Use lint::emit_lint rather than sess.add_lint because the lint-printing + // pass for the latter already ran. + lint::emit_lint(&ccx.tcx().sess, lint::builtin::variant_size_difference, + lvlsrc, sp, + 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, + "this variant is the largest"); + } } } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 889a2b35f58d2..279ce89c92cc8 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -357,7 +357,7 @@ pub struct ctxt { pub dependency_formats: RefCell, pub node_lint_levels: RefCell>, + lint::LevelSource>>, } pub enum tbox_flag { diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 0889020bfd999..5675c009dd441 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -81,7 +81,6 @@ use middle::const_eval; use middle::def; use middle::lang_items::{ExchangeHeapLangItem, GcLangItem}; use middle::lang_items::{ManagedHeapLangItem}; -use lint::UnreachableCode; use middle::pat_util::pat_id_map; use middle::pat_util; use middle::subst; @@ -112,6 +111,7 @@ use middle::typeck::no_params; use middle::typeck::{require_same_types, vtable_map}; use middle::typeck::{MethodCall, MethodMap}; use middle::lang_items::TypeIdLangItem; +use lint; use util::common::{block_query, indenter, loop_query}; use util::ppaux; use util::ppaux::{UserString, Repr}; @@ -3638,7 +3638,7 @@ pub fn check_block_with_expected(fcx: &FnCtxt, fcx.ccx .tcx .sess - .add_lint(UnreachableCode, + .add_lint(lint::builtin::unreachable_code, s_id, s.span, "unreachable statement".to_string()); @@ -3665,7 +3665,7 @@ pub fn check_block_with_expected(fcx: &FnCtxt, fcx.ccx .tcx .sess - .add_lint(UnreachableCode, + .add_lint(lint::builtin::unreachable_code, e.id, e.span, "unreachable expression".to_string()); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c07a902d06700..b47a097f4ecad 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -74,11 +74,13 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet, cfgs: Vec) let input = FileInput(cpath.clone()); + let warning_lint = lint::builtin::warnings.name.to_string(); + let sessopts = driver::config::Options { maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), addl_lib_search_paths: RefCell::new(libs), 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() }; From 15458696a70f05871d003ae5cc7acb823039f23e Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Tue, 10 Jun 2014 14:03:19 -0700 Subject: [PATCH 06/24] Store the registered lints in the Session --- src/librustc/driver/driver.rs | 33 ++++-- src/librustc/driver/mod.rs | 63 ++++++---- src/librustc/driver/session.rs | 9 +- src/librustc/lint/builtin.rs | 2 +- src/librustc/lint/mod.rs | 211 +++++++++++++++++++-------------- src/librustdoc/core.rs | 6 +- src/librustdoc/test.rs | 3 +- 7 files changed, 203 insertions(+), 124 deletions(-) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 5c6a6eee50c4d..2ac78f5c24aab 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -79,8 +79,12 @@ pub fn compile_input(sess: Session, &sess); let id = link::find_crate_id(krate.attrs.as_slice(), outputs.out_filestem.as_slice()); - let (expanded_crate, ast_map) = - phase_2_configure_and_expand(&sess, krate, &id); + let (expanded_crate, ast_map) + = match phase_2_configure_and_expand(&sess, krate, &id) { + None => return, + Some(p) => p, + }; + (outputs, expanded_crate, ast_map) }; write_out_deps(&sess, input, &outputs, &expanded_crate); @@ -172,10 +176,12 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input) /// syntax expansion, secondary `cfg` expansion, synthesis of a test /// harness if one is to be provided and injection of a dependency on the /// standard library and prelude. +/// +/// Returns `None` if we're aborting after handling -W help. pub fn phase_2_configure_and_expand(sess: &Session, mut krate: ast::Crate, crate_id: &CrateId) - -> (ast::Crate, syntax::ast_map::Map) { + -> Option<(ast::Crate, syntax::ast_map::Map)> { let time_passes = sess.time_passes(); *sess.crate_types.borrow_mut() = collect_crate_types(sess, krate.attrs.as_slice()); @@ -211,6 +217,17 @@ pub fn phase_2_configure_and_expand(sess: &Session, let Registry { syntax_exts, .. } = registry; + // Process command line flags for lints. + // Do this here because we will have lint plugins eventually. + if sess.opts.describe_lints { + super::describe_lints(&*sess.lint_store.borrow()); + return None; + } + sess.lint_store.borrow_mut().process_command_line(sess); + + // Abort if there are errors from lint processing or a plugin registrar. + sess.abort_if_errors(); + krate = time(time_passes, "expansion", (krate, macros, syntax_exts), |(krate, macros, syntax_exts)| { // Windows dlls do not have rpaths, so they don't know how to find their @@ -253,7 +270,7 @@ pub fn phase_2_configure_and_expand(sess: &Session, krate.encode(&mut json).unwrap(); } - (krate, map) + Some((krate, map)) } pub struct CrateAnalysis { @@ -613,9 +630,11 @@ pub fn pretty_print_input(sess: Session, let (krate, ast_map, is_expanded) = match ppm { PpmExpanded | PpmExpandedIdentified | PpmTyped | PpmFlowGraph(_) => { - let (krate, ast_map) = phase_2_configure_and_expand(&sess, - krate, - &id); + let (krate, ast_map) + = match phase_2_configure_and_expand(&sess, krate, &id) { + None => return, + Some(p) => p, + }; (krate, Some(ast_map), true) } _ => (krate, None, false) diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs index e5c4657490292..22a1c5bc98e6e 100644 --- a/src/librustc/driver/mod.rs +++ b/src/librustc/driver/mod.rs @@ -13,6 +13,7 @@ pub use syntax::diagnostic; use back::link; use driver::driver::{Input, FileInput, StrInput}; use driver::session::{Session, build_session}; +use lint::Lint; use lint; use metadata; @@ -48,15 +49,18 @@ fn run_compiler(args: &[String]) { Some(matches) => matches, 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() { - 0u => early_error("no input filename given"), + 0u => { + if sopts.describe_lints { + let mut ls = lint::LintStore::new(); + ls.register_builtin(None); + describe_lints(&ls); + return; + } + early_error("no input filename given"); + } 1u => { let ifile = matches.free.get(0).as_slice(); if ifile == "-" { @@ -128,43 +132,56 @@ Additional help: config::optgroups().as_slice())); } -fn describe_lints() { +fn describe_lints(lint_store: &lint::LintStore) { println!(" Available lint options: -W Warn about -A Allow -D Deny -F Forbid (deny, and deny all overrides) + "); - let mut builtin_specs = lint::builtin_lint_specs(); - builtin_specs.sort_by(|x, y| { - match x.default_level.cmp(&y.default_level) { - Equal => x.name.cmp(&y.name), - r => r, - } - }); + fn sort_lints(lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> { + let mut lints: Vec<_> = lints.move_iter().map(|(x, _)| x).collect(); + lints.sort_by(|x: &&Lint, y: &&Lint| { + match x.default_level.cmp(&y.default_level) { + Equal => x.name.cmp(&y.name), + r => r, + } + }); + lints + } + + let (_plugin, builtin) = lint_store.get_lints().partitioned(|&(_, p)| p); + // let plugin = sort_lints(plugin); + let builtin = sort_lints(builtin); // FIXME: What if someone uses combining characters or East Asian fullwidth // characters in a lint name?!?!? - let max_name_len = builtin_specs.iter() + let max_name_len = builtin.iter() .map(|&s| s.name.char_len()) .max().unwrap_or(0); let padded = |x: &str| { format!("{}{}", " ".repeat(max_name_len - x.char_len()), x) }; - println!("\nAvailable lint checks:\n"); + println!("Lint checks provided by rustc:\n"); println!(" {} {:7.7s} {}", padded("name"), "default", "meaning"); println!(" {} {:7.7s} {}", padded("----"), "-------", "-------"); - println!(""); - for spec in builtin_specs.move_iter() { - let name = spec.name.replace("_", "-"); - println!(" {} {:7.7s} {}", - padded(name.as_slice()), spec.default_level.as_str(), spec.desc); - } - println!(""); + let print_lints = |lints: Vec<&Lint>| { + for lint in lints.move_iter() { + let name = lint.name.replace("_", "-"); + println!(" {} {:7.7s} {}", + padded(name.as_slice()), lint.default_level.as_str(), lint.desc); + } + println!("\n"); + }; + + print_lints(builtin); + + // Describe lint plugins here once they exist. } fn describe_debug_flags() { diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index bbb145c9f151c..08ed4e27efa41 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -42,6 +42,7 @@ pub struct Session { // expected to be absolute. `None` means that there is no source file. pub local_crate_source_file: Option, pub working_dir: Path, + pub lint_store: RefCell, pub lints: RefCell>>, pub node_id: Cell, pub crate_types: RefCell>, @@ -225,7 +226,7 @@ pub fn build_session_(sopts: config::Options, } ); - Session { + let sess = Session { targ_cfg: target_cfg, opts: sopts, cstore: CStore::new(token::get_ident_interner()), @@ -237,12 +238,16 @@ pub fn build_session_(sopts: config::Options, default_sysroot: default_sysroot, local_crate_source_file: local_crate_source_file, working_dir: os::getcwd(), + lint_store: RefCell::new(lint::LintStore::new()), lints: RefCell::new(NodeMap::new()), node_id: Cell::new(1), crate_types: RefCell::new(Vec::new()), features: front::feature_gate::Features::new(), recursion_limit: Cell::new(64), - } + }; + + sess.lint_store.borrow_mut().register_builtin(Some(&sess)); + sess } // Seems out of place, but it uses session, so I'm putting it here diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 135cffdc86fc2..683ac1ea01d70 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1519,7 +1519,7 @@ impl LintPass for GatherNodeLevels { match it.node { ast::ItemEnum(..) => { let lint_id = lint::LintId::of(variant_size_difference); - match cx.get_level_source(lint_id) { + match cx.lints.get_level_source(lint_id) { lvlsrc @ (lvl, _) if lvl != lint::Allow => { cx.insert_node_level(it.id, lint_id, lvlsrc); }, diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 68433258715f6..2078e793ee10e 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -48,6 +48,7 @@ use middle::ty; use middle::typeck::astconv::AstConv; use middle::typeck::infer; use driver::session::Session; +use driver::early_error; use std::collections::HashMap; use std::rc::Rc; @@ -56,6 +57,7 @@ use std::default::Default; use std::hash::Hash; use std::tuple::Tuple2; use std::hash; +use std::mem; use syntax::ast_util::IdVisitingOperation; use syntax::attr::AttrMetaMethods; use syntax::attr; @@ -113,7 +115,7 @@ pub struct Lint { pub desc: &'static str, } -type LintArray = &'static [&'static Lint]; +pub 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`). @@ -121,7 +123,7 @@ type LintArray = &'static [&'static Lint]; // // FIXME: eliminate the duplication with `Visitor`. But this also // contains a few lint-specific methods with no equivalent in `Visitor`. -trait LintPass { +pub 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. @@ -248,15 +250,117 @@ pub enum LintSource { pub type LevelSource = (Level, LintSource); -struct Context<'a> { +/// Information about the registered lints. +/// This is basically the subset of `Context` that we can +/// build early in the compile pipeline. +pub struct LintStore { + /// Registered lints. The bool is true if the lint was + /// added by a plugin. + lints: Vec<(&'static Lint, bool)>, + /// Trait objects for each lint pass. - lint_objects: Vec>, + passes: Vec>, /// Lints indexed by name. - lints_by_name: HashMap<&'static str, LintId>, + by_name: HashMap<&'static str, LintId>, /// Current levels of each lint, and where they were set. levels: HashMap, +} + +impl LintStore { + fn get_level_source(&self, lint: LintId) -> LevelSource { + match self.levels.find(&lint) { + Some(&s) => s, + None => (Allow, Default), + } + } + + fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) { + if lvlsrc.val0() == Allow { + self.levels.remove(&lint); + } else { + self.levels.insert(lint, lvlsrc); + } + } + + pub fn new() -> LintStore { + LintStore { + lints: vec!(), + passes: vec!(), + by_name: HashMap::new(), + levels: HashMap::new(), + } + } + + pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] { + self.lints.as_slice() + } + + pub fn register_pass(&mut self, sess: Option<&Session>, + from_plugin: bool, pass: LintPassObject) { + for &lint in pass.get_lints().iter() { + self.lints.push((lint, from_plugin)); + + let id = LintId::of(lint); + if !self.by_name.insert(lint.name, id) { + let msg = format!("duplicate specification of lint {}", lint.name); + match (sess, from_plugin) { + // We load builtin lints first, so a duplicate is a compiler bug. + // Use early_error when handling -W help with no crate. + (None, _) => early_error(msg.as_slice()), + (Some(sess), false) => sess.bug(msg.as_slice()), + + // A duplicate name from a plugin is a user error. + (Some(sess), true) => sess.err(msg.as_slice()), + } + } + + if lint.default_level != Allow { + self.levels.insert(id, (lint.default_level, Default)); + } + } + self.passes.push(RefCell::new(pass)); + } + + pub fn register_builtin(&mut self, sess: Option<&Session>) { + macro_rules! add_builtin_lints ( ( $sess:ident, $($name:ident),*, ) => ( + {$( + { + let obj: builtin::$name = Default::default(); + self.register_pass($sess, false, box obj as LintPassObject); + }; + )*} + )) + + add_builtin_lints!(sess, + WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory, + RawPointerDeriving, UnusedAttribute, PathStatement, + UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes, + NonSnakeCaseFunctions, NonUppercaseStatics, + NonUppercasePatternStatics, UppercaseVariables, + UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut, + UnnecessaryAllocation, MissingDoc, Stability, + + GatherNodeLevels, HardwiredLints, + ) + } + + pub fn process_command_line(&mut self, sess: &Session) { + for &(ref lint_name, level) in sess.opts.lint_opts.iter() { + match self.by_name.find_equiv(&lint_name.as_slice()) { + Some(&lint_id) => self.set_level(lint_id, (level, CommandLine)), + None => sess.err(format!("unknown {} flag: {}", + level.as_str(), lint_name).as_slice()), + } + } + } +} + +/// Context for lint checking. +pub struct Context<'a> { + /// The store of registered lints. + lints: LintStore, /// Context we're checking in (used to access fields like sess). tcx: &'a ty::ctxt, @@ -273,7 +377,7 @@ struct Context<'a> { /// Convenience macro for calling a `LintPass` method on every pass in the context. macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ( - for obj in $cx.lint_objects.iter() { + for obj in $cx.lints.passes.iter() { obj.borrow_mut().$f($cx, $($args),*); } )) @@ -318,26 +422,11 @@ pub fn emit_lint(sess: &Session, lint: &'static Lint, } impl<'a> Context<'a> { - fn get_level_source(&self, lint: LintId) -> LevelSource { - match self.levels.find(&lint) { - Some(&s) => s, - None => (Allow, Default), - } - } - - fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) { - if lvlsrc.val0() == Allow { - self.levels.remove(&lint); - } else { - self.levels.insert(lint, lvlsrc); - } - } - - fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) { - let (level, src) = match self.levels.find(&LintId::of(lint)) { + pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) { + let (level, src) = match self.lints.levels.find(&LintId::of(lint)) { None => return, Some(&(Warn, src)) - => (self.get_level_source(LintId::of(builtin::warnings)).val0(), src), + => (self.lints.get_level_source(LintId::of(builtin::warnings)).val0(), src), Some(&pair) => pair, }; @@ -359,17 +448,17 @@ impl<'a> Context<'a> { let lint_attrs = self.gather_lint_attrs(attrs); let mut pushed = 0u; for (lint_id, level, span) in lint_attrs.move_iter() { - let now = self.get_level_source(lint_id).val0(); + let now = self.lints.get_level_source(lint_id).val0(); if now == Forbid && level != Forbid { let lint_name = lint_id.as_str(); self.tcx.sess.span_err(span, format!("{}({}) overruled by outer forbid({})", level.as_str(), lint_name, lint_name).as_slice()); } else if now != level { - let src = self.get_level_source(lint_id).val1(); + let src = self.lints.get_level_source(lint_id).val1(); self.level_stack.push((lint_id, (now, src))); pushed += 1; - self.set_level(lint_id, (level, Node(span))); + self.lints.set_level(lint_id, (level, Node(span))); } } @@ -380,7 +469,7 @@ impl<'a> Context<'a> { // rollback for _ in range(0, pushed) { let (lint, lvlsrc) = self.level_stack.pop().unwrap(); - self.set_level(lint, lvlsrc); + self.lints.set_level(lint, lvlsrc); } } @@ -421,7 +510,7 @@ impl<'a> Context<'a> { for meta in metas.iter() { match meta.node { ast::MetaWord(ref lint_name) => { - match self.lints_by_name.find_equiv(lint_name) { + match self.lints.by_name.find_equiv(lint_name) { Some(lint_id) => out.push((*lint_id, level, meta.span)), None => self.span_lint(builtin::unrecognized_lint, @@ -638,75 +727,21 @@ impl<'a> IdVisitingOperation for Context<'a> { } } -fn builtin_lints() -> Vec> { - macro_rules! builtin_lints (( $($name:ident),*, ) => ( - vec!($( - { - let obj: builtin::$name = Default::default(); - box obj as LintPassObject - } - ),*) - )) - - builtin_lints!( - WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory, - RawPointerDeriving, UnusedAttribute, PathStatement, - UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes, - NonSnakeCaseFunctions, NonUppercaseStatics, - NonUppercasePatternStatics, UppercaseVariables, - 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(); + + // We want to own the lint store, so move it out of the session. + let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), + LintStore::new()); let mut cx = Context { - lint_objects: lints, - lints_by_name: HashMap::new(), - levels: HashMap::new(), + lints: lint_store, tcx: tcx, level_stack: Vec::new(), node_levels: RefCell::new(HashMap::new()), }; - // Index the lints by name, and set the default levels. - for obj in cx.lint_objects.iter() { - for &lint in obj.borrow_mut().get_lints().iter() { - let id = LintId::of(lint); - 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)); - } - } - } - - // 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.visit_id(ast::CRATE_NODE_ID); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b47a097f4ecad..32ea70412ff90 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -101,8 +101,10 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet, cfgs: Vec) } let krate = phase_1_parse_input(&sess, cfg, &input); - let (krate, ast_map) = phase_2_configure_and_expand(&sess, krate, - &from_str("rustdoc").unwrap()); + let (krate, ast_map) + = phase_2_configure_and_expand(&sess, krate, &from_str("rustdoc").unwrap()) + .expect("phase_2_configure_and_expand aborted in rustdoc!"); + let driver::driver::CrateAnalysis { exported_items, public_items, ty_cx, .. } = phase_3_run_analysis_passes(sess, &krate, ast_map); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 0d77764127465..86bc17b3afdf8 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -68,7 +68,8 @@ pub fn run(input: &str, })); let krate = driver::phase_1_parse_input(&sess, cfg, &input); let (krate, _) = driver::phase_2_configure_and_expand(&sess, krate, - &from_str("rustdoc-test").unwrap()); + &from_str("rustdoc-test").unwrap()) + .expect("phase_2_configure_and_expand aborted in rustdoc!"); let ctx = @core::DocContext { krate: krate, From 129aa8b9482e8a3b0978896cd1cb8e05671cd025 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 6 Jun 2014 15:49:48 -0700 Subject: [PATCH 07/24] Clean up and document the public lint API Also change some code formatting. lint::builtin becomes a sibling of lint::context in order to ensure that lints implemented there use the same public API as lint plugins. --- src/librustc/lint/builtin.rs | 145 +++---- src/librustc/lint/context.rs | 633 +++++++++++++++++++++++++++++ src/librustc/lint/mod.rs | 652 +++--------------------------- src/librustc/middle/trans/base.rs | 12 +- 4 files changed, 767 insertions(+), 675 deletions(-) create mode 100644 src/librustc/lint/context.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 683ac1ea01d70..4d2abb9951e5c 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -9,18 +9,31 @@ // except according to those terms. //! Lints built in to rustc. +//! +//! This is a sibling of `lint::context` in order to ensure that +//! lints implemented here use the same public API as lint plugins. +//! +//! To add a new lint to rustc, declare it here using `declare_lint!()`. +//! Then add code to emit the new lint in the appropriate circumstances. +//! You can do that in an existing `LintPass` if it makes sense, or in +//! a new `LintPass`, or using `Session::add_lint` elsewhere in the +//! compiler. Only do the latter if the check can't be written cleanly +//! as a `LintPass`. +//! +//! If you define a new `LintPass`, you will also need to add it to the +//! `add_builtin_lints!()` invocation in `context.rs`. That macro +//! requires a `Default` impl for your `LintPass` type. use metadata::csearch; use middle::def::*; use middle::trans::adt; // for `adt::is_ffi_safe` -use middle::typeck::astconv::{ast_ty_to_ty, AstConv}; +use middle::typeck::astconv::ast_ty_to_ty; use middle::typeck::infer; use middle::privacy::ExportedItems; use middle::{typeck, ty, def, pat_util}; use util::ppaux::{ty_to_str}; use util::nodemap::NodeSet; use lint::{Context, LintPass, LintArray}; -use lint; use std::cmp; use std::collections::HashMap; @@ -88,8 +101,8 @@ impl LintPass for UnusedCasts { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { match e.node { ast::ExprCast(expr, 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 { + let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.type_context()), ty); + if ty::get(ty::expr_ty(cx.type_context(), expr)).sty == ty::get(t_t).sty { cx.span_lint(unnecessary_typecast, ty.span, "unnecessary type cast"); } } @@ -140,7 +153,7 @@ impl LintPass for TypeLimits { } }, _ => { - let t = ty::expr_ty(cx.tcx, expr); + let t = ty::expr_ty(cx.type_context(), expr); match ty::get(t).sty { ty::ty_uint(_) => { cx.span_lint(unsigned_negate, e.span, @@ -160,16 +173,16 @@ impl LintPass for TypeLimits { self.negated_expr_id = expr.id; }, ast::ExprBinary(binop, l, r) => { - if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) { + if is_comparison(binop) && !check_limits(cx.type_context(), binop, l, r) { cx.span_lint(type_limits, e.span, "comparison is useless due to type limits"); } }, ast::ExprLit(lit) => { - match ty::get(ty::expr_ty(cx.tcx, e)).sty { + match ty::get(ty::expr_ty(cx.type_context(), e)).sty { ty::ty_int(t) => { let int_type = if t == ast::TyI { - cx.tcx.sess.targ_cfg.int_type + cx.session().targ_cfg.int_type } else { t }; let (min, max) = int_ty_range(int_type); let mut lit_val: i64 = match lit.node { @@ -188,7 +201,7 @@ impl LintPass for TypeLimits { }, ty::ty_uint(t) => { let uint_type = if t == ast::TyU { - cx.tcx.sess.targ_cfg.uint_type + cx.session().targ_cfg.uint_type } else { t }; let (min, max) = uint_ty_range(uint_type); let lit_val: u64 = match lit.node { @@ -319,7 +332,7 @@ impl LintPass for CTypes { fn check_ty(cx: &Context, ty: &ast::Ty) { match ty.node { ast::TyPath(_, _, id) => { - match cx.tcx.def_map.borrow().get_copy(&id) { + match cx.type_context().def_map.borrow().get_copy(&id) { def::DefPrimTy(ast::TyInt(ast::TyI)) => { cx.span_lint(ctypes, ty.span, "found rust type `int` in foreign module, while \ @@ -331,7 +344,7 @@ impl LintPass for CTypes { libc::c_uint or libc::c_ulong should be used"); } def::DefTy(def_id) => { - if !adt::is_ffi_safe(cx.tcx, def_id) { + if !adt::is_ffi_safe(cx.type_context(), def_id) { cx.span_lint(ctypes, ty.span, "found enum type without foreign-function-safe \ representation annotation in foreign module"); @@ -385,7 +398,7 @@ impl HeapMemory { let mut n_box = 0; let mut n_uniq = 0; - ty::fold_ty(cx.tcx, ty, |t| { + ty::fold_ty(cx.type_context(), ty, |t| { match ty::get(t).sty { ty::ty_box(_) => { n_box += 1; @@ -407,14 +420,14 @@ impl HeapMemory { }); if n_uniq > 0 { - let s = ty_to_str(cx.tcx, ty); + let s = ty_to_str(cx.type_context(), ty); 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_box > 0 { - let s = ty_to_str(cx.tcx, ty); + let s = ty_to_str(cx.type_context(), ty); let m = format!("type uses managed (@ type) pointers: {}", s); cx.span_lint(managed_heap_memory, span, m.as_slice()); cx.span_lint(heap_memory, span, m.as_slice()); @@ -432,9 +445,9 @@ impl LintPass for HeapMemory { ast::ItemFn(..) | ast::ItemTy(..) | ast::ItemEnum(..) | - ast::ItemStruct(..) => self.check_heap_type(cx, it.span, - ty::node_id_to_type(cx.tcx, - it.id)), + ast::ItemStruct(..) + => self.check_heap_type(cx, it.span, + ty::node_id_to_type(cx.type_context(), it.id)), _ => () } @@ -443,8 +456,7 @@ impl LintPass for HeapMemory { ast::ItemStruct(struct_def, _) => { for struct_field in struct_def.fields.iter() { self.check_heap_type(cx, struct_field.span, - ty::node_id_to_type(cx.tcx, - struct_field.node.id)); + ty::node_id_to_type(cx.type_context(), struct_field.node.id)); } } _ => () @@ -452,7 +464,7 @@ impl LintPass for HeapMemory { } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { - let ty = ty::expr_ty(cx.tcx, e); + let ty = ty::expr_ty(cx.type_context(), e); self.check_heap_type(cx, e.span, ty); } } @@ -501,7 +513,7 @@ impl LintPass for RawPointerDeriving { } let did = match item.node { ast::ItemImpl(..) => { - match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty { + match ty::get(ty::node_id_to_type(cx.type_context(), item.id)).sty { ty::ty_enum(did, _) => did, ty::ty_struct(did, _) => did, _ => return, @@ -510,7 +522,7 @@ impl LintPass for RawPointerDeriving { _ => return, }; if !ast_util::is_local(did) { return } - let item = match cx.tcx.map.find(did.node) { + let item = match cx.type_context().map.find(did.node) { Some(ast_map::NodeItem(item)) => item, _ => return, }; @@ -651,7 +663,7 @@ impl LintPass for UnusedResult { ast::StmtSemi(expr, _) => expr, _ => return }; - let t = ty::expr_ty(cx.tcx, expr); + let t = ty::expr_ty(cx.type_context(), expr); match ty::get(t).sty { ty::ty_nil | ty::ty_bot | ty::ty_bool => return, _ => {} @@ -661,13 +673,13 @@ impl LintPass for UnusedResult { _ => {} } - let t = ty::expr_ty(cx.tcx, expr); + let t = ty::expr_ty(cx.type_context(), expr); let mut warned = false; match ty::get(t).sty { ty::ty_struct(did, _) | ty::ty_enum(did, _) => { if ast_util::is_local(did) { - match cx.tcx.map.get(did.node) { + match cx.type_context().map.get(did.node) { ast_map::NodeItem(it) => { if attr::contains_name(it.attrs.as_slice(), "must_use") { @@ -679,7 +691,7 @@ impl LintPass for UnusedResult { _ => {} } } else { - csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| { + csearch::get_item_attrs(&cx.session().cstore, did, |attrs| { if attr::contains_name(attrs.as_slice(), "must_use") { cx.span_lint(unused_must_use, s.span, "unused result which must be used"); @@ -708,12 +720,12 @@ impl LintPass for DeprecatedOwnedVector { } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { - let t = ty::expr_ty(cx.tcx, e); + let t = ty::expr_ty(cx.type_context(), e); match ty::get(t).sty { ty::ty_uniq(t) => match ty::get(t).sty { ty::ty_vec(_, None) => { 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`") } _ => {} }, @@ -792,13 +804,13 @@ fn method_context(cx: &Context, m: &ast::Method) -> MethodContext { node: m.id }; - match cx.tcx.methods.borrow().find_copy(&did) { - None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"), + match cx.type_context().methods.borrow().find_copy(&did) { + None => cx.session().span_bug(m.span, "missing method descriptor?!"), Some(md) => { match md.container { ty::TraitContainer(..) => TraitDefaultImpl, ty::ImplContainer(cid) => { - match ty::impl_trait_ref(cx.tcx, cid) { + match ty::impl_trait_ref(cx.type_context(), cid) { Some(..) => TraitImpl, None => PlainImpl } @@ -931,7 +943,7 @@ impl LintPass for NonUppercasePatternStatics { fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { // Lint for constants that look like binding identifiers (#7526) - match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) { + match (&p.node, cx.type_context().def_map.borrow().find(&p.id)) { (&ast::PatIdent(_, ref path, _), Some(&def::DefStatic(_, false))) => { // last identifier alone is right choice for this lint. let ident = path.segments.last().unwrap().identifier; @@ -963,7 +975,7 @@ impl LintPass for UppercaseVariables { fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { match &p.node { &ast::PatIdent(_, ref path, _) => { - match cx.tcx.def_map.borrow().find(&p.id) { + match cx.type_context().def_map.borrow().find(&p.id) { Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) | Some(&def::DefArg(_, _)) => { // last identifier alone is right choice for this lint. @@ -1065,7 +1077,7 @@ impl LintPass for UnusedUnsafe { // Don't warn about generated blocks, that'll just pollute the output. ast::ExprBlock(ref blk) => { if blk.rules == ast::UnsafeBlock(ast::UserProvided) && - !cx.tcx.used_unsafe.borrow().contains(&blk.id) { + !cx.type_context().used_unsafe.borrow().contains(&blk.id) { cx.span_lint(unused_unsafe, blk.span, "unnecessary `unsafe` block"); } } @@ -1108,19 +1120,18 @@ impl UnusedMut { // avoid false warnings in match arms with multiple patterns let mut mutables = HashMap::new(); for &p in pats.iter() { - pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path| { + pat_util::pat_bindings(&cx.type_context().def_map, p, |mode, id, _, path| { match mode { ast::BindByValue(ast::MutMutable) => { if path.segments.len() != 1 { - cx.tcx.sess.span_bug(p.span, + cx.session().span_bug(p.span, "mutable binding that doesn't consist \ of exactly one segment"); } let ident = path.segments.get(0).identifier; if !token::get_ident(ident).get().starts_with("_") { - mutables.insert_or_update_with(ident.name as uint, vec!(id), |_, old| { - old.push(id); - }); + mutables.insert_or_update_with(ident.name as uint, + vec!(id), |_, old| { old.push(id); }); } } _ => { @@ -1129,10 +1140,10 @@ impl UnusedMut { }); } - let used_mutables = cx.tcx.used_mut_nodes.borrow(); + let used_mutables = cx.type_context().used_mut_nodes.borrow(); for (_, v) in mutables.iter() { if !v.iter().any(|e| used_mutables.contains(e)) { - cx.span_lint(unused_mut, cx.tcx.map.span(*v.get(0)), + cx.span_lint(unused_mut, cx.type_context().map.span(*v.get(0)), "variable does not need to be mutable"); } } @@ -1213,7 +1224,7 @@ impl LintPass for UnnecessaryAllocation { _ => return }; - match cx.tcx.adjustments.borrow().find(&e.id) { + match cx.type_context().adjustments.borrow().find(&e.id) { Some(adjustment) => { match *adjustment { ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { @@ -1281,7 +1292,7 @@ impl MissingDoc { desc: &'static str) { // If we're building a test harness, then warning about // documentation is probably not really relevant right now. - if cx.tcx.sess.opts.test { return } + if cx.session().opts.test { return } // `#[doc(hidden)]` disables missing_doc check. if self.doc_hidden() { return } @@ -1360,7 +1371,8 @@ impl LintPass for MissingDoc { } fn check_fn(&mut self, cx: &Context, - fk: &visit::FnKind, _: &ast::FnDecl, _: &ast::Block, _: Span, _: ast::NodeId) { + fk: &visit::FnKind, _: &ast::FnDecl, + _: &ast::Block, _: Span, _: ast::NodeId) { match *fk { visit::FkMethod(_, _, m) => { // If the method is an impl for a trait, don't doc. @@ -1383,7 +1395,8 @@ impl LintPass for MissingDoc { fn check_struct_field(&mut self, cx: &Context, sf: &ast::StructField) { match sf.node.kind { ast::NamedField(_, vis) if vis == ast::Public => { - let cur_struct_def = *self.struct_def_stack.last().expect("empty struct_def_stack"); + let cur_struct_def = *self.struct_def_stack.last() + .expect("empty struct_def_stack"); self.check_missing_doc_attrs(cx, Some(cur_struct_def), sf.node.attrs.as_slice(), sf.span, "a struct field") } @@ -1406,8 +1419,8 @@ declare_lint!(experimental, Warn, declare_lint!(unstable, Allow, "detects use of #[unstable] items (incl. items with no stability attribute)") -/// Checks for use of items with #[deprecated], #[experimental] and -/// #[unstable] (or none of them) attributes. +/// Checks for use of items with `#[deprecated]`, `#[experimental]` and +/// `#[unstable]` attributes, or no stability attribute. #[deriving(Default)] pub struct Stability; @@ -1419,14 +1432,14 @@ impl LintPass for Stability { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { let id = match e.node { ast::ExprPath(..) | ast::ExprStruct(..) => { - match cx.tcx.def_map.borrow().find(&e.id) { + match cx.type_context().def_map.borrow().find(&e.id) { Some(&def) => def.def_id(), None => return } } ast::ExprMethodCall(..) => { let method_call = typeck::MethodCall::expr(e.id); - match cx.tcx.method_map.borrow().find(&method_call) { + match cx.type_context().method_map.borrow().find(&method_call) { Some(method) => { match method.origin { typeck::MethodStatic(def_id) => { @@ -1435,7 +1448,7 @@ impl LintPass for Stability { // Otherwise, use the current def_id (which refers // to the method inside impl). ty::trait_method_of_method( - cx.tcx, def_id).unwrap_or(def_id) + cx.type_context(), def_id).unwrap_or(def_id) } typeck::MethodParam(typeck::MethodParam { trait_id: trait_id, @@ -1446,7 +1459,7 @@ impl LintPass for Stability { trait_id: trait_id, method_num: index, .. - }) => ty::trait_method(cx.tcx, trait_id, index).def_id + }) => ty::trait_method(cx.type_context(), trait_id, index).def_id } } None => return @@ -1457,7 +1470,7 @@ impl LintPass for Stability { let stability = if ast_util::is_local(id) { // this crate - let s = cx.tcx.map.with_attrs(id.node, |attrs| { + let s = cx.type_context().map.with_attrs(id.node, |attrs| { attrs.map(|a| attr::find_stability(a.as_slice())) }); match s { @@ -1474,7 +1487,7 @@ impl LintPass for Stability { let mut s = None; // run through all the attributes and take the first // stability one. - csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| { + csearch::get_item_attrs(&cx.session().cstore, id, |attrs| { if s.is_none() { s = attr::find_stability(attrs.as_slice()) } @@ -1505,32 +1518,6 @@ impl LintPass for Stability { } } -/// 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.lints.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") diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs new file mode 100644 index 0000000000000..49bdf7a8a5e52 --- /dev/null +++ b/src/librustc/lint/context.rs @@ -0,0 +1,633 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of lint checking. +//! +//! The lint checking is mostly consolidated into one pass which runs just +//! before translation to LLVM bytecode. Throughout compilation, lint warnings +//! can be added via the `add_lint` method on the Session structure. This +//! requires a span and an id of the node that the lint is being added to. The +//! lint isn't actually emitted at that time because it is unknown what the +//! actual lint level at that location is. +//! +//! To actually emit lint warnings/errors, a separate pass is used just before +//! translation. A context keeps track of the current state of all lint levels. +//! Upon entering a node of the ast which can modify the lint settings, the +//! previous lint state is pushed onto a stack and the ast is then recursed +//! upon. As the ast is traversed, this keeps track of the current lint level +//! for all lint attributes. + +use middle::privacy::ExportedItems; +use middle::ty; +use middle::typeck::astconv::AstConv; +use middle::typeck::infer; +use driver::session::Session; +use driver::early_error; +use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject}; +use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid}; +use lint::builtin; + +use std::collections::HashMap; +use std::rc::Rc; +use std::cell::RefCell; +use std::default::Default; +use std::tuple::Tuple2; +use std::mem; +use syntax::ast_util::IdVisitingOperation; +use syntax::attr::AttrMetaMethods; +use syntax::attr; +use syntax::codemap::Span; +use syntax::visit::{Visitor, FnKind}; +use syntax::{ast, ast_util, visit}; + +/// Information about the registered lints. +/// +/// This is basically the subset of `Context` that we can +/// build early in the compile pipeline. +pub struct LintStore { + /// Registered lints. The bool is true if the lint was + /// added by a plugin. + lints: Vec<(&'static Lint, bool)>, + + /// Trait objects for each lint pass. + passes: Vec>, + + /// Lints indexed by name. + by_name: HashMap<&'static str, LintId>, + + /// Current levels of each lint, and where they were set. + levels: HashMap, +} + +impl LintStore { + fn get_level_source(&self, lint: LintId) -> LevelSource { + match self.levels.find(&lint) { + Some(&s) => s, + None => (Allow, Default), + } + } + + fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) { + if lvlsrc.val0() == Allow { + self.levels.remove(&lint); + } else { + self.levels.insert(lint, lvlsrc); + } + } + + pub fn new() -> LintStore { + LintStore { + lints: vec!(), + passes: vec!(), + by_name: HashMap::new(), + levels: HashMap::new(), + } + } + + pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] { + self.lints.as_slice() + } + + pub fn register_pass(&mut self, sess: Option<&Session>, + from_plugin: bool, pass: LintPassObject) { + for &lint in pass.get_lints().iter() { + self.lints.push((lint, from_plugin)); + + let id = LintId::of(lint); + if !self.by_name.insert(lint.name, id) { + let msg = format!("duplicate specification of lint {}", lint.name); + match (sess, from_plugin) { + // We load builtin lints first, so a duplicate is a compiler bug. + // Use early_error when handling -W help with no crate. + (None, _) => early_error(msg.as_slice()), + (Some(sess), false) => sess.bug(msg.as_slice()), + + // A duplicate name from a plugin is a user error. + (Some(sess), true) => sess.err(msg.as_slice()), + } + } + + if lint.default_level != Allow { + self.levels.insert(id, (lint.default_level, Default)); + } + } + self.passes.push(RefCell::new(pass)); + } + + pub fn register_builtin(&mut self, sess: Option<&Session>) { + macro_rules! add_builtin_lints ( ( $sess:ident, $($name:ident),*, ) => ( + {$( + { + let obj: builtin::$name = Default::default(); + self.register_pass($sess, false, box obj as LintPassObject); + }; + )*} + )) + + add_builtin_lints!(sess, HardwiredLints, + WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory, + RawPointerDeriving, UnusedAttribute, PathStatement, + UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes, + NonSnakeCaseFunctions, NonUppercaseStatics, + NonUppercasePatternStatics, UppercaseVariables, + UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut, + UnnecessaryAllocation, MissingDoc, Stability, + ) + + // We have one lint pass defined in this module. + self.register_pass(sess, false, box GatherNodeLevels as LintPassObject); + } + + pub fn process_command_line(&mut self, sess: &Session) { + for &(ref lint_name, level) in sess.opts.lint_opts.iter() { + match self.by_name.find_equiv(&lint_name.as_slice()) { + Some(&lint_id) => self.set_level(lint_id, (level, CommandLine)), + None => sess.err(format!("unknown {} flag: {}", + level.as_str(), lint_name).as_slice()), + } + } + } +} + +/// Context for lint checking. +pub struct Context<'a> { + /// The store of registered lints. + lints: LintStore, + + /// Context we're checking in (used to access fields like sess). + tcx: &'a 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. + level_stack: Vec<(LintId, LevelSource)>, + + /// Level of lints for certain NodeIds, stored here because the body of + /// the lint needs to run in trans. + node_levels: RefCell>, +} + +/// Convenience macro for calling a `LintPass` method on every pass in the context. +macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ( + for obj in $cx.lints.passes.iter() { + obj.borrow_mut().$f($cx, $($args),*); + } +)) + +/// Emit a lint as a warning or an error (or not at all) +/// according to `level`. +/// +/// This lives outside of `Context` so it can be used by checks +/// in trans that run after the main lint pass is finished. Most +/// lints elsewhere in the compiler should call +/// `Session::add_lint()` instead. +pub fn raw_emit_lint(sess: &Session, lint: &'static Lint, + lvlsrc: LevelSource, span: Option, msg: &str) { + let (mut level, source) = lvlsrc; + if level == Allow { return } + + let mut note = None; + let msg = match source { + Default => { + format!("{}, \\#[{}({})] on by default", msg, + level.as_str(), lint.name) + }, + CommandLine => { + format!("{} [-{} {}]", msg, + match level { + Warn => 'W', Deny => 'D', Forbid => 'F', + Allow => fail!() + }, lint.name.replace("_", "-")) + }, + Node(src) => { + note = Some(src); + msg.to_string() + } + }; + + // For purposes of printing, we can treat forbid as deny. + if level == Forbid { level = Deny; } + + match (level, span) { + (Warn, Some(sp)) => sess.span_warn(sp, msg.as_slice()), + (Warn, None) => sess.warn(msg.as_slice()), + (Deny, Some(sp)) => sess.span_err(sp, msg.as_slice()), + (Deny, None) => sess.err(msg.as_slice()), + _ => sess.bug("impossible level in raw_emit_lint"), + } + + for span in note.move_iter() { + sess.span_note(span, "lint level defined here"); + } +} + +impl<'a> Context<'a> { + fn new(tcx: &'a ty::ctxt) -> Context<'a> { + // We want to own the lint store, so move it out of the session. + let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), + LintStore::new()); + + Context { + lints: lint_store, + tcx: tcx, + level_stack: vec!(), + node_levels: RefCell::new(HashMap::new()), + } + } + + /// Get the overall compiler `Session` object. + pub fn session(&'a self) -> &'a Session { + &self.tcx.sess + } + + /// Get the type context, containing information generated by the typechecker. + pub fn type_context(&'a self) -> &'a ty::ctxt { + self.tcx + } + + fn lookup_and_emit(&self, lint: &'static Lint, span: Option, msg: &str) { + let (level, src) = match self.lints.levels.find(&LintId::of(lint)) { + None => return, + Some(&(Warn, src)) => { + let lint_id = LintId::of(builtin::warnings); + (self.lints.get_level_source(lint_id).val0(), src) + } + Some(&pair) => pair, + }; + + raw_emit_lint(&self.tcx.sess, lint, (level, src), span, msg); + } + + /// Emit a lint at the appropriate level, with no associated span. + pub fn lint(&self, lint: &'static Lint, msg: &str) { + self.lookup_and_emit(lint, None, msg); + } + + /// Emit a lint at the appropriate level, for a particular span. + pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) { + self.lookup_and_emit(lint, Some(span), msg); + } + + /** + * Merge the lints specified by any lint attributes into the + * current lint context, call the provided function, then reset the + * lints in effect to their previous state. + */ + fn with_lint_attrs(&mut self, + attrs: &[ast::Attribute], + f: |&mut Context|) { + // 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 lint_attrs = self.gather_lint_attrs(attrs); + let mut pushed = 0u; + for (lint_id, level, span) in lint_attrs.move_iter() { + let now = self.lints.get_level_source(lint_id).val0(); + if now == Forbid && level != Forbid { + let lint_name = lint_id.as_str(); + self.tcx.sess.span_err(span, + format!("{}({}) overruled by outer forbid({})", + level.as_str(), lint_name, lint_name).as_slice()); + } else if now != level { + let src = self.lints.get_level_source(lint_id).val1(); + self.level_stack.push((lint_id, (now, src))); + pushed += 1; + self.lints.set_level(lint_id, (level, Node(span))); + } + } + + run_lints!(self, enter_lint_attrs, attrs); + f(self); + run_lints!(self, exit_lint_attrs, attrs); + + // rollback + for _ in range(0, pushed) { + let (lint, lvlsrc) = self.level_stack.pop().unwrap(); + self.lints.set_level(lint, lvlsrc); + } + } + + fn visit_ids(&self, f: |&mut ast_util::IdVisitor|) { + let mut v = ast_util::IdVisitor { + operation: self, + pass_through_items: false, + visited_outermost: false, + }; + f(&mut v); + } + + fn gather_lint_attrs(&mut self, attrs: &[ast::Attribute]) -> Vec<(LintId, Level, Span)> { + // Doing this as an iterator is messy due to multiple borrowing. + // Allocating and copying these should be quick. + let mut out = vec!(); + for attr in attrs.iter() { + let level = match Level::from_str(attr.name().get()) { + None => continue, + Some(lvl) => lvl, + }; + + attr::mark_used(attr); + + let meta = attr.node.value; + let metas = match meta.node { + ast::MetaList(_, ref metas) => metas, + _ => { + self.tcx.sess.span_err(meta.span, "malformed lint attribute"); + continue; + } + }; + + for meta in metas.iter() { + match meta.node { + ast::MetaWord(ref lint_name) => { + match self.lints.by_name.find_equiv(lint_name) { + 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"), + } + } + } + out + } +} + +impl<'a> AstConv for Context<'a>{ + fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx } + + fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty { + ty::lookup_item_type(self.tcx, id) + } + + fn get_trait_def(&self, id: ast::DefId) -> Rc { + ty::lookup_trait_def(self.tcx, id) + } + + fn ty_infer(&self, _span: Span) -> ty::t { + infer::new_infer_ctxt(self.tcx).next_ty_var() + } +} + +impl<'a> Visitor<()> for Context<'a> { + fn visit_item(&mut self, it: &ast::Item, _: ()) { + self.with_lint_attrs(it.attrs.as_slice(), |cx| { + run_lints!(cx, check_item, it); + cx.visit_ids(|v| v.visit_item(it, ())); + visit::walk_item(cx, it, ()); + }) + } + + fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) { + self.with_lint_attrs(it.attrs.as_slice(), |cx| { + run_lints!(cx, check_foreign_item, it); + visit::walk_foreign_item(cx, it, ()); + }) + } + + fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) { + self.with_lint_attrs(i.attrs.as_slice(), |cx| { + run_lints!(cx, check_view_item, i); + cx.visit_ids(|v| v.visit_view_item(i, ())); + visit::walk_view_item(cx, i, ()); + }) + } + + fn visit_pat(&mut self, p: &ast::Pat, _: ()) { + run_lints!(self, check_pat, p); + visit::walk_pat(self, p, ()); + } + + fn visit_expr(&mut self, e: &ast::Expr, _: ()) { + run_lints!(self, check_expr, e); + visit::walk_expr(self, e, ()); + } + + fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) { + run_lints!(self, check_stmt, s); + visit::walk_stmt(self, s, ()); + } + + fn visit_fn(&mut self, fk: &FnKind, decl: &ast::FnDecl, + body: &ast::Block, span: Span, id: ast::NodeId, _: ()) { + match *fk { + visit::FkMethod(_, _, m) => { + self.with_lint_attrs(m.attrs.as_slice(), |cx| { + run_lints!(cx, check_fn, fk, decl, body, span, id); + cx.visit_ids(|v| { + v.visit_fn(fk, decl, body, span, id, ()); + }); + visit::walk_fn(cx, fk, decl, body, span, ()); + }) + }, + _ => { + run_lints!(self, check_fn, fk, decl, body, span, id); + visit::walk_fn(self, fk, decl, body, span, ()); + } + } + } + + fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) { + self.with_lint_attrs(t.attrs.as_slice(), |cx| { + run_lints!(cx, check_ty_method, t); + visit::walk_ty_method(cx, t, ()); + }) + } + + fn visit_struct_def(&mut self, + s: &ast::StructDef, + ident: ast::Ident, + g: &ast::Generics, + id: ast::NodeId, + _: ()) { + run_lints!(self, check_struct_def, s, ident, g, id); + visit::walk_struct_def(self, s, ()); + run_lints!(self, check_struct_def_post, s, ident, g, id); + } + + fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) { + self.with_lint_attrs(s.node.attrs.as_slice(), |cx| { + run_lints!(cx, check_struct_field, s); + visit::walk_struct_field(cx, s, ()); + }) + } + + fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) { + self.with_lint_attrs(v.node.attrs.as_slice(), |cx| { + run_lints!(cx, check_variant, v, g); + visit::walk_variant(cx, v, g, ()); + }) + } + + // FIXME(#10894) should continue recursing + fn visit_ty(&mut self, t: &ast::Ty, _: ()) { + run_lints!(self, check_ty, t); + } + + fn visit_ident(&mut self, sp: Span, id: ast::Ident, _: ()) { + run_lints!(self, check_ident, sp, id); + } + + fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId, _: ()) { + run_lints!(self, check_mod, m, s, n); + visit::walk_mod(self, m, ()); + } + + fn visit_local(&mut self, l: &ast::Local, _: ()) { + run_lints!(self, check_local, l); + visit::walk_local(self, l, ()); + } + + fn visit_block(&mut self, b: &ast::Block, _: ()) { + run_lints!(self, check_block, b); + visit::walk_block(self, b, ()); + } + + fn visit_arm(&mut self, a: &ast::Arm, _: ()) { + run_lints!(self, check_arm, a); + visit::walk_arm(self, a, ()); + } + + fn visit_decl(&mut self, d: &ast::Decl, _: ()) { + run_lints!(self, check_decl, d); + visit::walk_decl(self, d, ()); + } + + fn visit_expr_post(&mut self, e: &ast::Expr, _: ()) { + run_lints!(self, check_expr_post, e); + } + + fn visit_generics(&mut self, g: &ast::Generics, _: ()) { + run_lints!(self, check_generics, g); + visit::walk_generics(self, g, ()); + } + + fn visit_trait_method(&mut self, m: &ast::TraitMethod, _: ()) { + run_lints!(self, check_trait_method, m); + visit::walk_trait_method(self, m, ()); + } + + fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option, _: ()) { + run_lints!(self, check_opt_lifetime_ref, sp, lt); + } + + fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime, _: ()) { + run_lints!(self, check_lifetime_ref, lt); + } + + fn visit_lifetime_decl(&mut self, lt: &ast::Lifetime, _: ()) { + run_lints!(self, check_lifetime_decl, lt); + } + + fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf, _: ()) { + run_lints!(self, check_explicit_self, es); + visit::walk_explicit_self(self, es, ()); + } + + fn visit_mac(&mut self, mac: &ast::Mac, _: ()) { + run_lints!(self, check_mac, mac); + visit::walk_mac(self, mac, ()); + } + + fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId, _: ()) { + run_lints!(self, check_path, p, id); + visit::walk_path(self, p, ()); + } + + fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) { + run_lints!(self, check_attribute, attr); + } +} + +// Output any lints that were previously added to the session. +impl<'a> IdVisitingOperation for Context<'a> { + fn visit_id(&self, id: ast::NodeId) { + match self.tcx.sess.lints.borrow_mut().pop(&id) { + None => {} + Some(lints) => { + for (lint_id, span, msg) in lints.move_iter() { + self.span_lint(lint_id.lint, span, msg.as_slice()) + } + } + } + } +} + +// This lint pass is defined here because it touches parts of the `Context` +// that we don't want to expose. It records the lint level at certain AST +// nodes, so that the variant size difference check in trans can call +// `raw_emit_lint`. + +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 = LintId::of(builtin::variant_size_difference); + match cx.lints.get_level_source(lint_id) { + lvlsrc @ (lvl, _) if lvl != Allow => { + cx.node_levels.borrow_mut() + .insert((it.id, lint_id), lvlsrc); + }, + _ => { } + } + }, + _ => { } + } + } +} + +/// Perform lint checking on a crate. +/// +/// Consumes the `lint_store` field of the `Session`. +pub fn check_crate(tcx: &ty::ctxt, + exported_items: &ExportedItems, + krate: &ast::Crate) { + let mut cx = Context::new(tcx); + + // Visit the whole crate. + cx.with_lint_attrs(krate.attrs.as_slice(), |cx| { + cx.visit_id(ast::CRATE_NODE_ID); + cx.visit_ids(|v| { + v.visited_outermost = true; + visit::walk_crate(v, krate, ()); + }); + + // since the root module isn't visited as an item (because it isn't an + // item), warn for it here. + run_lints!(cx, check_crate, exported_items, krate); + + visit::walk_crate(cx, krate, ()); + }); + + // If we missed any lints added to the session, then there's a bug somewhere + // in the iteration code. + for (id, v) in tcx.sess.lints.borrow().iter() { + for &(lint, span, ref msg) in v.iter() { + tcx.sess.span_bug(span, + format!("unprocessed lint {} at {}: {}", + lint.as_str(), tcx.map.node_to_str(*id), *msg) + .as_slice()) + } + } + + tcx.sess.abort_if_errors(); + *tcx.node_lint_levels.borrow_mut() = cx.node_levels.unwrap(); +} diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 2078e793ee10e..bd9739652f990 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -8,63 +8,56 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Lints, aka compiler warnings. +//! //! A 'lint' check is a kind of miscellaneous constraint that a user _might_ //! want to enforce, but might reasonably want to permit as well, on a //! module-by-module basis. They contrast with static constraints enforced by //! other phases of the compiler, which are generally required to hold in order //! to compile the program at all. //! -//! The lint checking is all consolidated into one pass which runs just before -//! translation to LLVM bytecode. Throughout compilation, lint warnings can be -//! added via the `add_lint` method on the Session structure. This requires a -//! span and an id of the node that the lint is being added to. The lint isn't -//! actually emitted at that time because it is unknown what the actual lint -//! level at that location is. -//! -//! To actually emit lint warnings/errors, a separate pass is used just before -//! translation. A context keeps track of the current state of all lint levels. -//! Upon entering a node of the ast which can modify the lint settings, the -//! previous lint state is pushed onto a stack and the ast is then recursed -//! upon. As the ast is traversed, this keeps track of the current lint level -//! for all lint attributes. -//! -//! Most of the lints built into `rustc` are structs implementing `LintPass`, -//! and are defined within `builtin.rs`. To add a new lint you can define such -//! a struct and add it to the `builtin_lints!` macro invocation in this file. -//! `LintPass` itself is not a subtrait of `Default`, but the `builtin_lints!` -//! macro requires `Default` (usually via `deriving`). +//! Most lints can be written as `LintPass` instances. These run just before +//! translation to LLVM bytecode. The `LintPass`es built into rustc are defined +//! within `builtin.rs`, which has further comments on how to add such a lint. //! -//! Some lints are defined elsewhere in the compiler and work by calling -//! `add_lint()` on the overall `Session` object. -//! -//! If you're adding lints to the `Context` infrastructure itself, defined in -//! this file, use `span_lint` instead of `add_lint`. +//! Some of rustc's lints are defined elsewhere in the compiler and work by +//! calling `add_lint()` on the overall `Session` object. This works when +//! it happens before the main lint pass, which emits the lints stored by +//! `add_lint()`. To emit lints after the main lint pass (from trans, for +//! example) requires more effort. See `emit_lint` and `GatherNodeLevels` +//! in `context.rs`. -#![allow(non_camel_case_types)] #![macro_escape] use middle::privacy::ExportedItems; -use middle::ty; -use middle::typeck::astconv::AstConv; -use middle::typeck::infer; -use driver::session::Session; -use driver::early_error; - -use std::collections::HashMap; -use std::rc::Rc; -use std::cell::RefCell; -use std::default::Default; -use std::hash::Hash; -use std::tuple::Tuple2; + use std::hash; -use std::mem; -use syntax::ast_util::IdVisitingOperation; -use syntax::attr::AttrMetaMethods; -use syntax::attr; use syntax::codemap::Span; -use syntax::visit::{Visitor, FnKind}; -use syntax::{ast, ast_util, visit}; +use syntax::visit::FnKind; +use syntax::ast; + +pub use lint::context::{Context, LintStore, raw_emit_lint, check_crate}; + +/// Specification of a single lint. +pub struct Lint { + /// A string 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, +} + +/// Build a `Lint` initializer. #[macro_export] macro_rules! lint_initializer ( ($name:ident, $level:ident, $desc:expr) => ( @@ -76,6 +69,7 @@ macro_rules! lint_initializer ( ) ) +/// Declare a static item of type `&'static Lint`. #[macro_export] macro_rules! declare_lint ( // FIXME(#14660): deduplicate @@ -89,6 +83,7 @@ macro_rules! declare_lint ( ); ) +/// Declare a static `LintArray` and return it as an expression. #[macro_export] macro_rules! lint_array ( ($( $lint:expr ),*) => ( { @@ -97,29 +92,13 @@ macro_rules! lint_array ( ($( $lint:expr ),*) => ( } )) -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, -} - pub 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. +/// 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`. But this also // contains a few lint-specific methods with no equivalent in `Visitor`. @@ -174,7 +153,8 @@ pub trait LintPass { fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) { } } -type LintPassObject = Box; +/// A lint pass boxed up as a trait object. +pub type LintPassObject = Box; /// Identifies a lint known to the compiler. #[deriving(Clone)] @@ -195,7 +175,7 @@ impl PartialEq for LintId { impl Eq for LintId { } -impl Hash for LintId { +impl hash::Hash for LintId { fn hash(&self, state: &mut S) { let ptr = self.lint as *Lint; ptr.hash(state); @@ -203,23 +183,27 @@ impl Hash for LintId { } impl LintId { + /// Get the `LintId` for a `Lint`. pub fn of(lint: &'static Lint) -> LintId { LintId { lint: lint, } } + /// Get the name of the lint. pub fn as_str(&self) -> &'static str { self.lint.name } } +/// Setting for how to handle a lint. #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)] pub enum Level { Allow, Warn, Deny, Forbid } impl Level { + /// Convert a level to a lower-case string. pub fn as_str(self) -> &'static str { match self { Allow => "allow", @@ -229,6 +213,7 @@ impl Level { } } + /// Convert a lower-case string to a level. pub fn from_str(x: &str) -> Option { match x { "allow" => Some(Allow), @@ -240,534 +225,21 @@ impl Level { } } -// this is public for the lints that run in trans -#[deriving(PartialEq)] +/// How a lint level was set. +#[deriving(Clone, PartialEq, Eq)] pub enum LintSource { - Node(Span), + /// Lint is at the default level as declared + /// in rustc or a plugin. Default, - CommandLine -} - -pub type LevelSource = (Level, LintSource); - -/// Information about the registered lints. -/// This is basically the subset of `Context` that we can -/// build early in the compile pipeline. -pub struct LintStore { - /// Registered lints. The bool is true if the lint was - /// added by a plugin. - lints: Vec<(&'static Lint, bool)>, - - /// Trait objects for each lint pass. - passes: Vec>, - - /// Lints indexed by name. - by_name: HashMap<&'static str, LintId>, - - /// Current levels of each lint, and where they were set. - levels: HashMap, -} - -impl LintStore { - fn get_level_source(&self, lint: LintId) -> LevelSource { - match self.levels.find(&lint) { - Some(&s) => s, - None => (Allow, Default), - } - } - - fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) { - if lvlsrc.val0() == Allow { - self.levels.remove(&lint); - } else { - self.levels.insert(lint, lvlsrc); - } - } - - pub fn new() -> LintStore { - LintStore { - lints: vec!(), - passes: vec!(), - by_name: HashMap::new(), - levels: HashMap::new(), - } - } - - pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] { - self.lints.as_slice() - } - - pub fn register_pass(&mut self, sess: Option<&Session>, - from_plugin: bool, pass: LintPassObject) { - for &lint in pass.get_lints().iter() { - self.lints.push((lint, from_plugin)); - - let id = LintId::of(lint); - if !self.by_name.insert(lint.name, id) { - let msg = format!("duplicate specification of lint {}", lint.name); - match (sess, from_plugin) { - // We load builtin lints first, so a duplicate is a compiler bug. - // Use early_error when handling -W help with no crate. - (None, _) => early_error(msg.as_slice()), - (Some(sess), false) => sess.bug(msg.as_slice()), - - // A duplicate name from a plugin is a user error. - (Some(sess), true) => sess.err(msg.as_slice()), - } - } - - if lint.default_level != Allow { - self.levels.insert(id, (lint.default_level, Default)); - } - } - self.passes.push(RefCell::new(pass)); - } - - pub fn register_builtin(&mut self, sess: Option<&Session>) { - macro_rules! add_builtin_lints ( ( $sess:ident, $($name:ident),*, ) => ( - {$( - { - let obj: builtin::$name = Default::default(); - self.register_pass($sess, false, box obj as LintPassObject); - }; - )*} - )) - - add_builtin_lints!(sess, - WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory, - RawPointerDeriving, UnusedAttribute, PathStatement, - UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes, - NonSnakeCaseFunctions, NonUppercaseStatics, - NonUppercasePatternStatics, UppercaseVariables, - UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut, - UnnecessaryAllocation, MissingDoc, Stability, - - GatherNodeLevels, HardwiredLints, - ) - } - - pub fn process_command_line(&mut self, sess: &Session) { - for &(ref lint_name, level) in sess.opts.lint_opts.iter() { - match self.by_name.find_equiv(&lint_name.as_slice()) { - Some(&lint_id) => self.set_level(lint_id, (level, CommandLine)), - None => sess.err(format!("unknown {} flag: {}", - level.as_str(), lint_name).as_slice()), - } - } - } -} - -/// Context for lint checking. -pub struct Context<'a> { - /// The store of registered lints. - lints: LintStore, - - /// Context we're checking in (used to access fields like sess). - tcx: &'a 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. - level_stack: Vec<(LintId, LevelSource)>, - - /// Level of lints for certain NodeIds, stored here because the body of - /// the lint needs to run in trans. - node_levels: RefCell>, -} - -/// Convenience macro for calling a `LintPass` method on every pass in the context. -macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ( - for obj in $cx.lints.passes.iter() { - obj.borrow_mut().$f($cx, $($args),*); - } -)) - -/// Emit a lint as a `span_warn` or `span_err` (or not at all) -/// 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 } - - let mut note = None; - let msg = match source { - Default => { - format!("{}, \\#[{}({})] on by default", msg, - level.as_str(), lint.name) - }, - CommandLine => { - format!("{} [-{} {}]", msg, - match level { - Warn => 'W', Deny => 'D', Forbid => 'F', - Allow => fail!() - }, lint.name.replace("_", "-")) - }, - Node(src) => { - note = Some(src); - msg.to_string() - } - }; - - match level { - Warn => { sess.span_warn(span, msg.as_slice()); } - Deny | Forbid => { sess.span_err(span, msg.as_slice()); } - Allow => fail!(), - } - - for span in note.move_iter() { - sess.span_note(span, "lint level defined here"); - } -} - -impl<'a> Context<'a> { - pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) { - let (level, src) = match self.lints.levels.find(&LintId::of(lint)) { - None => return, - Some(&(Warn, src)) - => (self.lints.get_level_source(LintId::of(builtin::warnings)).val0(), src), - Some(&pair) => pair, - }; - - emit_lint(&self.tcx.sess, lint, (level, src), span, msg); - } - - /** - * Merge the lints specified by any lint attributes into the - * current lint context, call the provided function, then reset the - * lints in effect to their previous state. - */ - fn with_lint_attrs(&mut self, - attrs: &[ast::Attribute], - f: |&mut Context|) { - // 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 lint_attrs = self.gather_lint_attrs(attrs); - let mut pushed = 0u; - for (lint_id, level, span) in lint_attrs.move_iter() { - let now = self.lints.get_level_source(lint_id).val0(); - if now == Forbid && level != Forbid { - let lint_name = lint_id.as_str(); - self.tcx.sess.span_err(span, - format!("{}({}) overruled by outer forbid({})", - level.as_str(), lint_name, lint_name).as_slice()); - } else if now != level { - let src = self.lints.get_level_source(lint_id).val1(); - self.level_stack.push((lint_id, (now, src))); - pushed += 1; - self.lints.set_level(lint_id, (level, Node(span))); - } - } - - run_lints!(self, enter_lint_attrs, attrs); - f(self); - run_lints!(self, exit_lint_attrs, attrs); - - // rollback - for _ in range(0, pushed) { - let (lint, lvlsrc) = self.level_stack.pop().unwrap(); - self.lints.set_level(lint, lvlsrc); - } - } - - fn visit_ids(&self, f: |&mut ast_util::IdVisitor|) { - let mut v = ast_util::IdVisitor { - operation: self, - pass_through_items: false, - visited_outermost: false, - }; - f(&mut v); - } - - fn insert_node_level(&self, id: ast::NodeId, lint: LintId, lvlsrc: LevelSource) { - self.node_levels.borrow_mut().insert((id, lint), lvlsrc); - } - - fn gather_lint_attrs(&mut self, attrs: &[ast::Attribute]) -> Vec<(LintId, Level, Span)> { - // Doing this as an iterator is messy due to multiple borrowing. - // Allocating and copying these should be quick. - let mut out = vec!(); - for attr in attrs.iter() { - let level = match Level::from_str(attr.name().get()) { - None => continue, - Some(lvl) => lvl, - }; - - attr::mark_used(attr); - - let meta = attr.node.value; - let metas = match meta.node { - ast::MetaList(_, ref metas) => metas, - _ => { - self.tcx.sess.span_err(meta.span, "malformed lint attribute"); - continue; - } - }; - - for meta in metas.iter() { - match meta.node { - ast::MetaWord(ref lint_name) => { - match self.lints.by_name.find_equiv(lint_name) { - 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"), - } - } - } - out - } -} - -impl<'a> AstConv for Context<'a>{ - fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx } - - fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty { - ty::lookup_item_type(self.tcx, id) - } - - fn get_trait_def(&self, id: ast::DefId) -> Rc { - ty::lookup_trait_def(self.tcx, id) - } - - fn ty_infer(&self, _span: Span) -> ty::t { - infer::new_infer_ctxt(self.tcx).next_ty_var() - } -} - -impl<'a> Visitor<()> for Context<'a> { - fn visit_item(&mut self, it: &ast::Item, _: ()) { - self.with_lint_attrs(it.attrs.as_slice(), |cx| { - run_lints!(cx, check_item, it); - cx.visit_ids(|v| v.visit_item(it, ())); - visit::walk_item(cx, it, ()); - }) - } - - fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) { - self.with_lint_attrs(it.attrs.as_slice(), |cx| { - run_lints!(cx, check_foreign_item, it); - visit::walk_foreign_item(cx, it, ()); - }) - } - - fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) { - self.with_lint_attrs(i.attrs.as_slice(), |cx| { - run_lints!(cx, check_view_item, i); - cx.visit_ids(|v| v.visit_view_item(i, ())); - visit::walk_view_item(cx, i, ()); - }) - } - fn visit_pat(&mut self, p: &ast::Pat, _: ()) { - run_lints!(self, check_pat, p); - visit::walk_pat(self, p, ()); - } - - fn visit_expr(&mut self, e: &ast::Expr, _: ()) { - run_lints!(self, check_expr, e); - visit::walk_expr(self, e, ()); - } - - fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) { - run_lints!(self, check_stmt, s); - visit::walk_stmt(self, s, ()); - } - - fn visit_fn(&mut self, fk: &FnKind, decl: &ast::FnDecl, - body: &ast::Block, span: Span, id: ast::NodeId, _: ()) { - match *fk { - visit::FkMethod(_, _, m) => { - self.with_lint_attrs(m.attrs.as_slice(), |cx| { - run_lints!(cx, check_fn, fk, decl, body, span, id); - cx.visit_ids(|v| { - v.visit_fn(fk, decl, body, span, id, ()); - }); - visit::walk_fn(cx, fk, decl, body, span, ()); - }) - }, - _ => { - run_lints!(self, check_fn, fk, decl, body, span, id); - visit::walk_fn(self, fk, decl, body, span, ()); - } - } - } - - fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) { - self.with_lint_attrs(t.attrs.as_slice(), |cx| { - run_lints!(cx, check_ty_method, t); - visit::walk_ty_method(cx, t, ()); - }) - } - - fn visit_struct_def(&mut self, - s: &ast::StructDef, - ident: ast::Ident, - g: &ast::Generics, - id: ast::NodeId, - _: ()) { - run_lints!(self, check_struct_def, s, ident, g, id); - visit::walk_struct_def(self, s, ()); - run_lints!(self, check_struct_def_post, s, ident, g, id); - } - - fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) { - self.with_lint_attrs(s.node.attrs.as_slice(), |cx| { - run_lints!(cx, check_struct_field, s); - visit::walk_struct_field(cx, s, ()); - }) - } - - fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) { - self.with_lint_attrs(v.node.attrs.as_slice(), |cx| { - run_lints!(cx, check_variant, v, g); - visit::walk_variant(cx, v, g, ()); - }) - } - - // FIXME(#10894) should continue recursing - fn visit_ty(&mut self, t: &ast::Ty, _: ()) { - run_lints!(self, check_ty, t); - } - - fn visit_ident(&mut self, sp: Span, id: ast::Ident, _: ()) { - run_lints!(self, check_ident, sp, id); - } - - fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId, _: ()) { - run_lints!(self, check_mod, m, s, n); - visit::walk_mod(self, m, ()); - } - - fn visit_local(&mut self, l: &ast::Local, _: ()) { - run_lints!(self, check_local, l); - visit::walk_local(self, l, ()); - } - - fn visit_block(&mut self, b: &ast::Block, _: ()) { - run_lints!(self, check_block, b); - visit::walk_block(self, b, ()); - } - - fn visit_arm(&mut self, a: &ast::Arm, _: ()) { - run_lints!(self, check_arm, a); - visit::walk_arm(self, a, ()); - } - - fn visit_decl(&mut self, d: &ast::Decl, _: ()) { - run_lints!(self, check_decl, d); - visit::walk_decl(self, d, ()); - } - - fn visit_expr_post(&mut self, e: &ast::Expr, _: ()) { - run_lints!(self, check_expr_post, e); - } - - fn visit_generics(&mut self, g: &ast::Generics, _: ()) { - run_lints!(self, check_generics, g); - visit::walk_generics(self, g, ()); - } - - fn visit_trait_method(&mut self, m: &ast::TraitMethod, _: ()) { - run_lints!(self, check_trait_method, m); - visit::walk_trait_method(self, m, ()); - } - - fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option, _: ()) { - run_lints!(self, check_opt_lifetime_ref, sp, lt); - } - - fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime, _: ()) { - run_lints!(self, check_lifetime_ref, lt); - } - - fn visit_lifetime_decl(&mut self, lt: &ast::Lifetime, _: ()) { - run_lints!(self, check_lifetime_decl, lt); - } - - fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf, _: ()) { - run_lints!(self, check_explicit_self, es); - visit::walk_explicit_self(self, es, ()); - } - - fn visit_mac(&mut self, mac: &ast::Mac, _: ()) { - run_lints!(self, check_mac, mac); - visit::walk_mac(self, mac, ()); - } - - fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId, _: ()) { - run_lints!(self, check_path, p, id); - visit::walk_path(self, p, ()); - } - - fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) { - run_lints!(self, check_attribute, attr); - } -} + /// Lint level was set by an attribute. + Node(Span), -// Output any lints that were previously added to the session. -impl<'a> IdVisitingOperation for Context<'a> { - fn visit_id(&self, id: ast::NodeId) { - match self.tcx.sess.lints.borrow_mut().pop(&id) { - None => {} - Some(lints) => { - for (lint_id, span, msg) in lints.move_iter() { - self.span_lint(lint_id.lint, span, msg.as_slice()) - } - } - } - } + /// Lint level was set by a command-line flag. + CommandLine, } -pub fn check_crate(tcx: &ty::ctxt, - exported_items: &ExportedItems, - krate: &ast::Crate) { - - // We want to own the lint store, so move it out of the session. - let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), - LintStore::new()); - - let mut cx = Context { - lints: lint_store, - tcx: tcx, - level_stack: Vec::new(), - node_levels: RefCell::new(HashMap::new()), - }; - - // Visit the whole crate. - cx.with_lint_attrs(krate.attrs.as_slice(), |cx| { - cx.visit_id(ast::CRATE_NODE_ID); - cx.visit_ids(|v| { - v.visited_outermost = true; - visit::walk_crate(v, krate, ()); - }); - - // since the root module isn't visited as an item (because it isn't an - // item), warn for it here. - run_lints!(cx, check_crate, exported_items, krate); - - visit::walk_crate(cx, krate, ()); - }); - - // If we missed any lints added to the session, then there's a bug somewhere - // in the iteration code. - for (id, v) in tcx.sess.lints.borrow().iter() { - for &(lint, span, ref msg) in v.iter() { - tcx.sess.span_bug(span, - format!("unprocessed lint {} at {}: {}", - lint.as_str(), tcx.map.node_to_str(*id), *msg) - .as_slice()) - } - } +pub type LevelSource = (Level, LintSource); - tcx.sess.abort_if_errors(); - *tcx.node_lint_levels.borrow_mut() = cx.node_levels.unwrap(); -} +pub mod context; +pub mod builtin; diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 973f72feeaeb2..5314b69c40e47 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1595,13 +1595,13 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, // we only warn if the largest variant is at least thrice as large as // the second-largest. if largest > slargest * 3 && slargest > 0 { - // Use lint::emit_lint rather than sess.add_lint because the lint-printing + // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing // pass for the latter already ran. - lint::emit_lint(&ccx.tcx().sess, lint::builtin::variant_size_difference, - lvlsrc, sp, - format!("enum variant is more than three times larger \ - ({} bytes) than the next largest (ignoring padding)", - largest).as_slice()); + lint::raw_emit_lint(&ccx.tcx().sess, lint::builtin::variant_size_difference, + lvlsrc, Some(sp), + 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, "this variant is the largest"); From 0260fd27a0df1dfcbd8bb49a4d5da096d65db67c Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Thu, 12 Jun 2014 22:20:47 -0700 Subject: [PATCH 08/24] Stop using Default for initializing builtin lints It wasn't a very appropriate use of the trait. Instead, just enumerate unit structs and those with a `fn new()` separately. --- src/librustc/lint/builtin.rs | 40 ++++++++---------------------------- src/librustc/lint/context.rs | 30 ++++++++++++++++----------- 2 files changed, 27 insertions(+), 43 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 4d2abb9951e5c..6ff0be12dd6c7 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -21,8 +21,9 @@ //! as a `LintPass`. //! //! If you define a new `LintPass`, you will also need to add it to the -//! `add_builtin_lints!()` invocation in `context.rs`. That macro -//! requires a `Default` impl for your `LintPass` type. +//! `add_builtin!` or `add_builtin_with_new!` invocation in `context.rs`. +//! Use the former for unit-like structs and the latter for structs with +//! a `pub fn new()`. use metadata::csearch; use middle::def::*; @@ -45,7 +46,6 @@ use std::u16; use std::u32; use std::u64; use std::u8; -use std::default::Default; use syntax::abi; use syntax::ast_map; use syntax::attr::AttrMetaMethods; @@ -57,7 +57,6 @@ use syntax::{ast, ast_util, visit}; declare_lint!(while_true, Warn, "suggest using `loop { }` instead of `while true { }`") -#[deriving(Default)] pub struct WhileTrue; impl LintPass for WhileTrue { @@ -90,7 +89,6 @@ impl LintPass for WhileTrue { declare_lint!(unnecessary_typecast, Allow, "detects unnecessary type casts, that can be removed") -#[deriving(Default)] pub struct UnusedCasts; impl LintPass for UnusedCasts { @@ -125,8 +123,8 @@ pub struct TypeLimits { negated_expr_id: ast::NodeId, } -impl Default for TypeLimits { - fn default() -> TypeLimits { +impl TypeLimits { + pub fn new() -> TypeLimits { TypeLimits { negated_expr_id: -1, } @@ -320,7 +318,6 @@ impl LintPass for TypeLimits { declare_lint!(ctypes, Warn, "proper use of libc types in foreign modules") -#[deriving(Default)] pub struct CTypes; impl LintPass for CTypes { @@ -389,7 +386,6 @@ declare_lint!(owned_heap_memory, Allow, declare_lint!(heap_memory, Allow, "use of any (Box type or @ type) heap memory") -#[deriving(Default)] pub struct HeapMemory; impl HeapMemory { @@ -494,8 +490,8 @@ pub struct RawPointerDeriving { checked_raw_pointers: NodeSet, } -impl Default for RawPointerDeriving { - fn default() -> RawPointerDeriving { +impl RawPointerDeriving { + pub fn new() -> RawPointerDeriving { RawPointerDeriving { checked_raw_pointers: NodeSet::new(), } @@ -540,7 +536,6 @@ impl LintPass for RawPointerDeriving { declare_lint!(unused_attribute, Warn, "detects attributes that were not used by the compiler") -#[deriving(Default)] pub struct UnusedAttribute; impl LintPass for UnusedAttribute { @@ -622,7 +617,6 @@ impl LintPass for UnusedAttribute { declare_lint!(path_statement, Warn, "path statements with no effect") -#[deriving(Default)] pub struct PathStatement; impl LintPass for PathStatement { @@ -650,7 +644,6 @@ declare_lint!(unused_must_use, Warn, declare_lint!(unused_result, Allow, "unused result of an expression in a statement") -#[deriving(Default)] pub struct UnusedResult; impl LintPass for UnusedResult { @@ -711,7 +704,6 @@ impl LintPass for UnusedResult { declare_lint!(deprecated_owned_vector, Allow, "use of a `~[T]` vector") -#[deriving(Default)] pub struct DeprecatedOwnedVector; impl LintPass for DeprecatedOwnedVector { @@ -737,7 +729,6 @@ impl LintPass for DeprecatedOwnedVector { declare_lint!(non_camel_case_types, Warn, "types, variants and traits should have camel case names") -#[deriving(Default)] pub struct NonCamelCaseTypes; impl LintPass for NonCamelCaseTypes { @@ -823,7 +814,6 @@ 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)] pub struct NonSnakeCaseFunctions; impl NonSnakeCaseFunctions { @@ -901,7 +891,6 @@ impl LintPass for NonSnakeCaseFunctions { declare_lint!(non_uppercase_statics, Allow, "static constants should have uppercase identifiers") -#[deriving(Default)] pub struct NonUppercaseStatics; impl LintPass for NonUppercaseStatics { @@ -933,7 +922,6 @@ 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 { @@ -964,7 +952,6 @@ impl LintPass for NonUppercasePatternStatics { declare_lint!(uppercase_variables, Warn, "variable and structure field names should start with a lowercase character") -#[deriving(Default)] pub struct UppercaseVariables; impl LintPass for UppercaseVariables { @@ -1013,7 +1000,6 @@ impl LintPass for UppercaseVariables { declare_lint!(unnecessary_parens, Warn, "`if`, `match`, `while` and `return` do not need parentheses") -#[deriving(Default)] pub struct UnnecessaryParens; impl UnnecessaryParens { @@ -1064,7 +1050,6 @@ impl LintPass for UnnecessaryParens { declare_lint!(unused_unsafe, Warn, "unnecessary use of an `unsafe` block") -#[deriving(Default)] pub struct UnusedUnsafe; impl LintPass for UnusedUnsafe { @@ -1089,7 +1074,6 @@ impl LintPass for UnusedUnsafe { declare_lint!(unsafe_block, Allow, "usage of an `unsafe` block") -#[deriving(Default)] pub struct UnsafeBlock; impl LintPass for UnsafeBlock { @@ -1111,7 +1095,6 @@ impl LintPass for UnsafeBlock { declare_lint!(unused_mut, Warn, "detect mut variables which don't need to be mutable") -#[deriving(Default)] pub struct UnusedMut; impl UnusedMut { @@ -1197,7 +1180,6 @@ enum Allocation { declare_lint!(unnecessary_allocation, Warn, "detects unnecessary allocations that can be eliminated") -#[deriving(Default)] pub struct UnnecessaryAllocation; impl LintPass for UnnecessaryAllocation { @@ -1269,17 +1251,15 @@ pub struct MissingDoc { doc_hidden_stack: Vec, } -impl Default for MissingDoc { - fn default() -> MissingDoc { +impl MissingDoc { + pub fn new() -> MissingDoc { MissingDoc { exported_items: None, struct_def_stack: vec!(), doc_hidden_stack: vec!(false), } } -} -impl MissingDoc { fn doc_hidden(&self) -> bool { *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") } @@ -1421,7 +1401,6 @@ declare_lint!(unstable, Allow, /// Checks for use of items with `#[deprecated]`, `#[experimental]` and /// `#[unstable]` attributes, or no stability attribute. -#[deriving(Default)] pub struct Stability; impl LintPass for Stability { @@ -1556,7 +1535,6 @@ declare_lint!(pub variant_size_difference, Allow, /// 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 { diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 49bdf7a8a5e52..4364dd61fc6a3 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -37,7 +37,6 @@ use lint::builtin; use std::collections::HashMap; use std::rc::Rc; use std::cell::RefCell; -use std::default::Default; use std::tuple::Tuple2; use std::mem; use syntax::ast_util::IdVisitingOperation; @@ -122,23 +121,30 @@ impl LintStore { } pub fn register_builtin(&mut self, sess: Option<&Session>) { - macro_rules! add_builtin_lints ( ( $sess:ident, $($name:ident),*, ) => ( + macro_rules! add_builtin ( ( $sess:ident, $($name:ident),*, ) => ( {$( - { - let obj: builtin::$name = Default::default(); - self.register_pass($sess, false, box obj as LintPassObject); - }; + self.register_pass($sess, false, box builtin::$name as LintPassObject); )*} )) - add_builtin_lints!(sess, HardwiredLints, - WhileTrue, UnusedCasts, TypeLimits, CTypes, HeapMemory, - RawPointerDeriving, UnusedAttribute, PathStatement, - UnusedResult, DeprecatedOwnedVector, NonCamelCaseTypes, + macro_rules! add_builtin_with_new ( ( $sess:ident, $($name:ident),*, ) => ( + {$( + self.register_pass($sess, false, box builtin::$name::new() as LintPassObject); + )*} + )) + + add_builtin!(sess, HardwiredLints, + WhileTrue, UnusedCasts, CTypes, HeapMemory, + UnusedAttribute, PathStatement, UnusedResult, + DeprecatedOwnedVector, NonCamelCaseTypes, NonSnakeCaseFunctions, NonUppercaseStatics, NonUppercasePatternStatics, UppercaseVariables, - UnnecessaryParens, UnusedUnsafe, UnsafeBlock, UnusedMut, - UnnecessaryAllocation, MissingDoc, Stability, + UnnecessaryParens, UnusedUnsafe, UnsafeBlock, + UnusedMut, UnnecessaryAllocation, Stability, + ) + + add_builtin_with_new!(sess, + TypeLimits, RawPointerDeriving, MissingDoc, ) // We have one lint pass defined in this module. From 8a99dbb64dbb7bd848ca9b2bffe725b04226992c Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Thu, 12 Jun 2014 22:27:29 -0700 Subject: [PATCH 09/24] Remove big indent in enum_variant_size_lint Suggested viewing: git show -w --- src/librustc/middle/trans/base.rs | 83 ++++++++++++++++--------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 5314b69c40e47..93bd8341496fa 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1563,50 +1563,51 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully let levels = ccx.tcx.node_lint_levels.borrow(); - match levels.find(&(id, lint::LintId::of(lint::builtin::variant_size_difference))) { - None | Some(&(lint::Allow, _)) => (), - Some(&lvlsrc) => { - let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id)); - match *avar { - adt::General(_, ref variants) => { - for var in variants.iter() { - let mut size = 0; - for field in var.fields.iter().skip(1) { - // skip the discriminant - size += llsize_of_real(ccx, sizing_type_of(ccx, *field)); - } - sizes.push(size); - } - }, - _ => { /* its size is either constant or unimportant */ } + let lint_id = lint::LintId::of(lint::builtin::variant_size_difference); + let lvlsrc = match levels.find(&(id, lint_id)) { + None | Some(&(lint::Allow, _)) => return, + Some(&lvlsrc) => lvlsrc, + }; + + let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id)); + match *avar { + adt::General(_, ref variants) => { + for var in variants.iter() { + let mut size = 0; + for field in var.fields.iter().skip(1) { + // skip the discriminant + size += llsize_of_real(ccx, sizing_type_of(ccx, *field)); + } + sizes.push(size); } + }, + _ => { /* its size is either constant or unimportant */ } + } - let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0), - |(l, s, li), (idx, &size)| - if size > l { - (size, l, idx) - } else if size > s { - (l, size, li) - } else { - (l, s, li) - } - ); - - // we only warn if the largest variant is at least thrice as large as - // the second-largest. - if largest > slargest * 3 && slargest > 0 { - // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing - // pass for the latter already ran. - lint::raw_emit_lint(&ccx.tcx().sess, lint::builtin::variant_size_difference, - lvlsrc, Some(sp), - 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, - "this variant is the largest"); + let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0), + |(l, s, li), (idx, &size)| + if size > l { + (size, l, idx) + } else if size > s { + (l, size, li) + } else { + (l, s, li) } - } + ); + + // we only warn if the largest variant is at least thrice as large as + // the second-largest. + if largest > slargest * 3 && slargest > 0 { + // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing + // pass for the latter already ran. + lint::raw_emit_lint(&ccx.tcx().sess, lint::builtin::variant_size_difference, + lvlsrc, Some(sp), + 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, + "this variant is the largest"); } } From 9b90a7d9029b2bc8102142ca7a25518d209e7f43 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Thu, 12 Jun 2014 22:28:38 -0700 Subject: [PATCH 10/24] Use default PartialEq::ne for LintId --- src/librustc/lint/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index bd9739652f990..2489f814eb940 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -167,10 +167,6 @@ impl PartialEq for LintId { fn eq(&self, other: &LintId) -> bool { (self.lint as *Lint) == (other.lint as *Lint) } - - fn ne(&self, other: &LintId) -> bool { - (self.lint as *Lint) != (other.lint as *Lint) - } } impl Eq for LintId { } From 6f77351b3eed21aa44c2c32c9d83d9e0218c934f Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Thu, 12 Jun 2014 22:42:27 -0700 Subject: [PATCH 11/24] describe_lints: Address review comments --- src/librustc/driver/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs index 22a1c5bc98e6e..9d79480ec93d6 100644 --- a/src/librustc/driver/mod.rs +++ b/src/librustc/driver/mod.rs @@ -157,13 +157,13 @@ Available lint options: // let plugin = sort_lints(plugin); let builtin = sort_lints(builtin); - // FIXME: What if someone uses combining characters or East Asian fullwidth - // characters in a lint name?!?!? + // FIXME (#7043): We should use the width in character cells rather than + // the number of codepoints. let max_name_len = builtin.iter() .map(|&s| s.name.char_len()) .max().unwrap_or(0); let padded = |x: &str| { - format!("{}{}", " ".repeat(max_name_len - x.char_len()), x) + " ".repeat(max_name_len - x.char_len()).append(x) }; println!("Lint checks provided by rustc:\n"); From bd73a4bbe09680f378ea07310990f8d43fdfc769 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 13 Jun 2014 00:00:49 -0700 Subject: [PATCH 12/24] Run lint passes using the Option dance instead of RefCells --- src/librustc/lint/context.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 4364dd61fc6a3..97f7b6404619f 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -56,7 +56,9 @@ pub struct LintStore { lints: Vec<(&'static Lint, bool)>, /// Trait objects for each lint pass. - passes: Vec>, + /// This is only `None` while iterating over the objects. See the definition + /// of run_lints. + passes: Option>, /// Lints indexed by name. by_name: HashMap<&'static str, LintId>, @@ -84,7 +86,7 @@ impl LintStore { pub fn new() -> LintStore { LintStore { lints: vec!(), - passes: vec!(), + passes: Some(vec!()), by_name: HashMap::new(), levels: HashMap::new(), } @@ -117,7 +119,7 @@ impl LintStore { self.levels.insert(id, (lint.default_level, Default)); } } - self.passes.push(RefCell::new(pass)); + self.passes.get_mut_ref().push(pass); } pub fn register_builtin(&mut self, sess: Option<&Session>) { @@ -181,11 +183,15 @@ pub struct Context<'a> { } /// Convenience macro for calling a `LintPass` method on every pass in the context. -macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ( - for obj in $cx.lints.passes.iter() { - obj.borrow_mut().$f($cx, $($args),*); - } -)) +macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ({ + // Move the vector of passes out of `$cx` so that we can + // iterate over it mutably while passing `$cx` to the methods. + let mut passes = $cx.lints.passes.take_unwrap(); + for obj in passes.mut_iter() { + obj.$f($cx, $($args),*); + } + $cx.lints.passes = Some(passes); +})) /// Emit a lint as a warning or an error (or not at all) /// according to `level`. From 4f026f1203e7ac4185ea944b73c916cdc7363c82 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 13 Jun 2014 13:04:52 -0700 Subject: [PATCH 13/24] Use names in Lint structs in an ASCII-case-insensitive way In preparation for the next commit. --- src/librustc/driver/mod.rs | 3 ++- src/librustc/lint/context.rs | 13 +++++++------ src/librustc/lint/mod.rs | 24 ++++++++++++++++++------ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs index 9d79480ec93d6..95bcdd5ec0f3c 100644 --- a/src/librustc/driver/mod.rs +++ b/src/librustc/driver/mod.rs @@ -146,6 +146,7 @@ Available lint options: let mut lints: Vec<_> = lints.move_iter().map(|(x, _)| x).collect(); lints.sort_by(|x: &&Lint, y: &&Lint| { match x.default_level.cmp(&y.default_level) { + // The sort doesn't case-fold but it's doubtful we care. Equal => x.name.cmp(&y.name), r => r, } @@ -172,7 +173,7 @@ Available lint options: let print_lints = |lints: Vec<&Lint>| { for lint in lints.move_iter() { - let name = lint.name.replace("_", "-"); + let name = lint.name_lower().replace("_", "-"); println!(" {} {:7.7s} {}", padded(name.as_slice()), lint.default_level.as_str(), lint.desc); } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 97f7b6404619f..35f4af6c346a5 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -61,7 +61,7 @@ pub struct LintStore { passes: Option>, /// Lints indexed by name. - by_name: HashMap<&'static str, LintId>, + by_name: HashMap, /// Current levels of each lint, and where they were set. levels: HashMap, @@ -102,8 +102,8 @@ impl LintStore { self.lints.push((lint, from_plugin)); let id = LintId::of(lint); - if !self.by_name.insert(lint.name, id) { - let msg = format!("duplicate specification of lint {}", lint.name); + if !self.by_name.insert(lint.name_lower(), id) { + let msg = format!("duplicate specification of lint {}", lint.name_lower()); match (sess, from_plugin) { // We load builtin lints first, so a duplicate is a compiler bug. // Use early_error when handling -W help with no crate. @@ -205,18 +205,19 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint, let (mut level, source) = lvlsrc; if level == Allow { return } + let name = lint.name_lower(); let mut note = None; let msg = match source { Default => { format!("{}, \\#[{}({})] on by default", msg, - level.as_str(), lint.name) + level.as_str(), name) }, CommandLine => { format!("{} [-{} {}]", msg, match level { Warn => 'W', Deny => 'D', Forbid => 'F', Allow => fail!() - }, lint.name.replace("_", "-")) + }, name.replace("_", "-")) }, Node(src) => { note = Some(src); @@ -360,7 +361,7 @@ impl<'a> Context<'a> { for meta in metas.iter() { match meta.node { ast::MetaWord(ref lint_name) => { - match self.lints.by_name.find_equiv(lint_name) { + match self.lints.by_name.find_equiv(&lint_name.get()) { Some(lint_id) => out.push((*lint_id, level, meta.span)), None => self.span_lint(builtin::unrecognized_lint, diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 2489f814eb940..243d81f870cf4 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -32,6 +32,7 @@ use middle::privacy::ExportedItems; use std::hash; +use std::ascii::StrAsciiExt; use syntax::codemap::Span; use syntax::visit::FnKind; use syntax::ast; @@ -42,10 +43,14 @@ pub use lint::context::{Context, LintStore, raw_emit_lint, check_crate}; pub struct Lint { /// A string 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. + /// This identifies the lint in attributes and in command-line arguments. + /// In those contexts it is always lowercase, but this field is compared + /// in a way which is case-insensitive for ASCII characters. This allows + /// `declare_lint!()` invocations to follow the convention of upper-case + /// statics without repeating the name. + /// + /// The name is written with underscores, e.g. "unused_imports". + /// On the command line, underscores become dashes. pub name: &'static str, /// Default level for the lint. @@ -57,6 +62,13 @@ pub struct Lint { pub desc: &'static str, } +impl Lint { + /// Get the lint's name, with ASCII letters converted to lowercase. + pub fn name_lower(&self) -> String { + self.name.to_ascii_lower() + } +} + /// Build a `Lint` initializer. #[macro_export] macro_rules! lint_initializer ( @@ -187,8 +199,8 @@ impl LintId { } /// Get the name of the lint. - pub fn as_str(&self) -> &'static str { - self.lint.name + pub fn as_str(&self) -> String { + self.lint.name_lower() } } From 90a33b7461698150f142c552989202dad73f4c4f Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 13 Jun 2014 13:13:05 -0700 Subject: [PATCH 14/24] Convert builtin lints to uppercase names for style consistency --- src/librustc/driver/driver.rs | 4 +- src/librustc/front/feature_gate.rs | 2 +- src/librustc/lint/builtin.rs | 214 ++++++++++++------------ src/librustc/lint/context.rs | 6 +- src/librustc/middle/dead.rs | 2 +- src/librustc/middle/liveness.rs | 6 +- src/librustc/middle/privacy.rs | 2 +- src/librustc/middle/resolve.rs | 6 +- src/librustc/middle/trans/base.rs | 4 +- src/librustc/middle/typeck/check/mod.rs | 4 +- src/librustdoc/core.rs | 2 +- 11 files changed, 126 insertions(+), 126 deletions(-) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 2ac78f5c24aab..4b66a3b2cd2e4 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -768,7 +768,7 @@ pub fn collect_crate_types(session: &Session, } Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable), Some(_) => { - session.add_lint(lint::builtin::unknown_crate_type, + session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPE, ast::CRATE_NODE_ID, a.span, "invalid `crate_type` \ @@ -776,7 +776,7 @@ pub fn collect_crate_types(session: &Session, None } _ => { - session.add_lint(lint::builtin::unknown_crate_type, + session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPE, ast::CRATE_NODE_ID, a.span, "`crate_type` requires a \ diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index edb379e03b526..507d66638e5c2 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -366,7 +366,7 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) { directive not necessary"); } None => { - sess.add_lint(lint::builtin::unknown_features, + sess.add_lint(lint::builtin::UNKNOWN_FEATURES, ast::CRATE_NODE_ID, mi.span, "unknown feature".to_string()); diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 6ff0be12dd6c7..3b984386bc1e9 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -54,14 +54,14 @@ use syntax::codemap::Span; use syntax::parse::token; use syntax::{ast, ast_util, visit}; -declare_lint!(while_true, Warn, +declare_lint!(WHILE_TRUE, Warn, "suggest using `loop { }` instead of `while true { }`") pub struct WhileTrue; impl LintPass for WhileTrue { fn get_lints(&self) -> LintArray { - lint_array!(while_true) + lint_array!(WHILE_TRUE) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { @@ -71,7 +71,7 @@ impl LintPass for WhileTrue { ast::ExprLit(lit) => { match lit.node { ast::LitBool(true) => { - cx.span_lint(while_true, e.span, + cx.span_lint(WHILE_TRUE, e.span, "denote infinite loops with loop \ { ... }"); } @@ -86,14 +86,14 @@ impl LintPass for WhileTrue { } } -declare_lint!(unnecessary_typecast, Allow, +declare_lint!(UNNECESSARY_TYPECAST, Allow, "detects unnecessary type casts, that can be removed") pub struct UnusedCasts; impl LintPass for UnusedCasts { fn get_lints(&self) -> LintArray { - lint_array!(unnecessary_typecast) + lint_array!(UNNECESSARY_TYPECAST) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { @@ -101,7 +101,7 @@ impl LintPass for UnusedCasts { ast::ExprCast(expr, ty) => { let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.type_context()), ty); if ty::get(ty::expr_ty(cx.type_context(), expr)).sty == ty::get(t_t).sty { - cx.span_lint(unnecessary_typecast, ty.span, "unnecessary type cast"); + cx.span_lint(UNNECESSARY_TYPECAST, ty.span, "unnecessary type cast"); } } _ => () @@ -109,13 +109,13 @@ impl LintPass for UnusedCasts { } } -declare_lint!(unsigned_negate, Warn, +declare_lint!(UNSIGNED_NEGATE, Warn, "using an unary minus operator on unsigned type") -declare_lint!(type_limits, Warn, +declare_lint!(TYPE_LIMITS, Warn, "comparisons made useless by limits of the types involved") -declare_lint!(type_overflow, Warn, +declare_lint!(TYPE_OVERFLOW, Warn, "literal out of range for its type") pub struct TypeLimits { @@ -133,7 +133,7 @@ impl TypeLimits { impl LintPass for TypeLimits { fn get_lints(&self) -> LintArray { - lint_array!(unsigned_negate, type_limits, type_overflow) + lint_array!(UNSIGNED_NEGATE, TYPE_LIMITS, TYPE_OVERFLOW) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { @@ -143,7 +143,7 @@ impl LintPass for TypeLimits { ast::ExprLit(lit) => { match lit.node { ast::LitUint(..) => { - cx.span_lint(unsigned_negate, e.span, + cx.span_lint(UNSIGNED_NEGATE, e.span, "negation of unsigned int literal may \ be unintentional"); }, @@ -154,7 +154,7 @@ impl LintPass for TypeLimits { let t = ty::expr_ty(cx.type_context(), expr); match ty::get(t).sty { ty::ty_uint(_) => { - cx.span_lint(unsigned_negate, e.span, + cx.span_lint(UNSIGNED_NEGATE, e.span, "negation of unsigned int variable may \ be unintentional"); }, @@ -172,7 +172,7 @@ impl LintPass for TypeLimits { }, ast::ExprBinary(binop, l, r) => { if is_comparison(binop) && !check_limits(cx.type_context(), binop, l, r) { - cx.span_lint(type_limits, e.span, + cx.span_lint(TYPE_LIMITS, e.span, "comparison is useless due to type limits"); } }, @@ -193,7 +193,7 @@ impl LintPass for TypeLimits { lit_val *= -1; } if lit_val < min || lit_val > max { - cx.span_lint(type_overflow, e.span, + cx.span_lint(TYPE_OVERFLOW, e.span, "literal out of range for its type"); } }, @@ -209,7 +209,7 @@ impl LintPass for TypeLimits { _ => fail!() }; if lit_val < min || lit_val > max { - cx.span_lint(type_overflow, e.span, + cx.span_lint(TYPE_OVERFLOW, e.span, "literal out of range for its type"); } }, @@ -315,14 +315,14 @@ impl LintPass for TypeLimits { } } -declare_lint!(ctypes, Warn, +declare_lint!(CTYPES, Warn, "proper use of libc types in foreign modules") pub struct CTypes; impl LintPass for CTypes { fn get_lints(&self) -> LintArray { - lint_array!(ctypes) + lint_array!(CTYPES) } fn check_item(&mut self, cx: &Context, it: &ast::Item) { @@ -331,18 +331,18 @@ impl LintPass for CTypes { ast::TyPath(_, _, id) => { match cx.type_context().def_map.borrow().get_copy(&id) { def::DefPrimTy(ast::TyInt(ast::TyI)) => { - cx.span_lint(ctypes, 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"); } def::DefPrimTy(ast::TyUint(ast::TyU)) => { - cx.span_lint(ctypes, 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"); } def::DefTy(def_id) => { if !adt::is_ffi_safe(cx.type_context(), def_id) { - cx.span_lint(ctypes, ty.span, + cx.span_lint(CTYPES, ty.span, "found enum type without foreign-function-safe \ representation annotation in foreign module"); // hmm... this message could be more helpful @@ -377,13 +377,13 @@ impl LintPass for CTypes { } } -declare_lint!(managed_heap_memory, Allow, +declare_lint!(MANAGED_HEAP_MEMORY, Allow, "use of managed (@ type) heap memory") -declare_lint!(owned_heap_memory, Allow, +declare_lint!(OWNED_HEAP_MEMORY, Allow, "use of owned (Box type) heap memory") -declare_lint!(heap_memory, Allow, +declare_lint!(HEAP_MEMORY, Allow, "use of any (Box type or @ type) heap memory") pub struct HeapMemory; @@ -418,22 +418,22 @@ impl HeapMemory { if n_uniq > 0 { let s = ty_to_str(cx.type_context(), ty); 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()); + cx.span_lint(OWNED_HEAP_MEMORY, span, m.as_slice()); + cx.span_lint(HEAP_MEMORY, span, m.as_slice()); } if n_box > 0 { let s = ty_to_str(cx.type_context(), ty); let m = format!("type uses managed (@ type) pointers: {}", s); - cx.span_lint(managed_heap_memory, span, m.as_slice()); - cx.span_lint(heap_memory, span, m.as_slice()); + cx.span_lint(MANAGED_HEAP_MEMORY, span, m.as_slice()); + cx.span_lint(HEAP_MEMORY, span, m.as_slice()); } } } impl LintPass for HeapMemory { fn get_lints(&self) -> LintArray { - lint_array!(managed_heap_memory, owned_heap_memory, heap_memory) + lint_array!(MANAGED_HEAP_MEMORY, OWNED_HEAP_MEMORY, HEAP_MEMORY) } fn check_item(&mut self, cx: &Context, it: &ast::Item) { @@ -465,7 +465,7 @@ impl LintPass for HeapMemory { } } -declare_lint!(raw_pointer_deriving, Warn, +declare_lint!(RAW_POINTER_DERIVING, Warn, "uses of #[deriving] with raw pointers are rarely correct") struct RawPtrDerivingVisitor<'a> { @@ -476,7 +476,7 @@ impl<'a> visit::Visitor<()> for RawPtrDerivingVisitor<'a> { fn visit_ty(&mut self, ty: &ast::Ty, _: ()) { static MSG: &'static str = "use of `#[deriving]` with a raw pointer"; match ty.node { - ast::TyPtr(..) => self.cx.span_lint(raw_pointer_deriving, ty.span, MSG), + ast::TyPtr(..) => self.cx.span_lint(RAW_POINTER_DERIVING, ty.span, MSG), _ => {} } visit::walk_ty(self, ty, ()); @@ -500,7 +500,7 @@ impl RawPointerDeriving { impl LintPass for RawPointerDeriving { fn get_lints(&self) -> LintArray { - lint_array!(raw_pointer_deriving) + lint_array!(RAW_POINTER_DERIVING) } fn check_item(&mut self, cx: &Context, item: &ast::Item) { @@ -533,14 +533,14 @@ impl LintPass for RawPointerDeriving { } } -declare_lint!(unused_attribute, Warn, +declare_lint!(UNUSED_ATTRIBUTE, Warn, "detects attributes that were not used by the compiler") pub struct UnusedAttribute; impl LintPass for UnusedAttribute { fn get_lints(&self) -> LintArray { - lint_array!(unused_attribute) + lint_array!(UNUSED_ATTRIBUTE) } fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) { @@ -600,7 +600,7 @@ impl LintPass for UnusedAttribute { } if !attr::is_used(attr) { - cx.span_lint(unused_attribute, attr.span, "unused attribute"); + cx.span_lint(UNUSED_ATTRIBUTE, attr.span, "unused attribute"); if CRATE_ATTRS.contains(&attr.name().get()) { let msg = match attr.node.style { ast::AttrOuter => "crate-level attribute should be an inner \ @@ -608,27 +608,27 @@ impl LintPass for UnusedAttribute { ast::AttrInner => "crate-level attribute should be in the \ root module", }; - cx.span_lint(unused_attribute, attr.span, msg); + cx.span_lint(UNUSED_ATTRIBUTE, attr.span, msg); } } } } -declare_lint!(path_statement, Warn, +declare_lint!(PATH_STATEMENT, Warn, "path statements with no effect") pub struct PathStatement; impl LintPass for PathStatement { fn get_lints(&self) -> LintArray { - lint_array!(path_statement) + lint_array!(PATH_STATEMENT) } fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { match s.node { ast::StmtSemi(expr, _) => { match expr.node { - ast::ExprPath(_) => cx.span_lint(path_statement, s.span, + ast::ExprPath(_) => cx.span_lint(PATH_STATEMENT, s.span, "path statement with no effect"), _ => () } @@ -638,17 +638,17 @@ impl LintPass for PathStatement { } } -declare_lint!(unused_must_use, Warn, +declare_lint!(UNUSED_MUST_USE, Warn, "unused result of a type flagged as #[must_use]") -declare_lint!(unused_result, Allow, +declare_lint!(UNUSED_RESULT, Allow, "unused result of an expression in a statement") pub struct UnusedResult; impl LintPass for UnusedResult { fn get_lints(&self) -> LintArray { - lint_array!(unused_must_use, unused_result) + lint_array!(UNUSED_MUST_USE, UNUSED_RESULT) } fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { @@ -676,7 +676,7 @@ impl LintPass for UnusedResult { ast_map::NodeItem(it) => { if attr::contains_name(it.attrs.as_slice(), "must_use") { - cx.span_lint(unused_must_use, s.span, + cx.span_lint(UNUSED_MUST_USE, s.span, "unused result which must be used"); warned = true; } @@ -686,7 +686,7 @@ impl LintPass for UnusedResult { } else { csearch::get_item_attrs(&cx.session().cstore, did, |attrs| { if attr::contains_name(attrs.as_slice(), "must_use") { - cx.span_lint(unused_must_use, s.span, + cx.span_lint(UNUSED_MUST_USE, s.span, "unused result which must be used"); warned = true; } @@ -696,19 +696,19 @@ impl LintPass for UnusedResult { _ => {} } if !warned { - cx.span_lint(unused_result, s.span, "unused result"); + cx.span_lint(UNUSED_RESULT, s.span, "unused result"); } } } -declare_lint!(deprecated_owned_vector, Allow, +declare_lint!(DEPRECATED_OWNED_VECTOR, Allow, "use of a `~[T]` vector") pub struct DeprecatedOwnedVector; impl LintPass for DeprecatedOwnedVector { fn get_lints(&self) -> LintArray { - lint_array!(deprecated_owned_vector) + lint_array!(DEPRECATED_OWNED_VECTOR) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { @@ -716,7 +716,7 @@ impl LintPass for DeprecatedOwnedVector { match ty::get(t).sty { ty::ty_uniq(t) => match ty::get(t).sty { ty::ty_vec(_, None) => { - cx.span_lint(deprecated_owned_vector, e.span, + cx.span_lint(DEPRECATED_OWNED_VECTOR, e.span, "use of deprecated `~[]` vector; replaced by `std::vec::Vec`") } _ => {} @@ -726,14 +726,14 @@ impl LintPass for DeprecatedOwnedVector { } } -declare_lint!(non_camel_case_types, Warn, +declare_lint!(NON_CAMEL_CASE_TYPES, Warn, "types, variants and traits should have camel case names") pub struct NonCamelCaseTypes; impl LintPass for NonCamelCaseTypes { fn get_lints(&self) -> LintArray { - lint_array!(non_camel_case_types) + lint_array!(NON_CAMEL_CASE_TYPES) } fn check_item(&mut self, cx: &Context, it: &ast::Item) { @@ -758,7 +758,7 @@ impl LintPass for NonCamelCaseTypes { let s = token::get_ident(ident); if !is_camel_case(ident) { - cx.span_lint(non_camel_case_types, span, + cx.span_lint(NON_CAMEL_CASE_TYPES, span, format!("{} `{}` should have a camel case name such as `{}`", sort, s, to_camel_case(s.get())).as_slice()); } @@ -811,7 +811,7 @@ fn method_context(cx: &Context, m: &ast::Method) -> MethodContext { } } -declare_lint!(non_snake_case_functions, Warn, +declare_lint!(NON_SNAKE_CASE_FUNCTIONS, Warn, "methods and functions should have snake case names") pub struct NonSnakeCaseFunctions; @@ -854,7 +854,7 @@ impl NonSnakeCaseFunctions { let s = token::get_ident(ident); if !is_snake_case(ident) { - cx.span_lint(non_snake_case_functions, span, + cx.span_lint(NON_SNAKE_CASE_FUNCTIONS, span, format!("{} `{}` should have a snake case name such as `{}`", sort, s, to_snake_case(s.get())).as_slice()); } @@ -863,7 +863,7 @@ impl NonSnakeCaseFunctions { impl LintPass for NonSnakeCaseFunctions { fn get_lints(&self) -> LintArray { - lint_array!(non_snake_case_functions) + lint_array!(NON_SNAKE_CASE_FUNCTIONS) } fn check_fn(&mut self, cx: &Context, @@ -888,14 +888,14 @@ impl LintPass for NonSnakeCaseFunctions { } } -declare_lint!(non_uppercase_statics, Allow, +declare_lint!(NON_UPPERCASE_STATICS, Allow, "static constants should have uppercase identifiers") pub struct NonUppercaseStatics; impl LintPass for NonUppercaseStatics { fn get_lints(&self) -> LintArray { - lint_array!(non_uppercase_statics) + lint_array!(NON_UPPERCASE_STATICS) } fn check_item(&mut self, cx: &Context, it: &ast::Item) { @@ -907,7 +907,7 @@ impl LintPass for NonUppercaseStatics { // ones (some scripts don't have a concept of // upper/lowercase) if s.get().chars().any(|c| c.is_lowercase()) { - cx.span_lint(non_uppercase_statics, it.span, + cx.span_lint(NON_UPPERCASE_STATICS, it.span, format!("static constant `{}` should have an uppercase name \ such as `{}`", s.get(), s.get().chars().map(|c| c.to_uppercase()) @@ -919,14 +919,14 @@ impl LintPass for NonUppercaseStatics { } } -declare_lint!(non_uppercase_pattern_statics, Warn, +declare_lint!(NON_UPPERCASE_PATTERN_STATICS, Warn, "static constants in match patterns should be all caps") pub struct NonUppercasePatternStatics; impl LintPass for NonUppercasePatternStatics { fn get_lints(&self) -> LintArray { - lint_array!(non_uppercase_pattern_statics) + lint_array!(NON_UPPERCASE_PATTERN_STATICS) } fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { @@ -937,7 +937,7 @@ impl LintPass for NonUppercasePatternStatics { let ident = path.segments.last().unwrap().identifier; let s = token::get_ident(ident); if s.get().chars().any(|c| c.is_lowercase()) { - cx.span_lint(non_uppercase_pattern_statics, path.span, + cx.span_lint(NON_UPPERCASE_PATTERN_STATICS, path.span, format!("static constant in pattern `{}` should have an uppercase \ name such as `{}`", s.get(), s.get().chars().map(|c| c.to_uppercase()) @@ -949,14 +949,14 @@ impl LintPass for NonUppercasePatternStatics { } } -declare_lint!(uppercase_variables, Warn, +declare_lint!(UPPERCASE_VARIABLES, Warn, "variable and structure field names should start with a lowercase character") pub struct UppercaseVariables; impl LintPass for UppercaseVariables { fn get_lints(&self) -> LintArray { - lint_array!(uppercase_variables) + lint_array!(UPPERCASE_VARIABLES) } fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { @@ -969,7 +969,7 @@ impl LintPass for UppercaseVariables { let ident = path.segments.last().unwrap().identifier; let s = token::get_ident(ident); if s.get().len() > 0 && s.get().char_at(0).is_uppercase() { - cx.span_lint(uppercase_variables, path.span, + cx.span_lint(UPPERCASE_VARIABLES, path.span, "variable names should start with a lowercase character"); } } @@ -987,7 +987,7 @@ impl LintPass for UppercaseVariables { ast::StructField_ { kind: ast::NamedField(ident, _), .. } => { let s = token::get_ident(ident); if s.get().char_at(0).is_uppercase() { - cx.span_lint(uppercase_variables, sf.span, + cx.span_lint(UPPERCASE_VARIABLES, sf.span, "structure field names should start with a lowercase character"); } } @@ -997,7 +997,7 @@ impl LintPass for UppercaseVariables { } } -declare_lint!(unnecessary_parens, Warn, +declare_lint!(UNNECESSARY_PARENS, Warn, "`if`, `match`, `while` and `return` do not need parentheses") pub struct UnnecessaryParens; @@ -1006,7 +1006,7 @@ impl UnnecessaryParens { fn check_unnecessary_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str) { match value.node { ast::ExprParen(_) => { - cx.span_lint(unnecessary_parens, value.span, + cx.span_lint(UNNECESSARY_PARENS, value.span, format!("unnecessary parentheses around {}", msg).as_slice()) } _ => {} @@ -1016,7 +1016,7 @@ impl UnnecessaryParens { impl LintPass for UnnecessaryParens { fn get_lints(&self) -> LintArray { - lint_array!(unnecessary_parens) + lint_array!(UNNECESSARY_PARENS) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { @@ -1047,14 +1047,14 @@ impl LintPass for UnnecessaryParens { } } -declare_lint!(unused_unsafe, Warn, +declare_lint!(UNUSED_UNSAFE, Warn, "unnecessary use of an `unsafe` block") pub struct UnusedUnsafe; impl LintPass for UnusedUnsafe { fn get_lints(&self) -> LintArray { - lint_array!(unused_unsafe) + lint_array!(UNUSED_UNSAFE) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { @@ -1063,7 +1063,7 @@ impl LintPass for UnusedUnsafe { ast::ExprBlock(ref blk) => { if blk.rules == ast::UnsafeBlock(ast::UserProvided) && !cx.type_context().used_unsafe.borrow().contains(&blk.id) { - cx.span_lint(unused_unsafe, blk.span, "unnecessary `unsafe` block"); + cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block"); } } _ => () @@ -1071,28 +1071,28 @@ impl LintPass for UnusedUnsafe { } } -declare_lint!(unsafe_block, Allow, +declare_lint!(UNSAFE_BLOCK, Allow, "usage of an `unsafe` block") pub struct UnsafeBlock; impl LintPass for UnsafeBlock { fn get_lints(&self) -> LintArray { - lint_array!(unsafe_block) + lint_array!(UNSAFE_BLOCK) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { match e.node { // Don't warn about generated blocks, that'll just pollute the output. ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => { - cx.span_lint(unsafe_block, blk.span, "usage of an `unsafe` block"); + cx.span_lint(UNSAFE_BLOCK, blk.span, "usage of an `unsafe` block"); } _ => () } } } -declare_lint!(unused_mut, Warn, +declare_lint!(UNUSED_MUT, Warn, "detect mut variables which don't need to be mutable") pub struct UnusedMut; @@ -1126,7 +1126,7 @@ impl UnusedMut { let used_mutables = cx.type_context().used_mut_nodes.borrow(); for (_, v) in mutables.iter() { if !v.iter().any(|e| used_mutables.contains(e)) { - cx.span_lint(unused_mut, cx.type_context().map.span(*v.get(0)), + cx.span_lint(UNUSED_MUT, cx.type_context().map.span(*v.get(0)), "variable does not need to be mutable"); } } @@ -1135,7 +1135,7 @@ impl UnusedMut { impl LintPass for UnusedMut { fn get_lints(&self) -> LintArray { - lint_array!(unused_mut) + lint_array!(UNUSED_MUT) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { @@ -1177,14 +1177,14 @@ enum Allocation { BoxAllocation } -declare_lint!(unnecessary_allocation, Warn, +declare_lint!(UNNECESSARY_ALLOCATION, Warn, "detects unnecessary allocations that can be eliminated") pub struct UnnecessaryAllocation; impl LintPass for UnnecessaryAllocation { fn get_lints(&self) -> LintArray { - lint_array!(unnecessary_allocation) + lint_array!(UNNECESSARY_ALLOCATION) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { @@ -1212,17 +1212,17 @@ impl LintPass for UnnecessaryAllocation { ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { match (allocation, autoref) { (VectorAllocation, Some(ty::AutoBorrowVec(..))) => { - cx.span_lint(unnecessary_allocation, e.span, + cx.span_lint(UNNECESSARY_ALLOCATION, e.span, "unnecessary allocation, the sigil can be removed"); } (BoxAllocation, Some(ty::AutoPtr(_, ast::MutImmutable))) => { - cx.span_lint(unnecessary_allocation, e.span, + cx.span_lint(UNNECESSARY_ALLOCATION, e.span, "unnecessary allocation, use & instead"); } (BoxAllocation, Some(ty::AutoPtr(_, ast::MutMutable))) => { - cx.span_lint(unnecessary_allocation, e.span, + cx.span_lint(UNNECESSARY_ALLOCATION, e.span, "unnecessary allocation, use &mut instead"); } _ => () @@ -1236,7 +1236,7 @@ impl LintPass for UnnecessaryAllocation { } } -declare_lint!(missing_doc, Allow, +declare_lint!(MISSING_DOC, Allow, "detects missing documentation for public members") pub struct MissingDoc { @@ -1293,7 +1293,7 @@ impl MissingDoc { } }); if !has_doc { - cx.span_lint(missing_doc, sp, + cx.span_lint(MISSING_DOC, sp, format!("missing documentation for {}", desc).as_slice()); } } @@ -1301,7 +1301,7 @@ impl MissingDoc { impl LintPass for MissingDoc { fn get_lints(&self) -> LintArray { - lint_array!(missing_doc) + lint_array!(MISSING_DOC) } fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) { @@ -1390,13 +1390,13 @@ impl LintPass for MissingDoc { } } -declare_lint!(deprecated, Warn, +declare_lint!(DEPRECATED, Warn, "detects use of #[deprecated] items") -declare_lint!(experimental, Warn, +declare_lint!(EXPERIMENTAL, Warn, "detects use of #[experimental] items") -declare_lint!(unstable, Allow, +declare_lint!(UNSTABLE, Allow, "detects use of #[unstable] items (incl. items with no stability attribute)") /// Checks for use of items with `#[deprecated]`, `#[experimental]` and @@ -1405,7 +1405,7 @@ pub struct Stability; impl LintPass for Stability { fn get_lints(&self) -> LintArray { - lint_array!(deprecated, experimental, unstable) + lint_array!(DEPRECATED, EXPERIMENTAL, UNSTABLE) } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { @@ -1476,13 +1476,13 @@ impl LintPass for Stability { let (lint, label) = match stability { // no stability attributes == Unstable - None => (unstable, "unmarked"), + None => (UNSTABLE, "unmarked"), Some(attr::Stability { level: attr::Unstable, .. }) => - (unstable, "unstable"), + (UNSTABLE, "unstable"), Some(attr::Stability { level: attr::Experimental, .. }) => - (experimental, "experimental"), + (EXPERIMENTAL, "experimental"), Some(attr::Stability { level: attr::Deprecated, .. }) => - (deprecated, "deprecated"), + (DEPRECATED, "deprecated"), _ => return }; @@ -1497,40 +1497,40 @@ impl LintPass for Stability { } } -declare_lint!(pub unused_imports, Warn, +declare_lint!(pub UNUSED_IMPORTS, Warn, "imports that are never used") -declare_lint!(pub unnecessary_qualification, Allow, +declare_lint!(pub UNNECESSARY_QUALIFICATION, Allow, "detects unnecessarily qualified names") -declare_lint!(pub unrecognized_lint, Warn, +declare_lint!(pub UNRECOGNIZED_LINT, Warn, "unrecognized lint attribute") -declare_lint!(pub unused_variable, Warn, +declare_lint!(pub UNUSED_VARIABLE, Warn, "detect variables which are not used in any way") -declare_lint!(pub dead_assignment, Warn, +declare_lint!(pub DEAD_ASSIGNMENT, Warn, "detect assignments that will never be read") -declare_lint!(pub dead_code, Warn, +declare_lint!(pub DEAD_CODE, Warn, "detect piece of code that will never be used") -declare_lint!(pub visible_private_types, Warn, +declare_lint!(pub VISIBLE_PRIVATE_TYPES, Warn, "detect use of private types in exported type signatures") -declare_lint!(pub unreachable_code, Warn, +declare_lint!(pub UNREACHABLE_CODE, Warn, "detects unreachable code") -declare_lint!(pub warnings, Warn, +declare_lint!(pub WARNINGS, Warn, "mass-change the level for lints which produce warnings") -declare_lint!(pub unknown_features, Deny, +declare_lint!(pub UNKNOWN_FEATURES, Deny, "unknown features found in crate-level #[feature] directives") -declare_lint!(pub unknown_crate_type, Deny, +declare_lint!(pub UNKNOWN_CRATE_TYPE, Deny, "unknown crate type found in #[crate_type] directive") -declare_lint!(pub variant_size_difference, Allow, +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 @@ -1540,9 +1540,9 @@ 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) + 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) } } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 35f4af6c346a5..384717ebd80c7 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -269,7 +269,7 @@ impl<'a> Context<'a> { let (level, src) = match self.lints.levels.find(&LintId::of(lint)) { None => return, Some(&(Warn, src)) => { - let lint_id = LintId::of(builtin::warnings); + let lint_id = LintId::of(builtin::WARNINGS); (self.lints.get_level_source(lint_id).val0(), src) } Some(&pair) => pair, @@ -364,7 +364,7 @@ impl<'a> Context<'a> { match self.lints.by_name.find_equiv(&lint_name.get()) { Some(lint_id) => out.push((*lint_id, level, meta.span)), - None => self.span_lint(builtin::unrecognized_lint, + None => self.span_lint(builtin::UNRECOGNIZED_LINT, meta.span, format!("unknown `{}` attribute: `{}`", level.as_str(), lint_name).as_slice()), @@ -593,7 +593,7 @@ impl LintPass for GatherNodeLevels { fn check_item(&mut self, cx: &Context, it: &ast::Item) { match it.node { ast::ItemEnum(..) => { - let lint_id = LintId::of(builtin::variant_size_difference); + let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCE); match cx.lints.get_level_source(lint_id) { lvlsrc @ (lvl, _) if lvl != Allow => { cx.node_levels.borrow_mut() diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 2f2e147f069e1..1ea6b46500bc1 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -443,7 +443,7 @@ impl<'a> DeadVisitor<'a> { ident: ast::Ident) { self.tcx .sess - .add_lint(lint::builtin::dead_code, + .add_lint(lint::builtin::DEAD_CODE, id, span, format!("code is never used: `{}`", diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index cb67a465d5029..511599ebaa62e 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1559,11 +1559,11 @@ impl<'a> Liveness<'a> { }; if is_assigned { - self.ir.tcx.sess.add_lint(lint::builtin::unused_variable, id, sp, + self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLE, id, sp, format!("variable `{}` is assigned to, but never used", *name)); } else { - self.ir.tcx.sess.add_lint(lint::builtin::unused_variable, id, sp, + self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLE, id, sp, format!("unused variable: `{}`", *name)); } } @@ -1581,7 +1581,7 @@ impl<'a> Liveness<'a> { if self.live_on_exit(ln, var).is_none() { let r = self.should_warn(var); for name in r.iter() { - self.ir.tcx.sess.add_lint(lint::builtin::dead_assignment, id, sp, + self.ir.tcx.sess.add_lint(lint::builtin::DEAD_ASSIGNMENT, id, sp, format!("value assigned to `{}` is never read", *name)); } } diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index f4cdd3a88f784..34e0094912eeb 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -1392,7 +1392,7 @@ impl<'a> Visitor<()> for VisiblePrivateTypesVisitor<'a> { ast::TyPath(ref p, _, path_id) => { if self.path_is_private_type(path_id) { self.tcx.sess.add_lint( - lint::builtin::visible_private_types, + lint::builtin::VISIBLE_PRIVATE_TYPES, path_id, p.span, "private type in exported type \ signature".to_string()); diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index d64336aa9feb9..c2dd61bb6f416 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -4592,7 +4592,7 @@ impl<'a> Resolver<'a> { match (def, unqualified_def) { (Some((d, _)), Some((ud, _))) if d == ud => { self.session - .add_lint(lint::builtin::unnecessary_qualification, + .add_lint(lint::builtin::UNNECESSARY_QUALIFICATION, id, path.span, "unnecessary qualification".to_string()); @@ -5427,7 +5427,7 @@ impl<'a> Resolver<'a> { if !self.used_imports.contains(&(id, TypeNS)) && !self.used_imports.contains(&(id, ValueNS)) { self.session - .add_lint(lint::builtin::unused_imports, + .add_lint(lint::builtin::UNUSED_IMPORTS, id, p.span, "unused import".to_string()); @@ -5451,7 +5451,7 @@ impl<'a> Resolver<'a> { if !self.used_imports.contains(&(id, TypeNS)) && !self.used_imports.contains(&(id, ValueNS)) { - self.session.add_lint(lint::builtin::unused_imports, + self.session.add_lint(lint::builtin::UNUSED_IMPORTS, id, span, "unused import".to_string()); diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 93bd8341496fa..5bb9fd5a1820e 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1563,7 +1563,7 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully let levels = ccx.tcx.node_lint_levels.borrow(); - let lint_id = lint::LintId::of(lint::builtin::variant_size_difference); + let lint_id = lint::LintId::of(lint::builtin::VARIANT_SIZE_DIFFERENCE); let lvlsrc = match levels.find(&(id, lint_id)) { None | Some(&(lint::Allow, _)) => return, Some(&lvlsrc) => lvlsrc, @@ -1600,7 +1600,7 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, if largest > slargest * 3 && slargest > 0 { // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing // pass for the latter already ran. - lint::raw_emit_lint(&ccx.tcx().sess, lint::builtin::variant_size_difference, + lint::raw_emit_lint(&ccx.tcx().sess, lint::builtin::VARIANT_SIZE_DIFFERENCE, lvlsrc, Some(sp), format!("enum variant is more than three times larger \ ({} bytes) than the next largest (ignoring padding)", diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 5675c009dd441..7745e8293a9f4 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3638,7 +3638,7 @@ pub fn check_block_with_expected(fcx: &FnCtxt, fcx.ccx .tcx .sess - .add_lint(lint::builtin::unreachable_code, + .add_lint(lint::builtin::UNREACHABLE_CODE, s_id, s.span, "unreachable statement".to_string()); @@ -3665,7 +3665,7 @@ pub fn check_block_with_expected(fcx: &FnCtxt, fcx.ccx .tcx .sess - .add_lint(lint::builtin::unreachable_code, + .add_lint(lint::builtin::UNREACHABLE_CODE, e.id, e.span, "unreachable expression".to_string()); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 32ea70412ff90..e5cc68c9f08c4 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -74,7 +74,7 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet, cfgs: Vec) let input = FileInput(cpath.clone()); - let warning_lint = lint::builtin::warnings.name.to_string(); + let warning_lint = lint::builtin::WARNINGS.name_lower(); let sessopts = driver::config::Options { maybe_sysroot: Some(os::self_exe_path().unwrap().dir_path()), From b8a2a0fd9fe5f65b05b0591145fe48cdf4e5347c Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 13 Jun 2014 15:03:26 -0700 Subject: [PATCH 15/24] Rework lint attr parsing and use it in middle::dead --- src/librustc/lint/context.rs | 95 ++++++++++++++++++++---------------- src/librustc/lint/mod.rs | 2 +- src/librustc/middle/dead.rs | 16 +++--- 3 files changed, 60 insertions(+), 53 deletions(-) diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 384717ebd80c7..a4bcad1e2a97c 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -44,6 +44,7 @@ use syntax::attr::AttrMetaMethods; use syntax::attr; use syntax::codemap::Span; use syntax::visit::{Visitor, FnKind}; +use syntax::parse::token::InternedString; use syntax::{ast, ast_util, visit}; /// Information about the registered lints. @@ -193,6 +194,38 @@ macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.lints.passes = Some(passes); })) +/// Parse the lint attributes into a vector, with `Err`s for malformed lint +/// attributes. Writing this as an iterator is an enormous mess. +pub fn gather_attrs(attrs: &[ast::Attribute]) + -> Vec> { + let mut out = vec!(); + for attr in attrs.iter() { + let level = match Level::from_str(attr.name().get()) { + None => continue, + Some(lvl) => lvl, + }; + + attr::mark_used(attr); + + let meta = attr.node.value; + let metas = match meta.node { + ast::MetaList(_, ref metas) => metas, + _ => { + out.push(Err(meta.span)); + continue; + } + }; + + for meta in metas.iter() { + out.push(match meta.node { + ast::MetaWord(ref lint_name) => Ok((lint_name.clone(), level, meta.span)), + _ => Err(meta.span), + }); + } + } + out +} + /// Emit a lint as a warning or an error (or not at all) /// according to `level`. /// @@ -300,9 +333,27 @@ impl<'a> Context<'a> { // 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 lint_attrs = self.gather_lint_attrs(attrs); let mut pushed = 0u; - for (lint_id, level, span) in lint_attrs.move_iter() { + + for result in gather_attrs(attrs).move_iter() { + let (lint_id, level, span) = match result { + Err(span) => { + self.tcx.sess.span_err(span, "malformed lint attribute"); + continue; + } + Ok((lint_name, level, span)) => { + match self.lints.by_name.find_equiv(&lint_name.get()) { + Some(&lint_id) => (lint_id, level, span), + None => { + self.span_lint(builtin::UNRECOGNIZED_LINT, span, + format!("unknown `{}` attribute: `{}`", + level.as_str(), lint_name).as_slice()); + continue; + } + } + } + }; + let now = self.lints.get_level_source(lint_id).val0(); if now == Forbid && level != Forbid { let lint_name = lint_id.as_str(); @@ -336,46 +387,6 @@ impl<'a> Context<'a> { }; f(&mut v); } - - fn gather_lint_attrs(&mut self, attrs: &[ast::Attribute]) -> Vec<(LintId, Level, Span)> { - // Doing this as an iterator is messy due to multiple borrowing. - // Allocating and copying these should be quick. - let mut out = vec!(); - for attr in attrs.iter() { - let level = match Level::from_str(attr.name().get()) { - None => continue, - Some(lvl) => lvl, - }; - - attr::mark_used(attr); - - let meta = attr.node.value; - let metas = match meta.node { - ast::MetaList(_, ref metas) => metas, - _ => { - self.tcx.sess.span_err(meta.span, "malformed lint attribute"); - continue; - } - }; - - for meta in metas.iter() { - match meta.node { - ast::MetaWord(ref lint_name) => { - match self.lints.by_name.find_equiv(&lint_name.get()) { - 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"), - } - } - } - out - } } impl<'a> AstConv for Context<'a>{ diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 243d81f870cf4..db2051ecf6c3c 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -37,7 +37,7 @@ use syntax::codemap::Span; use syntax::visit::FnKind; use syntax::ast; -pub use lint::context::{Context, LintStore, raw_emit_lint, check_crate}; +pub use lint::context::{Context, LintStore, raw_emit_lint, check_crate, gather_attrs}; /// Specification of a single lint. pub struct Lint { diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 1ea6b46500bc1..d89bcdc9c751f 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -251,16 +251,12 @@ fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool { 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, - _ => (), - } - }, + for attr in lint::gather_attrs(attrs).move_iter() { + match attr { + // Want to use lint::builtin::DEAD_CODE.name_lower() but that + // will allocate every time for something that's actually + // constant. + Ok((ref name, lint::Allow, _)) if name.get() == "dead_code" => return true, _ => (), } } From c45b9a78a8db1986e4e20ce0cb668aad58a852c4 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 13 Jun 2014 15:04:15 -0700 Subject: [PATCH 16/24] Make lint::context private --- src/librustc/lint/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index db2051ecf6c3c..28ba368692c77 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -249,5 +249,6 @@ pub enum LintSource { pub type LevelSource = (Level, LintSource); -pub mod context; pub mod builtin; + +mod context; From e8f4229e773e29c5b3c073134082178163e28fe5 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 13 Jun 2014 15:17:55 -0700 Subject: [PATCH 17/24] Remove a HeapMemory lint FIXME The proper, more general solution is to skip a lint pass entirely when all the lints it could emit are Allow. That should probably be a ticket once this lands. --- src/librustc/lint/builtin.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 3b984386bc1e9..95e4d1a97db28 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -390,8 +390,6 @@ pub struct HeapMemory; impl HeapMemory { fn check_heap_type(&self, cx: &Context, span: Span, ty: ty::t) { - // FIXME: skip the fold for performance when all are allowed? - let mut n_box = 0; let mut n_uniq = 0; ty::fold_ty(cx.type_context(), ty, |t| { From 18b834c3c517718a8bffca4eb0d301b4f42a8eb3 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 13 Jun 2014 15:21:02 -0700 Subject: [PATCH 18/24] Make lint::Context conform to rustc API conventions It's really too early to be thinking about this as a separate "public" API. --- src/librustc/lint/builtin.rs | 80 ++++++++++++++++++------------------ src/librustc/lint/context.rs | 13 ++---- 2 files changed, 44 insertions(+), 49 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 95e4d1a97db28..903cb7f4f2692 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -99,8 +99,8 @@ impl LintPass for UnusedCasts { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { match e.node { ast::ExprCast(expr, ty) => { - let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.type_context()), ty); - if ty::get(ty::expr_ty(cx.type_context(), expr)).sty == ty::get(t_t).sty { + 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 { cx.span_lint(UNNECESSARY_TYPECAST, ty.span, "unnecessary type cast"); } } @@ -151,7 +151,7 @@ impl LintPass for TypeLimits { } }, _ => { - let t = ty::expr_ty(cx.type_context(), expr); + let t = ty::expr_ty(cx.tcx, expr); match ty::get(t).sty { ty::ty_uint(_) => { cx.span_lint(UNSIGNED_NEGATE, e.span, @@ -171,16 +171,16 @@ impl LintPass for TypeLimits { self.negated_expr_id = expr.id; }, ast::ExprBinary(binop, l, r) => { - if is_comparison(binop) && !check_limits(cx.type_context(), binop, l, r) { + if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) { cx.span_lint(TYPE_LIMITS, e.span, "comparison is useless due to type limits"); } }, ast::ExprLit(lit) => { - match ty::get(ty::expr_ty(cx.type_context(), e)).sty { + match ty::get(ty::expr_ty(cx.tcx, e)).sty { ty::ty_int(t) => { let int_type = if t == ast::TyI { - cx.session().targ_cfg.int_type + cx.sess().targ_cfg.int_type } else { t }; let (min, max) = int_ty_range(int_type); let mut lit_val: i64 = match lit.node { @@ -199,7 +199,7 @@ impl LintPass for TypeLimits { }, ty::ty_uint(t) => { let uint_type = if t == ast::TyU { - cx.session().targ_cfg.uint_type + cx.sess().targ_cfg.uint_type } else { t }; let (min, max) = uint_ty_range(uint_type); let lit_val: u64 = match lit.node { @@ -329,7 +329,7 @@ impl LintPass for CTypes { fn check_ty(cx: &Context, ty: &ast::Ty) { match ty.node { ast::TyPath(_, _, id) => { - match cx.type_context().def_map.borrow().get_copy(&id) { + match cx.tcx.def_map.borrow().get_copy(&id) { def::DefPrimTy(ast::TyInt(ast::TyI)) => { cx.span_lint(CTYPES, ty.span, "found rust type `int` in foreign module, while \ @@ -341,7 +341,7 @@ impl LintPass for CTypes { libc::c_uint or libc::c_ulong should be used"); } def::DefTy(def_id) => { - if !adt::is_ffi_safe(cx.type_context(), def_id) { + if !adt::is_ffi_safe(cx.tcx, def_id) { cx.span_lint(CTYPES, ty.span, "found enum type without foreign-function-safe \ representation annotation in foreign module"); @@ -392,7 +392,7 @@ impl HeapMemory { fn check_heap_type(&self, cx: &Context, span: Span, ty: ty::t) { let mut n_box = 0; let mut n_uniq = 0; - ty::fold_ty(cx.type_context(), ty, |t| { + ty::fold_ty(cx.tcx, ty, |t| { match ty::get(t).sty { ty::ty_box(_) => { n_box += 1; @@ -414,14 +414,14 @@ impl HeapMemory { }); if n_uniq > 0 { - let s = ty_to_str(cx.type_context(), ty); + let s = ty_to_str(cx.tcx, ty); 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_box > 0 { - let s = ty_to_str(cx.type_context(), ty); + let s = ty_to_str(cx.tcx, ty); let m = format!("type uses managed (@ type) pointers: {}", s); cx.span_lint(MANAGED_HEAP_MEMORY, span, m.as_slice()); cx.span_lint(HEAP_MEMORY, span, m.as_slice()); @@ -441,7 +441,7 @@ impl LintPass for HeapMemory { ast::ItemEnum(..) | ast::ItemStruct(..) => self.check_heap_type(cx, it.span, - ty::node_id_to_type(cx.type_context(), it.id)), + ty::node_id_to_type(cx.tcx, it.id)), _ => () } @@ -450,7 +450,7 @@ impl LintPass for HeapMemory { ast::ItemStruct(struct_def, _) => { for struct_field in struct_def.fields.iter() { self.check_heap_type(cx, struct_field.span, - ty::node_id_to_type(cx.type_context(), struct_field.node.id)); + ty::node_id_to_type(cx.tcx, struct_field.node.id)); } } _ => () @@ -458,7 +458,7 @@ impl LintPass for HeapMemory { } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { - let ty = ty::expr_ty(cx.type_context(), e); + let ty = ty::expr_ty(cx.tcx, e); self.check_heap_type(cx, e.span, ty); } } @@ -507,7 +507,7 @@ impl LintPass for RawPointerDeriving { } let did = match item.node { ast::ItemImpl(..) => { - match ty::get(ty::node_id_to_type(cx.type_context(), item.id)).sty { + match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty { ty::ty_enum(did, _) => did, ty::ty_struct(did, _) => did, _ => return, @@ -516,7 +516,7 @@ impl LintPass for RawPointerDeriving { _ => return, }; if !ast_util::is_local(did) { return } - let item = match cx.type_context().map.find(did.node) { + let item = match cx.tcx.map.find(did.node) { Some(ast_map::NodeItem(item)) => item, _ => return, }; @@ -654,7 +654,7 @@ impl LintPass for UnusedResult { ast::StmtSemi(expr, _) => expr, _ => return }; - let t = ty::expr_ty(cx.type_context(), expr); + let t = ty::expr_ty(cx.tcx, expr); match ty::get(t).sty { ty::ty_nil | ty::ty_bot | ty::ty_bool => return, _ => {} @@ -664,13 +664,13 @@ impl LintPass for UnusedResult { _ => {} } - let t = ty::expr_ty(cx.type_context(), expr); + let t = ty::expr_ty(cx.tcx, expr); let mut warned = false; match ty::get(t).sty { ty::ty_struct(did, _) | ty::ty_enum(did, _) => { if ast_util::is_local(did) { - match cx.type_context().map.get(did.node) { + match cx.tcx.map.get(did.node) { ast_map::NodeItem(it) => { if attr::contains_name(it.attrs.as_slice(), "must_use") { @@ -682,7 +682,7 @@ impl LintPass for UnusedResult { _ => {} } } else { - csearch::get_item_attrs(&cx.session().cstore, did, |attrs| { + csearch::get_item_attrs(&cx.sess().cstore, did, |attrs| { if attr::contains_name(attrs.as_slice(), "must_use") { cx.span_lint(UNUSED_MUST_USE, s.span, "unused result which must be used"); @@ -710,7 +710,7 @@ impl LintPass for DeprecatedOwnedVector { } fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { - let t = ty::expr_ty(cx.type_context(), e); + let t = ty::expr_ty(cx.tcx, e); match ty::get(t).sty { ty::ty_uniq(t) => match ty::get(t).sty { ty::ty_vec(_, None) => { @@ -793,13 +793,13 @@ fn method_context(cx: &Context, m: &ast::Method) -> MethodContext { node: m.id }; - match cx.type_context().methods.borrow().find_copy(&did) { - None => cx.session().span_bug(m.span, "missing method descriptor?!"), + match cx.tcx.methods.borrow().find_copy(&did) { + None => cx.sess().span_bug(m.span, "missing method descriptor?!"), Some(md) => { match md.container { ty::TraitContainer(..) => TraitDefaultImpl, ty::ImplContainer(cid) => { - match ty::impl_trait_ref(cx.type_context(), cid) { + match ty::impl_trait_ref(cx.tcx, cid) { Some(..) => TraitImpl, None => PlainImpl } @@ -929,7 +929,7 @@ impl LintPass for NonUppercasePatternStatics { fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { // Lint for constants that look like binding identifiers (#7526) - match (&p.node, cx.type_context().def_map.borrow().find(&p.id)) { + match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) { (&ast::PatIdent(_, ref path, _), Some(&def::DefStatic(_, false))) => { // last identifier alone is right choice for this lint. let ident = path.segments.last().unwrap().identifier; @@ -960,7 +960,7 @@ impl LintPass for UppercaseVariables { fn check_pat(&mut self, cx: &Context, p: &ast::Pat) { match &p.node { &ast::PatIdent(_, ref path, _) => { - match cx.type_context().def_map.borrow().find(&p.id) { + match cx.tcx.def_map.borrow().find(&p.id) { Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) | Some(&def::DefArg(_, _)) => { // last identifier alone is right choice for this lint. @@ -1060,7 +1060,7 @@ impl LintPass for UnusedUnsafe { // Don't warn about generated blocks, that'll just pollute the output. ast::ExprBlock(ref blk) => { if blk.rules == ast::UnsafeBlock(ast::UserProvided) && - !cx.type_context().used_unsafe.borrow().contains(&blk.id) { + !cx.tcx.used_unsafe.borrow().contains(&blk.id) { cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block"); } } @@ -1101,11 +1101,11 @@ impl UnusedMut { // avoid false warnings in match arms with multiple patterns let mut mutables = HashMap::new(); for &p in pats.iter() { - pat_util::pat_bindings(&cx.type_context().def_map, p, |mode, id, _, path| { + pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path| { match mode { ast::BindByValue(ast::MutMutable) => { if path.segments.len() != 1 { - cx.session().span_bug(p.span, + cx.sess().span_bug(p.span, "mutable binding that doesn't consist \ of exactly one segment"); } @@ -1121,10 +1121,10 @@ impl UnusedMut { }); } - let used_mutables = cx.type_context().used_mut_nodes.borrow(); + let used_mutables = cx.tcx.used_mut_nodes.borrow(); for (_, v) in mutables.iter() { if !v.iter().any(|e| used_mutables.contains(e)) { - cx.span_lint(UNUSED_MUT, cx.type_context().map.span(*v.get(0)), + cx.span_lint(UNUSED_MUT, cx.tcx.map.span(*v.get(0)), "variable does not need to be mutable"); } } @@ -1204,7 +1204,7 @@ impl LintPass for UnnecessaryAllocation { _ => return }; - match cx.type_context().adjustments.borrow().find(&e.id) { + match cx.tcx.adjustments.borrow().find(&e.id) { Some(adjustment) => { match *adjustment { ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => { @@ -1270,7 +1270,7 @@ impl MissingDoc { desc: &'static str) { // If we're building a test harness, then warning about // documentation is probably not really relevant right now. - if cx.session().opts.test { return } + if cx.sess().opts.test { return } // `#[doc(hidden)]` disables missing_doc check. if self.doc_hidden() { return } @@ -1409,14 +1409,14 @@ impl LintPass for Stability { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { let id = match e.node { ast::ExprPath(..) | ast::ExprStruct(..) => { - match cx.type_context().def_map.borrow().find(&e.id) { + match cx.tcx.def_map.borrow().find(&e.id) { Some(&def) => def.def_id(), None => return } } ast::ExprMethodCall(..) => { let method_call = typeck::MethodCall::expr(e.id); - match cx.type_context().method_map.borrow().find(&method_call) { + match cx.tcx.method_map.borrow().find(&method_call) { Some(method) => { match method.origin { typeck::MethodStatic(def_id) => { @@ -1425,7 +1425,7 @@ impl LintPass for Stability { // Otherwise, use the current def_id (which refers // to the method inside impl). ty::trait_method_of_method( - cx.type_context(), def_id).unwrap_or(def_id) + cx.tcx, def_id).unwrap_or(def_id) } typeck::MethodParam(typeck::MethodParam { trait_id: trait_id, @@ -1436,7 +1436,7 @@ impl LintPass for Stability { trait_id: trait_id, method_num: index, .. - }) => ty::trait_method(cx.type_context(), trait_id, index).def_id + }) => ty::trait_method(cx.tcx, trait_id, index).def_id } } None => return @@ -1447,7 +1447,7 @@ impl LintPass for Stability { let stability = if ast_util::is_local(id) { // this crate - let s = cx.type_context().map.with_attrs(id.node, |attrs| { + let s = cx.tcx.map.with_attrs(id.node, |attrs| { attrs.map(|a| attr::find_stability(a.as_slice())) }); match s { @@ -1464,7 +1464,7 @@ impl LintPass for Stability { let mut s = None; // run through all the attributes and take the first // stability one. - csearch::get_item_attrs(&cx.session().cstore, id, |attrs| { + csearch::get_item_attrs(&cx.sess().cstore, id, |attrs| { if s.is_none() { s = attr::find_stability(attrs.as_slice()) } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index a4bcad1e2a97c..c0c76fc4aaaf0 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -167,12 +167,12 @@ impl LintStore { /// Context for lint checking. pub struct Context<'a> { + /// Type context we're checking in (used to access fields like sess). + pub tcx: &'a ty::ctxt, + /// The store of registered lints. lints: LintStore, - /// Context we're checking in (used to access fields like sess). - tcx: &'a 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. @@ -289,15 +289,10 @@ impl<'a> Context<'a> { } /// Get the overall compiler `Session` object. - pub fn session(&'a self) -> &'a Session { + pub fn sess(&'a self) -> &'a Session { &self.tcx.sess } - /// Get the type context, containing information generated by the typechecker. - pub fn type_context(&'a self) -> &'a ty::ctxt { - self.tcx - } - fn lookup_and_emit(&self, lint: &'static Lint, span: Option, msg: &str) { let (level, src) = match self.lints.levels.find(&LintId::of(lint)) { None => return, From 3b846ef25bcabed5c2f59c2f21d3e2269fe870a0 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Fri, 13 Jun 2014 20:42:56 -0700 Subject: [PATCH 19/24] fixup! Rework lint attr parsing and use it in middle::dead --- src/librustc/middle/dead.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index d89bcdc9c751f..786d0a1305c11 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -251,12 +251,11 @@ fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool { return true; } + let dead_code = lint::builtin::DEAD_CODE.name_lower(); for attr in lint::gather_attrs(attrs).move_iter() { match attr { - // Want to use lint::builtin::DEAD_CODE.name_lower() but that - // will allocate every time for something that's actually - // constant. - Ok((ref name, lint::Allow, _)) if name.get() == "dead_code" => return true, + Ok((ref name, lint::Allow, _)) + if name.get() == dead_code.as_slice() => return true, _ => (), } } From c306d208d48c73847d4f353d9c2f7b6e16cdde06 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Tue, 17 Jun 2014 16:43:34 -0700 Subject: [PATCH 20/24] fixup! Make lint::Context conform to rustc API conventions --- src/librustc/lint/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index c0c76fc4aaaf0..9c7da3daa82be 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -167,7 +167,7 @@ impl LintStore { /// Context for lint checking. pub struct Context<'a> { - /// Type context we're checking in (used to access fields like sess). + /// Type context we're checking in. pub tcx: &'a ty::ctxt, /// The store of registered lints. From 6512035c49ade2c3916828a3fbb1676132fa4333 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Tue, 17 Jun 2014 16:55:34 -0700 Subject: [PATCH 21/24] Make the crate and its exported items available in the lint context --- src/librustc/lint/builtin.rs | 12 ++---------- src/librustc/lint/context.rs | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 903cb7f4f2692..ee0973c90bbfe 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1238,9 +1238,6 @@ declare_lint!(MISSING_DOC, Allow, "detects missing documentation for public members") pub struct MissingDoc { - /// Set of nodes exported from this module. - exported_items: Option, - /// Stack of IDs of struct definitions. struct_def_stack: Vec, @@ -1252,7 +1249,6 @@ pub struct MissingDoc { impl MissingDoc { pub fn new() -> MissingDoc { MissingDoc { - exported_items: None, struct_def_stack: vec!(), doc_hidden_stack: vec!(false), } @@ -1278,9 +1274,8 @@ impl MissingDoc { // Only check publicly-visible items, using the result from the privacy pass. // It's an option so the crate root can also use this function (it doesn't // have a NodeId). - let exported = self.exported_items.as_ref().expect("exported_items not set"); match id { - Some(ref id) if !exported.contains(id) => return, + Some(ref id) if !cx.exported_items.contains(id) => return, _ => () } @@ -1327,10 +1322,7 @@ impl LintPass for MissingDoc { assert!(popped == id); } - fn check_crate(&mut self, cx: &Context, exported: &ExportedItems, krate: &ast::Crate) { - // FIXME: clone to avoid lifetime trickiness - self.exported_items = Some(exported.clone()); - + fn check_crate(&mut self, cx: &Context, _: &ExportedItems, krate: &ast::Crate) { self.check_missing_doc_attrs(cx, None, krate.attrs.as_slice(), krate.span, "crate"); } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 9c7da3daa82be..f90b08f740f9b 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -170,6 +170,12 @@ pub struct Context<'a> { /// Type context we're checking in. pub tcx: &'a ty::ctxt, + /// The crate being checked. + pub krate: &'a ast::Crate, + + /// Items exported from the crate being checked. + pub exported_items: &'a ExportedItems, + /// The store of registered lints. lints: LintStore, @@ -275,14 +281,18 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint, } impl<'a> Context<'a> { - fn new(tcx: &'a ty::ctxt) -> Context<'a> { + fn new(tcx: &'a ty::ctxt, + krate: &'a ast::Crate, + exported_items: &'a ExportedItems) -> Context<'a> { // We want to own the lint store, so move it out of the session. let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), LintStore::new()); Context { - lints: lint_store, tcx: tcx, + krate: krate, + exported_items: exported_items, + lints: lint_store, level_stack: vec!(), node_levels: RefCell::new(HashMap::new()), } @@ -619,7 +629,7 @@ impl LintPass for GatherNodeLevels { pub fn check_crate(tcx: &ty::ctxt, exported_items: &ExportedItems, krate: &ast::Crate) { - let mut cx = Context::new(tcx); + let mut cx = Context::new(tcx, krate, exported_items); // Visit the whole crate. cx.with_lint_attrs(krate.attrs.as_slice(), |cx| { From 5d1872e40e5867d3d4cccdfd1f996a38ac68977a Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Tue, 17 Jun 2014 17:06:04 -0700 Subject: [PATCH 22/24] Drop the ExportedItems argument from LintPass::check_crate None of the builtin lints use this, and it's now available through the Context. --- src/librustc/lint/builtin.rs | 3 +-- src/librustc/lint/context.rs | 2 +- src/librustc/lint/mod.rs | 4 +--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index ee0973c90bbfe..af9d0cbcb7f51 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -30,7 +30,6 @@ use middle::def::*; use middle::trans::adt; // for `adt::is_ffi_safe` use middle::typeck::astconv::ast_ty_to_ty; use middle::typeck::infer; -use middle::privacy::ExportedItems; use middle::{typeck, ty, def, pat_util}; use util::ppaux::{ty_to_str}; use util::nodemap::NodeSet; @@ -1322,7 +1321,7 @@ impl LintPass for MissingDoc { assert!(popped == id); } - fn check_crate(&mut self, cx: &Context, _: &ExportedItems, krate: &ast::Crate) { + fn check_crate(&mut self, cx: &Context, krate: &ast::Crate) { self.check_missing_doc_attrs(cx, None, krate.attrs.as_slice(), krate.span, "crate"); } diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index f90b08f740f9b..27b06cde2c2cc 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -641,7 +641,7 @@ pub fn check_crate(tcx: &ty::ctxt, // since the root module isn't visited as an item (because it isn't an // item), warn for it here. - run_lints!(cx, check_crate, exported_items, krate); + run_lints!(cx, check_crate, krate); visit::walk_crate(cx, krate, ()); }); diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 28ba368692c77..5ad3a7405f7a8 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -29,8 +29,6 @@ #![macro_escape] -use middle::privacy::ExportedItems; - use std::hash; use std::ascii::StrAsciiExt; use syntax::codemap::Span; @@ -123,7 +121,7 @@ pub trait LintPass { /// `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, _: &ast::Crate) { } fn check_ident(&mut self, _: &Context, _: Span, _: ast::Ident) { } fn check_mod(&mut self, _: &Context, _: &ast::Mod, _: Span, _: ast::NodeId) { } fn check_view_item(&mut self, _: &Context, _: &ast::ViewItem) { } From d98af225132223a8ca6d6eb10bf568e73cb33f4a Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Tue, 17 Jun 2014 17:06:44 -0700 Subject: [PATCH 23/24] Reorder arguments to lint::check_crate for consistency --- src/librustc/driver/driver.rs | 2 +- src/librustc/lint/context.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 4b66a3b2cd2e4..f6a76154437f0 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -377,7 +377,7 @@ pub fn phase_3_run_analysis_passes(sess: Session, }); time(time_passes, "lint checking", (), |_| - lint::check_crate(&ty_cx, &exported_items, krate)); + lint::check_crate(&ty_cx, krate, &exported_items)); CrateAnalysis { exp_map2: exp_map2, diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 27b06cde2c2cc..3683ed4a0c9e7 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -627,8 +627,8 @@ impl LintPass for GatherNodeLevels { /// /// Consumes the `lint_store` field of the `Session`. pub fn check_crate(tcx: &ty::ctxt, - exported_items: &ExportedItems, - krate: &ast::Crate) { + krate: &ast::Crate, + exported_items: &ExportedItems) { let mut cx = Context::new(tcx, krate, exported_items); // Visit the whole crate. From b0597c60c8ad000e3c7c58a685c7b20cd06902c3 Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Tue, 17 Jun 2014 17:41:50 -0700 Subject: [PATCH 24/24] Reindent function call continuations, and other style fixes --- src/librustc/driver/mod.rs | 2 +- src/librustc/lint/builtin.rs | 158 +++++++++++++++--------------- src/librustc/lint/context.rs | 39 ++++---- src/librustc/middle/trans/base.rs | 8 +- 4 files changed, 104 insertions(+), 103 deletions(-) diff --git a/src/librustc/driver/mod.rs b/src/librustc/driver/mod.rs index 95bcdd5ec0f3c..7793edb74f7ef 100644 --- a/src/librustc/driver/mod.rs +++ b/src/librustc/driver/mod.rs @@ -175,7 +175,7 @@ Available lint options: for lint in lints.move_iter() { let name = lint.name_lower().replace("_", "-"); println!(" {} {:7.7s} {}", - padded(name.as_slice()), lint.default_level.as_str(), lint.desc); + padded(name.as_slice()), lint.default_level.as_str(), lint.desc); } println!("\n"); }; diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index af9d0cbcb7f51..604edf280f14c 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -54,7 +54,7 @@ use syntax::parse::token; use syntax::{ast, ast_util, visit}; declare_lint!(WHILE_TRUE, Warn, - "suggest using `loop { }` instead of `while true { }`") + "suggest using `loop { }` instead of `while true { }`") pub struct WhileTrue; @@ -86,7 +86,7 @@ impl LintPass for WhileTrue { } declare_lint!(UNNECESSARY_TYPECAST, Allow, - "detects unnecessary type casts, that can be removed") + "detects unnecessary type casts, that can be removed") pub struct UnusedCasts; @@ -99,7 +99,7 @@ impl LintPass for UnusedCasts { match e.node { ast::ExprCast(expr, 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(UNNECESSARY_TYPECAST, ty.span, "unnecessary type cast"); } } @@ -109,13 +109,13 @@ impl LintPass for UnusedCasts { } declare_lint!(UNSIGNED_NEGATE, Warn, - "using an unary minus operator on unsigned type") + "using an unary minus operator on unsigned type") declare_lint!(TYPE_LIMITS, Warn, - "comparisons made useless by limits of the types involved") + "comparisons made useless by limits of the types involved") declare_lint!(TYPE_OVERFLOW, Warn, - "literal out of range for its type") + "literal out of range for its type") pub struct TypeLimits { /// Id of the last visited negated expression @@ -315,7 +315,7 @@ impl LintPass for TypeLimits { } declare_lint!(CTYPES, Warn, - "proper use of libc types in foreign modules") + "proper use of libc types in foreign modules") pub struct CTypes; @@ -331,19 +331,19 @@ impl LintPass for CTypes { match cx.tcx.def_map.borrow().get_copy(&id) { def::DefPrimTy(ast::TyInt(ast::TyI)) => { cx.span_lint(CTYPES, ty.span, - "found rust type `int` in foreign module, while \ - libc::c_int or libc::c_long should be used"); + "found rust type `int` in foreign module, while \ + libc::c_int or libc::c_long should be used"); } def::DefPrimTy(ast::TyUint(ast::TyU)) => { cx.span_lint(CTYPES, ty.span, - "found rust type `uint` in foreign module, while \ - libc::c_uint or libc::c_ulong should be used"); + "found rust type `uint` in foreign module, while \ + libc::c_uint or libc::c_ulong should be used"); } def::DefTy(def_id) => { if !adt::is_ffi_safe(cx.tcx, def_id) { cx.span_lint(CTYPES, ty.span, - "found enum type without foreign-function-safe \ - representation annotation in foreign module"); + "found enum type without foreign-function-safe \ + representation annotation in foreign module"); // hmm... this message could be more helpful } } @@ -377,13 +377,13 @@ impl LintPass for CTypes { } declare_lint!(MANAGED_HEAP_MEMORY, Allow, - "use of managed (@ type) heap memory") + "use of managed (@ type) heap memory") declare_lint!(OWNED_HEAP_MEMORY, Allow, - "use of owned (Box type) heap memory") + "use of owned (Box type) heap memory") declare_lint!(HEAP_MEMORY, Allow, - "use of any (Box type or @ type) heap memory") + "use of any (Box type or @ type) heap memory") pub struct HeapMemory; @@ -438,9 +438,9 @@ impl LintPass for HeapMemory { ast::ItemFn(..) | ast::ItemTy(..) | ast::ItemEnum(..) | - ast::ItemStruct(..) - => self.check_heap_type(cx, it.span, - ty::node_id_to_type(cx.tcx, it.id)), + ast::ItemStruct(..) => + self.check_heap_type(cx, it.span, + ty::node_id_to_type(cx.tcx, it.id)), _ => () } @@ -449,7 +449,7 @@ impl LintPass for HeapMemory { ast::ItemStruct(struct_def, _) => { for struct_field in struct_def.fields.iter() { self.check_heap_type(cx, struct_field.span, - ty::node_id_to_type(cx.tcx, struct_field.node.id)); + ty::node_id_to_type(cx.tcx, struct_field.node.id)); } } _ => () @@ -463,7 +463,7 @@ impl LintPass for HeapMemory { } declare_lint!(RAW_POINTER_DERIVING, Warn, - "uses of #[deriving] with raw pointers are rarely correct") + "uses of #[deriving] with raw pointers are rarely correct") struct RawPtrDerivingVisitor<'a> { cx: &'a Context<'a> @@ -531,7 +531,7 @@ impl LintPass for RawPointerDeriving { } declare_lint!(UNUSED_ATTRIBUTE, Warn, - "detects attributes that were not used by the compiler") + "detects attributes that were not used by the compiler") pub struct UnusedAttribute; @@ -600,8 +600,8 @@ impl LintPass for UnusedAttribute { cx.span_lint(UNUSED_ATTRIBUTE, attr.span, "unused attribute"); if CRATE_ATTRS.contains(&attr.name().get()) { let msg = match attr.node.style { - ast::AttrOuter => "crate-level attribute should be an inner \ - attribute: add an exclamation mark: #![foo]", + ast::AttrOuter => "crate-level attribute should be an inner \ + attribute: add an exclamation mark: #![foo]", ast::AttrInner => "crate-level attribute should be in the \ root module", }; @@ -612,7 +612,7 @@ impl LintPass for UnusedAttribute { } declare_lint!(PATH_STATEMENT, Warn, - "path statements with no effect") + "path statements with no effect") pub struct PathStatement; @@ -636,10 +636,10 @@ impl LintPass for PathStatement { } declare_lint!(UNUSED_MUST_USE, Warn, - "unused result of a type flagged as #[must_use]") + "unused result of a type flagged as #[must_use]") declare_lint!(UNUSED_RESULT, Allow, - "unused result of an expression in a statement") + "unused result of an expression in a statement") pub struct UnusedResult; @@ -699,7 +699,7 @@ impl LintPass for UnusedResult { } declare_lint!(DEPRECATED_OWNED_VECTOR, Allow, - "use of a `~[T]` vector") + "use of a `~[T]` vector") pub struct DeprecatedOwnedVector; @@ -714,7 +714,7 @@ impl LintPass for DeprecatedOwnedVector { ty::ty_uniq(t) => match ty::get(t).sty { ty::ty_vec(_, None) => { 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`") } _ => {} }, @@ -724,7 +724,7 @@ impl LintPass for DeprecatedOwnedVector { } declare_lint!(NON_CAMEL_CASE_TYPES, Warn, - "types, variants and traits should have camel case names") + "types, variants and traits should have camel case names") pub struct NonCamelCaseTypes; @@ -757,7 +757,7 @@ impl LintPass for NonCamelCaseTypes { if !is_camel_case(ident) { cx.span_lint(NON_CAMEL_CASE_TYPES, span, 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()); } } @@ -809,7 +809,7 @@ fn method_context(cx: &Context, m: &ast::Method) -> MethodContext { } declare_lint!(NON_SNAKE_CASE_FUNCTIONS, Warn, - "methods and functions should have snake case names") + "methods and functions should have snake case names") pub struct NonSnakeCaseFunctions; @@ -853,7 +853,7 @@ impl NonSnakeCaseFunctions { if !is_snake_case(ident) { cx.span_lint(NON_SNAKE_CASE_FUNCTIONS, span, 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()); } } } @@ -886,7 +886,7 @@ impl LintPass for NonSnakeCaseFunctions { } declare_lint!(NON_UPPERCASE_STATICS, Allow, - "static constants should have uppercase identifiers") + "static constants should have uppercase identifiers") pub struct NonUppercaseStatics; @@ -906,8 +906,8 @@ impl LintPass for NonUppercaseStatics { if s.get().chars().any(|c| c.is_lowercase()) { cx.span_lint(NON_UPPERCASE_STATICS, it.span, format!("static constant `{}` should have an uppercase name \ - such as `{}`", s.get(), - s.get().chars().map(|c| c.to_uppercase()) + such as `{}`", + s.get(), s.get().chars().map(|c| c.to_uppercase()) .collect::().as_slice()).as_slice()); } } @@ -917,7 +917,7 @@ impl LintPass for NonUppercaseStatics { } declare_lint!(NON_UPPERCASE_PATTERN_STATICS, Warn, - "static constants in match patterns should be all caps") + "static constants in match patterns should be all caps") pub struct NonUppercasePatternStatics; @@ -936,9 +936,9 @@ impl LintPass for NonUppercasePatternStatics { if s.get().chars().any(|c| c.is_lowercase()) { cx.span_lint(NON_UPPERCASE_PATTERN_STATICS, path.span, format!("static constant in pattern `{}` should have an uppercase \ - name such as `{}`", s.get(), - s.get().chars().map(|c| c.to_uppercase()) - .collect::().as_slice()).as_slice()); + name such as `{}`", + s.get(), s.get().chars().map(|c| c.to_uppercase()) + .collect::().as_slice()).as_slice()); } } _ => {} @@ -947,7 +947,7 @@ impl LintPass for NonUppercasePatternStatics { } declare_lint!(UPPERCASE_VARIABLES, Warn, - "variable and structure field names should start with a lowercase character") + "variable and structure field names should start with a lowercase character") pub struct UppercaseVariables; @@ -967,7 +967,8 @@ impl LintPass for UppercaseVariables { let s = token::get_ident(ident); if s.get().len() > 0 && s.get().char_at(0).is_uppercase() { cx.span_lint(UPPERCASE_VARIABLES, path.span, - "variable names should start with a lowercase character"); + "variable names should start with \ + a lowercase character"); } } _ => {} @@ -985,7 +986,8 @@ impl LintPass for UppercaseVariables { let s = token::get_ident(ident); if s.get().char_at(0).is_uppercase() { cx.span_lint(UPPERCASE_VARIABLES, sf.span, - "structure field names should start with a lowercase character"); + "structure field names should start with \ + a lowercase character"); } } _ => {} @@ -995,7 +997,7 @@ impl LintPass for UppercaseVariables { } declare_lint!(UNNECESSARY_PARENS, Warn, - "`if`, `match`, `while` and `return` do not need parentheses") + "`if`, `match`, `while` and `return` do not need parentheses") pub struct UnnecessaryParens; @@ -1045,7 +1047,7 @@ impl LintPass for UnnecessaryParens { } declare_lint!(UNUSED_UNSAFE, Warn, - "unnecessary use of an `unsafe` block") + "unnecessary use of an `unsafe` block") pub struct UnusedUnsafe; @@ -1069,7 +1071,7 @@ impl LintPass for UnusedUnsafe { } declare_lint!(UNSAFE_BLOCK, Allow, - "usage of an `unsafe` block") + "usage of an `unsafe` block") pub struct UnsafeBlock; @@ -1090,7 +1092,7 @@ impl LintPass for UnsafeBlock { } declare_lint!(UNUSED_MUT, Warn, - "detect mut variables which don't need to be mutable") + "detect mut variables which don't need to be mutable") pub struct UnusedMut; @@ -1105,8 +1107,8 @@ impl UnusedMut { ast::BindByValue(ast::MutMutable) => { if path.segments.len() != 1 { cx.sess().span_bug(p.span, - "mutable binding that doesn't consist \ - of exactly one segment"); + "mutable binding that doesn't consist \ + of exactly one segment"); } let ident = path.segments.get(0).identifier; if !token::get_ident(ident).get().starts_with("_") { @@ -1124,7 +1126,7 @@ impl UnusedMut { for (_, v) in mutables.iter() { if !v.iter().any(|e| used_mutables.contains(e)) { 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"); } } } @@ -1175,7 +1177,7 @@ enum Allocation { } declare_lint!(UNNECESSARY_ALLOCATION, Warn, - "detects unnecessary allocations that can be eliminated") + "detects unnecessary allocations that can be eliminated") pub struct UnnecessaryAllocation; @@ -1210,17 +1212,17 @@ impl LintPass for UnnecessaryAllocation { match (allocation, autoref) { (VectorAllocation, Some(ty::AutoBorrowVec(..))) => { cx.span_lint(UNNECESSARY_ALLOCATION, e.span, - "unnecessary allocation, the sigil can be removed"); + "unnecessary allocation, the sigil can be removed"); } (BoxAllocation, Some(ty::AutoPtr(_, ast::MutImmutable))) => { cx.span_lint(UNNECESSARY_ALLOCATION, e.span, - "unnecessary allocation, use & instead"); + "unnecessary allocation, use & instead"); } (BoxAllocation, Some(ty::AutoPtr(_, ast::MutMutable))) => { cx.span_lint(UNNECESSARY_ALLOCATION, e.span, - "unnecessary allocation, use &mut instead"); + "unnecessary allocation, use &mut instead"); } _ => () } @@ -1234,7 +1236,7 @@ impl LintPass for UnnecessaryAllocation { } declare_lint!(MISSING_DOC, Allow, - "detects missing documentation for public members") + "detects missing documentation for public members") pub struct MissingDoc { /// Stack of IDs of struct definitions. @@ -1323,7 +1325,7 @@ impl LintPass for MissingDoc { fn check_crate(&mut self, cx: &Context, krate: &ast::Crate) { self.check_missing_doc_attrs(cx, None, krate.attrs.as_slice(), - krate.span, "crate"); + krate.span, "crate"); } fn check_item(&mut self, cx: &Context, it: &ast::Item) { @@ -1336,7 +1338,7 @@ impl LintPass for MissingDoc { _ => return }; self.check_missing_doc_attrs(cx, Some(it.id), it.attrs.as_slice(), - it.span, desc); + it.span, desc); } fn check_fn(&mut self, cx: &Context, @@ -1350,7 +1352,7 @@ impl LintPass for MissingDoc { // Otherwise, doc according to privacy. This will also check // doc for default methods defined on traits. self.check_missing_doc_attrs(cx, Some(m.id), m.attrs.as_slice(), - m.span, "a method"); + m.span, "a method"); } _ => {} } @@ -1358,7 +1360,7 @@ impl LintPass for MissingDoc { fn check_ty_method(&mut self, cx: &Context, tm: &ast::TypeMethod) { self.check_missing_doc_attrs(cx, Some(tm.id), tm.attrs.as_slice(), - tm.span, "a type method"); + tm.span, "a type method"); } fn check_struct_field(&mut self, cx: &Context, sf: &ast::StructField) { @@ -1367,7 +1369,8 @@ impl LintPass for MissingDoc { let cur_struct_def = *self.struct_def_stack.last() .expect("empty struct_def_stack"); self.check_missing_doc_attrs(cx, Some(cur_struct_def), - sf.node.attrs.as_slice(), sf.span, "a struct field") + sf.node.attrs.as_slice(), sf.span, + "a struct field") } _ => {} } @@ -1375,18 +1378,18 @@ impl LintPass for MissingDoc { fn check_variant(&mut self, cx: &Context, v: &ast::Variant, _: &ast::Generics) { self.check_missing_doc_attrs(cx, Some(v.node.id), v.node.attrs.as_slice(), - v.span, "a variant"); + v.span, "a variant"); } } declare_lint!(DEPRECATED, Warn, - "detects use of #[deprecated] items") + "detects use of #[deprecated] items") declare_lint!(EXPERIMENTAL, Warn, - "detects use of #[experimental] items") + "detects use of #[experimental] items") declare_lint!(UNSTABLE, Allow, - "detects use of #[unstable] items (incl. items with no stability attribute)") + "detects use of #[unstable] items (incl. items with no stability attribute)") /// Checks for use of items with `#[deprecated]`, `#[experimental]` and /// `#[unstable]` attributes, or no stability attribute. @@ -1415,8 +1418,7 @@ impl LintPass for Stability { // of the method inside trait definition. // Otherwise, use the current def_id (which refers // to the method inside impl). - ty::trait_method_of_method( - cx.tcx, def_id).unwrap_or(def_id) + ty::trait_method_of_method(cx.tcx, def_id).unwrap_or(def_id) } typeck::MethodParam(typeck::MethodParam { trait_id: trait_id, @@ -1487,40 +1489,40 @@ impl LintPass for Stability { } declare_lint!(pub UNUSED_IMPORTS, Warn, - "imports that are never used") + "imports that are never used") declare_lint!(pub UNNECESSARY_QUALIFICATION, Allow, - "detects unnecessarily qualified names") + "detects unnecessarily qualified names") declare_lint!(pub UNRECOGNIZED_LINT, Warn, - "unrecognized lint attribute") + "unrecognized lint attribute") declare_lint!(pub UNUSED_VARIABLE, Warn, - "detect variables which are not used in any way") + "detect variables which are not used in any way") declare_lint!(pub DEAD_ASSIGNMENT, Warn, - "detect assignments that will never be read") + "detect assignments that will never be read") declare_lint!(pub DEAD_CODE, Warn, - "detect piece of code that will never be used") + "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") + "detect use of private types in exported type signatures") declare_lint!(pub UNREACHABLE_CODE, Warn, - "detects unreachable code") + "detects unreachable code") declare_lint!(pub WARNINGS, Warn, - "mass-change the level for lints which produce warnings") + "mass-change the level for lints which produce warnings") declare_lint!(pub UNKNOWN_FEATURES, Deny, - "unknown features found in crate-level #[feature] directives") + "unknown features found in crate-level #[feature] directives") declare_lint!(pub UNKNOWN_CRATE_TYPE, Deny, - "unknown crate type found in #[crate_type] directive") + "unknown crate type found in #[crate_type] directive") declare_lint!(pub VARIANT_SIZE_DIFFERENCE, Allow, - "detects enums with widely varying variant sizes") + "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. diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 3683ed4a0c9e7..3d7f32296e35a 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -137,17 +137,17 @@ impl LintStore { )) add_builtin!(sess, HardwiredLints, - WhileTrue, UnusedCasts, CTypes, HeapMemory, - UnusedAttribute, PathStatement, UnusedResult, - DeprecatedOwnedVector, NonCamelCaseTypes, - NonSnakeCaseFunctions, NonUppercaseStatics, - NonUppercasePatternStatics, UppercaseVariables, - UnnecessaryParens, UnusedUnsafe, UnsafeBlock, - UnusedMut, UnnecessaryAllocation, Stability, + WhileTrue, UnusedCasts, CTypes, HeapMemory, + UnusedAttribute, PathStatement, UnusedResult, + DeprecatedOwnedVector, NonCamelCaseTypes, + NonSnakeCaseFunctions, NonUppercaseStatics, + NonUppercasePatternStatics, UppercaseVariables, + UnnecessaryParens, UnusedUnsafe, UnsafeBlock, + UnusedMut, UnnecessaryAllocation, Stability, ) add_builtin_with_new!(sess, - TypeLimits, RawPointerDeriving, MissingDoc, + TypeLimits, RawPointerDeriving, MissingDoc, ) // We have one lint pass defined in this module. @@ -159,7 +159,7 @@ impl LintStore { match self.by_name.find_equiv(&lint_name.as_slice()) { Some(&lint_id) => self.set_level(lint_id, (level, CommandLine)), None => sess.err(format!("unknown {} flag: {}", - level.as_str(), lint_name).as_slice()), + level.as_str(), lint_name).as_slice()), } } } @@ -249,14 +249,14 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint, let msg = match source { Default => { format!("{}, \\#[{}({})] on by default", msg, - level.as_str(), name) + level.as_str(), name) }, CommandLine => { format!("{} [-{} {}]", msg, - match level { - Warn => 'W', Deny => 'D', Forbid => 'F', - Allow => fail!() - }, name.replace("_", "-")) + match level { + Warn => 'W', Deny => 'D', Forbid => 'F', + Allow => fail!() + }, name.replace("_", "-")) }, Node(src) => { note = Some(src); @@ -286,7 +286,7 @@ impl<'a> Context<'a> { exported_items: &'a ExportedItems) -> Context<'a> { // We want to own the lint store, so move it out of the session. let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), - LintStore::new()); + LintStore::new()); Context { tcx: tcx, @@ -363,8 +363,8 @@ impl<'a> Context<'a> { if now == Forbid && level != Forbid { let lint_name = lint_id.as_str(); self.tcx.sess.span_err(span, - format!("{}({}) overruled by outer forbid({})", - level.as_str(), lint_name, lint_name).as_slice()); + format!("{}({}) overruled by outer forbid({})", + level.as_str(), lint_name, lint_name).as_slice()); } else if now != level { let src = self.lints.get_level_source(lint_id).val1(); self.level_stack.push((lint_id, (now, src))); @@ -651,9 +651,8 @@ pub fn check_crate(tcx: &ty::ctxt, for (id, v) in tcx.sess.lints.borrow().iter() { for &(lint, span, ref msg) in v.iter() { tcx.sess.span_bug(span, - format!("unprocessed lint {} at {}: {}", - lint.as_str(), 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()) } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 5bb9fd5a1820e..b24e75f4be83e 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1601,10 +1601,10 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing // pass for the latter already ran. lint::raw_emit_lint(&ccx.tcx().sess, lint::builtin::VARIANT_SIZE_DIFFERENCE, - lvlsrc, Some(sp), - format!("enum variant is more than three times larger \ - ({} bytes) than the next largest (ignoring padding)", - largest).as_slice()); + lvlsrc, Some(sp), + 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, "this variant is the largest");