1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Lints in the Rust compiler.
13 //! This contains lints which can feasibly be implemented as their own
14 //! AST visitor. Also see `rustc::lint::builtin`, which contains the
15 //! definitions of lints that are emitted directly inside the main
18 //! To add a new lint to rustc, declare it here using `declare_lint!()`.
19 //! Then add code to emit the new lint in the appropriate circumstances.
20 //! You can do that in an existing `LintPass` if it makes sense, or in a
21 //! new `LintPass`, or using `Session::add_lint` elsewhere in the
22 //! compiler. Only do the latter if the check can't be written cleanly as a
23 //! `LintPass` (also, note that such lints will need to be defined in
24 //! `rustc::lint::builtin`, not here).
26 //! If you define a new `LintPass`, you will also need to add it to the
27 //! `add_builtin!` or `add_builtin_with_new!` invocation in `lib.rs`.
28 //! Use the former for unit-like structs and the latter for structs with
31 use metadata::{csearch, decoder};
33 use middle::subst::Substs;
34 use middle::ty::{self, Ty};
35 use middle::{def, pat_util, stability};
36 use middle::const_eval::{eval_const_expr_partial, const_int, const_uint};
38 use util::ppaux::ty_to_string;
39 use util::nodemap::{FnvHashMap, NodeSet};
40 use lint::{Level, Context, LintPass, LintArray, Lint};
42 use std::collections::BitSet;
43 use std::collections::hash_map::Entry::{Occupied, Vacant};
44 use std::{cmp, slice};
45 use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
47 use syntax::{abi, ast, ast_map};
48 use syntax::ast_util::{self, is_shift_binop, local_def};
49 use syntax::attr::{self, AttrMetaMethods};
50 use syntax::codemap::{self, Span};
51 use syntax::feature_gate::{KNOWN_ATTRIBUTES, AttributeType};
52 use syntax::parse::token;
53 use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
55 use syntax::visit::{self, Visitor};
57 // hardwired lints from librustc
58 pub use lint::builtin::*;
63 "suggest using `loop { }` instead of `while true { }`"
69 impl LintPass for WhileTrue {
70 fn get_lints(&self) -> LintArray {
71 lint_array!(WHILE_TRUE)
74 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
75 if let ast::ExprWhile(ref cond, _, _) = e.node {
76 if let ast::ExprLit(ref lit) = cond.node {
77 if let ast::LitBool(true) = lit.node {
78 cx.span_lint(WHILE_TRUE, e.span,
79 "denote infinite loops with loop { ... }");
89 "using an unary minus operator on unsigned type"
95 "comparisons made useless by limits of the types involved"
101 "literal out of range for its type"
107 "shift exceeds the type's number of bits"
111 pub struct TypeLimits {
112 /// Id of the last visited negated expression
113 negated_expr_id: ast::NodeId,
117 pub fn new() -> TypeLimits {
124 impl LintPass for TypeLimits {
125 fn get_lints(&self) -> LintArray {
126 lint_array!(UNSIGNED_NEGATION, UNUSED_COMPARISONS, OVERFLOWING_LITERALS,
130 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
132 ast::ExprUnary(ast::UnNeg, ref expr) => {
134 ast::ExprLit(ref lit) => {
136 ast::LitInt(_, ast::UnsignedIntLit(_)) => {
137 cx.span_lint(UNSIGNED_NEGATION, e.span,
138 "negation of unsigned int literal may \
145 let t = ty::expr_ty(cx.tcx, &**expr);
148 cx.span_lint(UNSIGNED_NEGATION, e.span,
149 "negation of unsigned int variable may \
156 // propagate negation, if the negation itself isn't negated
157 if self.negated_expr_id != e.id {
158 self.negated_expr_id = expr.id;
161 ast::ExprParen(ref expr) if self.negated_expr_id == e.id => {
162 self.negated_expr_id = expr.id;
164 ast::ExprBinary(binop, ref l, ref r) => {
165 if is_comparison(binop) && !check_limits(cx.tcx, binop, &**l, &**r) {
166 cx.span_lint(UNUSED_COMPARISONS, e.span,
167 "comparison is useless due to type limits");
170 if is_shift_binop(binop.node) {
171 let opt_ty_bits = match ty::expr_ty(cx.tcx, &**l).sty {
172 ty::ty_int(t) => Some(int_ty_bits(t, cx.sess().target.int_type)),
173 ty::ty_uint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)),
177 if let Some(bits) = opt_ty_bits {
178 let exceeding = if let ast::ExprLit(ref lit) = r.node {
179 if let ast::LitInt(shift, _) = lit.node { shift >= bits }
182 match eval_const_expr_partial(cx.tcx, &**r, Some(cx.tcx.types.usize)) {
183 Ok(const_int(shift)) => { shift as u64 >= bits },
184 Ok(const_uint(shift)) => { shift >= bits },
189 cx.span_lint(EXCEEDING_BITSHIFTS, e.span,
190 "bitshift exceeds the type's number of bits");
195 ast::ExprLit(ref lit) => {
196 match ty::expr_ty(cx.tcx, e).sty {
199 ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
200 ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => {
201 let int_type = if let ast::TyIs = t {
202 cx.sess().target.int_type
206 let (min, max) = int_ty_range(int_type);
207 let negative = self.negated_expr_id == e.id;
209 if (negative && v > (min.abs() as u64)) ||
210 (!negative && v > (max.abs() as u64)) {
211 cx.span_lint(OVERFLOWING_LITERALS, e.span,
212 &*format!("literal out of range for {:?}", t));
220 let uint_type = if let ast::TyUs = t {
221 cx.sess().target.uint_type
225 let (min, max) = uint_ty_range(uint_type);
226 let lit_val: u64 = match lit.node {
227 ast::LitByte(_v) => return, // _v is u8, within range by definition
228 ast::LitInt(v, _) => v,
231 if lit_val < min || lit_val > max {
232 cx.span_lint(OVERFLOWING_LITERALS, e.span,
233 &*format!("literal out of range for {:?}", t));
237 let (min, max) = float_ty_range(t);
238 let lit_val: f64 = match lit.node {
239 ast::LitFloat(ref v, _) |
240 ast::LitFloatUnsuffixed(ref v) => {
248 if lit_val < min || lit_val > max {
249 cx.span_lint(OVERFLOWING_LITERALS, e.span,
250 &*format!("literal out of range for {:?}", t));
259 fn is_valid<T:cmp::PartialOrd>(binop: ast::BinOp, v: T,
260 min: T, max: T) -> bool {
262 ast::BiLt => v > min && v <= max,
263 ast::BiLe => v >= min && v < max,
264 ast::BiGt => v >= min && v < max,
265 ast::BiGe => v > min && v <= max,
266 ast::BiEq | ast::BiNe => v >= min && v <= max,
271 fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
272 codemap::respan(binop.span, match binop.node {
273 ast::BiLt => ast::BiGt,
274 ast::BiLe => ast::BiGe,
275 ast::BiGt => ast::BiLt,
276 ast::BiGe => ast::BiLe,
281 // for isize & usize, be conservative with the warnings, so that the
282 // warnings are consistent between 32- and 64-bit platforms
283 fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
285 ast::TyIs => (i64::MIN, i64::MAX),
286 ast::TyI8 => (i8::MIN as i64, i8::MAX as i64),
287 ast::TyI16 => (i16::MIN as i64, i16::MAX as i64),
288 ast::TyI32 => (i32::MIN as i64, i32::MAX as i64),
289 ast::TyI64 => (i64::MIN, i64::MAX)
293 fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
295 ast::TyUs => (u64::MIN, u64::MAX),
296 ast::TyU8 => (u8::MIN as u64, u8::MAX as u64),
297 ast::TyU16 => (u16::MIN as u64, u16::MAX as u64),
298 ast::TyU32 => (u32::MIN as u64, u32::MAX as u64),
299 ast::TyU64 => (u64::MIN, u64::MAX)
303 fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
305 ast::TyF32 => (f32::MIN as f64, f32::MAX as f64),
306 ast::TyF64 => (f64::MIN, f64::MAX)
310 fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 {
312 ast::TyIs => int_ty_bits(target_int_ty, target_int_ty),
313 ast::TyI8 => i8::BITS as u64,
314 ast::TyI16 => i16::BITS as u64,
315 ast::TyI32 => i32::BITS as u64,
316 ast::TyI64 => i64::BITS as u64
320 fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 {
322 ast::TyUs => uint_ty_bits(target_uint_ty, target_uint_ty),
323 ast::TyU8 => u8::BITS as u64,
324 ast::TyU16 => u16::BITS as u64,
325 ast::TyU32 => u32::BITS as u64,
326 ast::TyU64 => u64::BITS as u64
330 fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp,
331 l: &ast::Expr, r: &ast::Expr) -> bool {
332 let (lit, expr, swap) = match (&l.node, &r.node) {
333 (&ast::ExprLit(_), _) => (l, r, true),
334 (_, &ast::ExprLit(_)) => (r, l, false),
337 // Normalize the binop so that the literal is always on the RHS in
339 let norm_binop = if swap {
344 match ty::expr_ty(tcx, expr).sty {
345 ty::ty_int(int_ty) => {
346 let (min, max) = int_ty_range(int_ty);
347 let lit_val: i64 = match lit.node {
348 ast::ExprLit(ref li) => match li.node {
349 ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
350 ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => v as i64,
351 ast::LitInt(v, ast::SignedIntLit(_, ast::Minus)) |
352 ast::LitInt(v, ast::UnsuffixedIntLit(ast::Minus)) => -(v as i64),
357 is_valid(norm_binop, lit_val, min, max)
359 ty::ty_uint(uint_ty) => {
360 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
361 let lit_val: u64 = match lit.node {
362 ast::ExprLit(ref li) => match li.node {
363 ast::LitInt(v, _) => v,
368 is_valid(norm_binop, lit_val, min, max)
374 fn is_comparison(binop: ast::BinOp) -> bool {
376 ast::BiEq | ast::BiLt | ast::BiLe |
377 ast::BiNe | ast::BiGe | ast::BiGt => true,
387 "proper use of libc types in foreign modules"
390 struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
391 cx: &'a Context<'a, 'tcx>
394 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
395 fn check_def(&mut self, sp: Span, id: ast::NodeId) {
396 match self.cx.tcx.def_map.borrow().get(&id).unwrap().full_def() {
397 def::DefPrimTy(ast::TyInt(ast::TyIs)) => {
398 self.cx.span_lint(IMPROPER_CTYPES, sp,
399 "found rust type `isize` in foreign module, while \
400 libc::c_int or libc::c_long should be used");
402 def::DefPrimTy(ast::TyUint(ast::TyUs)) => {
403 self.cx.span_lint(IMPROPER_CTYPES, sp,
404 "found rust type `usize` in foreign module, while \
405 libc::c_uint or libc::c_ulong should be used");
408 let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().get(&id) {
410 None => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
413 if !ty::is_ffi_safe(self.cx.tcx, tty) {
414 self.cx.span_lint(IMPROPER_CTYPES, sp,
415 "found type without foreign-function-safe \
416 representation annotation in foreign module, consider \
417 adding a #[repr(...)] attribute to the type");
425 impl<'a, 'tcx, 'v> Visitor<'v> for ImproperCTypesVisitor<'a, 'tcx> {
426 fn visit_ty(&mut self, ty: &ast::Ty) {
427 if let ast::TyPath(..) = ty.node {
428 self.check_def(ty.span, ty.id);
430 visit::walk_ty(self, ty);
435 pub struct ImproperCTypes;
437 impl LintPass for ImproperCTypes {
438 fn get_lints(&self) -> LintArray {
439 lint_array!(IMPROPER_CTYPES)
442 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
443 fn check_ty(cx: &Context, ty: &ast::Ty) {
444 let mut vis = ImproperCTypesVisitor { cx: cx };
448 fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) {
449 for input in &decl.inputs {
450 check_ty(cx, &*input.ty);
452 if let ast::Return(ref ret_ty) = decl.output {
453 check_ty(cx, &**ret_ty);
458 ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => {
459 for ni in &nmod.items {
461 ast::ForeignItemFn(ref decl, _) => check_foreign_fn(cx, &**decl),
462 ast::ForeignItemStatic(ref t, _) => check_ty(cx, &**t)
474 "use of owned (Box type) heap memory"
478 pub struct BoxPointers;
481 fn check_heap_type<'a, 'tcx>(&self, cx: &Context<'a, 'tcx>,
482 span: Span, ty: Ty<'tcx>) {
483 let mut n_uniq: usize = 0;
484 ty::fold_ty(cx.tcx, ty, |t| {
495 let s = ty_to_string(cx.tcx, ty);
496 let m = format!("type uses owned (Box type) pointers: {}", s);
497 cx.span_lint(BOX_POINTERS, span, &m[..]);
502 impl LintPass for BoxPointers {
503 fn get_lints(&self) -> LintArray {
504 lint_array!(BOX_POINTERS)
507 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
512 ast::ItemStruct(..) =>
513 self.check_heap_type(cx, it.span,
514 ty::node_id_to_type(cx.tcx, it.id)),
518 // If it's a struct, we also have to check the fields' types
520 ast::ItemStruct(ref struct_def, _) => {
521 for struct_field in &struct_def.fields {
522 self.check_heap_type(cx, struct_field.span,
523 ty::node_id_to_type(cx.tcx, struct_field.node.id));
530 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
531 let ty = ty::expr_ty(cx.tcx, e);
532 self.check_heap_type(cx, e.span, ty);
539 "uses of #[derive] with raw pointers are rarely correct"
542 struct RawPtrDeriveVisitor<'a, 'tcx: 'a> {
543 cx: &'a Context<'a, 'tcx>
546 impl<'a, 'tcx, 'v> Visitor<'v> for RawPtrDeriveVisitor<'a, 'tcx> {
547 fn visit_ty(&mut self, ty: &ast::Ty) {
548 const MSG: &'static str = "use of `#[derive]` with a raw pointer";
549 if let ast::TyPtr(..) = ty.node {
550 self.cx.span_lint(RAW_POINTER_DERIVE, ty.span, MSG);
552 visit::walk_ty(self, ty);
554 // explicit override to a no-op to reduce code bloat
555 fn visit_expr(&mut self, _: &ast::Expr) {}
556 fn visit_block(&mut self, _: &ast::Block) {}
559 pub struct RawPointerDerive {
560 checked_raw_pointers: NodeSet,
563 impl RawPointerDerive {
564 pub fn new() -> RawPointerDerive {
566 checked_raw_pointers: NodeSet(),
571 impl LintPass for RawPointerDerive {
572 fn get_lints(&self) -> LintArray {
573 lint_array!(RAW_POINTER_DERIVE)
576 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
577 if !attr::contains_name(&item.attrs, "automatically_derived") {
580 let did = match item.node {
581 ast::ItemImpl(_, _, _, ref t_ref_opt, _, _) => {
582 // Deriving the Copy trait does not cause a warning
583 if let &Some(ref trait_ref) = t_ref_opt {
584 let def_id = ty::trait_ref_to_def_id(cx.tcx, trait_ref);
585 if Some(def_id) == cx.tcx.lang_items.copy_trait() {
590 match ty::node_id_to_type(cx.tcx, item.id).sty {
591 ty::ty_enum(did, _) => did,
592 ty::ty_struct(did, _) => did,
598 if !ast_util::is_local(did) {
601 let item = match cx.tcx.map.find(did.node) {
602 Some(ast_map::NodeItem(item)) => item,
605 if !self.checked_raw_pointers.insert(item.id) {
609 ast::ItemStruct(..) | ast::ItemEnum(..) => {
610 let mut visitor = RawPtrDeriveVisitor { cx: cx };
611 visit::walk_item(&mut visitor, &*item);
621 "detects attributes that were not used by the compiler"
625 pub struct UnusedAttributes;
627 impl LintPass for UnusedAttributes {
628 fn get_lints(&self) -> LintArray {
629 lint_array!(UNUSED_ATTRIBUTES)
632 fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) {
633 // Note that check_name() marks the attribute as used if it matches.
634 for &(ref name, ty) in KNOWN_ATTRIBUTES {
636 AttributeType::Whitelisted
637 | AttributeType::Gated(_, _) if attr.check_name(name) => {
644 if !attr::is_used(attr) {
645 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
646 if KNOWN_ATTRIBUTES.contains(&(&attr.name(), AttributeType::CrateLevel)) {
647 let msg = match attr.node.style {
648 ast::AttrOuter => "crate-level attribute should be an inner \
649 attribute: add an exclamation mark: #![foo]",
650 ast::AttrInner => "crate-level attribute should be in the \
653 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
662 "path statements with no effect"
666 pub struct PathStatements;
668 impl LintPass for PathStatements {
669 fn get_lints(&self) -> LintArray {
670 lint_array!(PATH_STATEMENTS)
673 fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
675 ast::StmtSemi(ref expr, _) => {
677 ast::ExprPath(..) => cx.span_lint(PATH_STATEMENTS, s.span,
678 "path statement with no effect"),
690 "unused result of a type flagged as #[must_use]"
696 "unused result of an expression in a statement"
700 pub struct UnusedResults;
702 impl LintPass for UnusedResults {
703 fn get_lints(&self) -> LintArray {
704 lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS)
707 fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
708 let expr = match s.node {
709 ast::StmtSemi(ref expr, _) => &**expr,
713 if let ast::ExprRet(..) = expr.node {
717 let t = ty::expr_ty(cx.tcx, expr);
718 let warned = match t.sty {
719 ty::ty_tup(ref tys) if tys.is_empty() => return,
720 ty::ty_bool => return,
721 ty::ty_struct(did, _) |
722 ty::ty_enum(did, _) => {
723 if ast_util::is_local(did) {
724 if let ast_map::NodeItem(it) = cx.tcx.map.get(did.node) {
725 check_must_use(cx, &it.attrs, s.span)
730 let attrs = csearch::get_item_attrs(&cx.sess().cstore, did);
731 check_must_use(cx, &attrs[..], s.span)
737 cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
740 fn check_must_use(cx: &Context, attrs: &[ast::Attribute], sp: Span) -> bool {
742 if attr.check_name("must_use") {
743 let mut msg = "unused result which must be used".to_string();
744 // check for #[must_use="..."]
745 match attr.value_str() {
752 cx.span_lint(UNUSED_MUST_USE, sp, &msg);
762 pub NON_CAMEL_CASE_TYPES,
764 "types, variants, traits and type parameters should have camel case names"
768 pub struct NonCamelCaseTypes;
770 impl NonCamelCaseTypes {
771 fn check_case(&self, cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
772 fn is_camel_case(ident: ast::Ident) -> bool {
773 let ident = token::get_ident(ident);
774 if ident.is_empty() {
777 let ident = ident.trim_matches('_');
779 // start with a non-lowercase letter rather than non-uppercase
780 // ones (some scripts don't have a concept of upper/lowercase)
781 ident.len() > 0 && !ident.char_at(0).is_lowercase() && !ident.contains('_')
784 fn to_camel_case(s: &str) -> String {
785 s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)|
787 c.to_uppercase().collect::<String>()
789 c.to_lowercase().collect()
791 )).collect::<Vec<_>>().concat()
794 let s = token::get_ident(ident);
796 if !is_camel_case(ident) {
797 let c = to_camel_case(&s);
798 let m = if c.is_empty() {
799 format!("{} `{}` should have a camel case name such as `CamelCase`", sort, s)
801 format!("{} `{}` should have a camel case name such as `{}`", sort, s, c)
803 cx.span_lint(NON_CAMEL_CASE_TYPES, span, &m[..]);
808 impl LintPass for NonCamelCaseTypes {
809 fn get_lints(&self) -> LintArray {
810 lint_array!(NON_CAMEL_CASE_TYPES)
813 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
814 let has_extern_repr = it.attrs.iter().any(|attr| {
815 attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr).iter()
816 .any(|r| r == &attr::ReprExtern)
823 ast::ItemTy(..) | ast::ItemStruct(..) => {
824 self.check_case(cx, "type", it.ident, it.span)
826 ast::ItemTrait(..) => {
827 self.check_case(cx, "trait", it.ident, it.span)
829 ast::ItemEnum(ref enum_definition, _) => {
833 self.check_case(cx, "type", it.ident, it.span);
834 for variant in &enum_definition.variants {
835 self.check_case(cx, "variant", variant.node.name, variant.span);
842 fn check_generics(&mut self, cx: &Context, it: &ast::Generics) {
843 for gen in &*it.ty_params {
844 self.check_case(cx, "type parameter", gen.ident, gen.span);
856 fn method_context(cx: &Context, id: ast::NodeId, span: Span) -> MethodContext {
857 match cx.tcx.impl_or_trait_items.borrow().get(&local_def(id)) {
858 None => cx.sess().span_bug(span, "missing method descriptor?!"),
859 Some(item) => match item.container() {
860 ty::TraitContainer(..) => MethodContext::TraitDefaultImpl,
861 ty::ImplContainer(cid) => {
862 match ty::impl_trait_ref(cx.tcx, cid) {
863 Some(_) => MethodContext::TraitImpl,
864 None => MethodContext::PlainImpl
874 "methods, functions, lifetime parameters and modules should have snake case names"
878 pub struct NonSnakeCase;
881 fn to_snake_case(mut str: &str) -> String {
882 let mut words = vec![];
883 // Preserve leading underscores
884 str = str.trim_left_matches(|c: char| {
886 words.push(String::new());
892 for s in str.split('_') {
893 let mut last_upper = false;
894 let mut buf = String::new();
898 for ch in s.chars() {
899 if !buf.is_empty() && buf != "'"
905 last_upper = ch.is_uppercase();
906 buf.extend(ch.to_lowercase());
913 fn check_snake_case(&self, cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
914 fn is_snake_case(ident: ast::Ident) -> bool {
915 let ident = token::get_ident(ident);
916 if ident.is_empty() {
919 let ident = ident.trim_left_matches('\'');
920 let ident = ident.trim_matches('_');
922 let mut allow_underscore = true;
923 ident.chars().all(|c| {
924 allow_underscore = match c {
925 '_' if !allow_underscore => return false,
927 c if !c.is_uppercase() => true,
934 let s = token::get_ident(ident);
936 if !is_snake_case(ident) {
937 let sc = NonSnakeCase::to_snake_case(&s);
939 cx.span_lint(NON_SNAKE_CASE, span,
940 &*format!("{} `{}` should have a snake case name such as `{}`",
943 cx.span_lint(NON_SNAKE_CASE, span,
944 &*format!("{} `{}` should have a snake case name",
951 impl LintPass for NonSnakeCase {
952 fn get_lints(&self) -> LintArray {
953 lint_array!(NON_SNAKE_CASE)
956 fn check_fn(&mut self, cx: &Context,
957 fk: visit::FnKind, _: &ast::FnDecl,
958 _: &ast::Block, span: Span, id: ast::NodeId) {
960 visit::FkMethod(ident, _) => match method_context(cx, id, span) {
961 MethodContext::PlainImpl => {
962 self.check_snake_case(cx, "method", ident, span)
964 MethodContext::TraitDefaultImpl => {
965 self.check_snake_case(cx, "trait method", ident, span)
969 visit::FkItemFn(ident, _, _, _) => {
970 self.check_snake_case(cx, "function", ident, span)
976 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
977 if let ast::ItemMod(_) = it.node {
978 self.check_snake_case(cx, "module", it.ident, it.span);
982 fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) {
983 if let ast::MethodTraitItem(_, None) = trait_item.node {
984 self.check_snake_case(cx, "trait method", trait_item.ident, trait_item.span);
988 fn check_lifetime_def(&mut self, cx: &Context, t: &ast::LifetimeDef) {
989 self.check_snake_case(cx, "lifetime", t.lifetime.name.ident(), t.lifetime.span);
992 fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
993 if let &ast::PatIdent(_, ref path1, _) = &p.node {
994 let def = cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def());
995 if let Some(def::DefLocal(_)) = def {
996 self.check_snake_case(cx, "variable", path1.node, p.span);
1001 fn check_struct_def(&mut self, cx: &Context, s: &ast::StructDef,
1002 _: ast::Ident, _: &ast::Generics, _: ast::NodeId) {
1003 for sf in &s.fields {
1004 if let ast::StructField_ { kind: ast::NamedField(ident, _), .. } = sf.node {
1005 self.check_snake_case(cx, "structure field", ident, sf.span);
1012 pub NON_UPPER_CASE_GLOBALS,
1014 "static constants should have uppercase identifiers"
1018 pub struct NonUpperCaseGlobals;
1020 impl NonUpperCaseGlobals {
1021 fn check_upper_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
1022 let s = token::get_ident(ident);
1024 if s.chars().any(|c| c.is_lowercase()) {
1025 let uc = NonSnakeCase::to_snake_case(&s).to_uppercase();
1027 cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
1028 &format!("{} `{}` should have an upper case name such as `{}`",
1031 cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
1032 &format!("{} `{}` should have an upper case name",
1039 impl LintPass for NonUpperCaseGlobals {
1040 fn get_lints(&self) -> LintArray {
1041 lint_array!(NON_UPPER_CASE_GLOBALS)
1044 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
1046 // only check static constants
1047 ast::ItemStatic(_, ast::MutImmutable, _) => {
1048 NonUpperCaseGlobals::check_upper_case(cx, "static constant", it.ident, it.span);
1050 ast::ItemConst(..) => {
1051 NonUpperCaseGlobals::check_upper_case(cx, "constant", it.ident, it.span);
1057 fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
1058 // Lint for constants that look like binding identifiers (#7526)
1059 match (&p.node, cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def())) {
1060 (&ast::PatIdent(_, ref path1, _), Some(def::DefConst(..))) => {
1061 NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern",
1062 path1.node, p.span);
1072 "`if`, `match`, `while` and `return` do not need parentheses"
1076 pub struct UnusedParens;
1079 fn check_unused_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str,
1080 struct_lit_needs_parens: bool) {
1081 if let ast::ExprParen(ref inner) = value.node {
1082 let necessary = struct_lit_needs_parens && contains_exterior_struct_lit(&**inner);
1084 cx.span_lint(UNUSED_PARENS, value.span,
1085 &format!("unnecessary parentheses around {}", msg))
1089 /// Expressions that syntactically contain an "exterior" struct
1090 /// literal i.e. not surrounded by any parens or other
1091 /// delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo
1092 /// == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X {
1093 /// y: 1 }) == foo` does not.
1094 fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
1096 ast::ExprStruct(..) => true,
1098 ast::ExprAssign(ref lhs, ref rhs) |
1099 ast::ExprAssignOp(_, ref lhs, ref rhs) |
1100 ast::ExprBinary(_, ref lhs, ref rhs) => {
1101 // X { y: 1 } + X { y: 2 }
1102 contains_exterior_struct_lit(&**lhs) ||
1103 contains_exterior_struct_lit(&**rhs)
1105 ast::ExprUnary(_, ref x) |
1106 ast::ExprCast(ref x, _) |
1107 ast::ExprField(ref x, _) |
1108 ast::ExprTupField(ref x, _) |
1109 ast::ExprIndex(ref x, _) => {
1110 // &X { y: 1 }, X { y: 1 }.y
1111 contains_exterior_struct_lit(&**x)
1114 ast::ExprMethodCall(_, _, ref exprs) => {
1115 // X { y: 1 }.bar(...)
1116 contains_exterior_struct_lit(&*exprs[0])
1125 impl LintPass for UnusedParens {
1126 fn get_lints(&self) -> LintArray {
1127 lint_array!(UNUSED_PARENS)
1130 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1131 let (value, msg, struct_lit_needs_parens) = match e.node {
1132 ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true),
1133 ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true),
1134 ast::ExprMatch(ref head, _, source) => match source {
1135 ast::MatchSource::Normal => (head, "`match` head expression", true),
1136 ast::MatchSource::IfLetDesugar { .. } => (head, "`if let` head expression", true),
1137 ast::MatchSource::WhileLetDesugar => (head, "`while let` head expression", true),
1138 ast::MatchSource::ForLoopDesugar => (head, "`for` head expression", true),
1140 ast::ExprRet(Some(ref value)) => (value, "`return` value", false),
1141 ast::ExprAssign(_, ref value) => (value, "assigned value", false),
1142 ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false),
1145 self.check_unused_parens_core(cx, &**value, msg, struct_lit_needs_parens);
1148 fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
1149 let (value, msg) = match s.node {
1150 ast::StmtDecl(ref decl, _) => match decl.node {
1151 ast::DeclLocal(ref local) => match local.init {
1152 Some(ref value) => (value, "assigned value"),
1159 self.check_unused_parens_core(cx, &**value, msg, false);
1164 UNUSED_IMPORT_BRACES,
1166 "unnecessary braces around an imported item"
1170 pub struct UnusedImportBraces;
1172 impl LintPass for UnusedImportBraces {
1173 fn get_lints(&self) -> LintArray {
1174 lint_array!(UNUSED_IMPORT_BRACES)
1177 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1178 if let ast::ItemUse(ref view_path) = item.node {
1179 if let ast::ViewPathList(_, ref items) = view_path.node {
1180 if items.len() == 1 {
1181 if let ast::PathListIdent {ref name, ..} = items[0].node {
1182 let m = format!("braces around {} is unnecessary",
1183 &token::get_ident(*name));
1184 cx.span_lint(UNUSED_IMPORT_BRACES, item.span,
1194 NON_SHORTHAND_FIELD_PATTERNS,
1196 "using `Struct { x: x }` instead of `Struct { x }`"
1200 pub struct NonShorthandFieldPatterns;
1202 impl LintPass for NonShorthandFieldPatterns {
1203 fn get_lints(&self) -> LintArray {
1204 lint_array!(NON_SHORTHAND_FIELD_PATTERNS)
1207 fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) {
1208 let def_map = cx.tcx.def_map.borrow();
1209 if let ast::PatStruct(_, ref v, _) = pat.node {
1210 let field_pats = v.iter().filter(|fieldpat| {
1211 if fieldpat.node.is_shorthand {
1214 let def = def_map.get(&fieldpat.node.pat.id).map(|d| d.full_def());
1215 def == Some(def::DefLocal(fieldpat.node.pat.id))
1217 for fieldpat in field_pats {
1218 if let ast::PatIdent(_, ident, None) = fieldpat.node.pat.node {
1219 if ident.node.as_str() == fieldpat.node.ident.as_str() {
1220 cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span,
1221 &format!("the `{}:` in this pattern is redundant and can \
1222 be removed", ident.node.as_str()))
1233 "unnecessary use of an `unsafe` block"
1237 pub struct UnusedUnsafe;
1239 impl LintPass for UnusedUnsafe {
1240 fn get_lints(&self) -> LintArray {
1241 lint_array!(UNUSED_UNSAFE)
1244 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1245 if let ast::ExprBlock(ref blk) = e.node {
1246 // Don't warn about generated blocks, that'll just pollute the output.
1247 if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
1248 !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
1249 cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block");
1258 "usage of `unsafe` code"
1262 pub struct UnsafeCode;
1264 impl LintPass for UnsafeCode {
1265 fn get_lints(&self) -> LintArray {
1266 lint_array!(UNSAFE_CODE)
1269 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1270 if let ast::ExprBlock(ref blk) = e.node {
1271 // Don't warn about generated blocks, that'll just pollute the output.
1272 if blk.rules == ast::UnsafeBlock(ast::UserProvided) {
1273 cx.span_lint(UNSAFE_CODE, blk.span, "usage of an `unsafe` block");
1278 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
1280 ast::ItemTrait(ast::Unsafety::Unsafe, _, _, _) =>
1281 cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait"),
1283 ast::ItemImpl(ast::Unsafety::Unsafe, _, _, _, _, _) =>
1284 cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait"),
1290 fn check_fn(&mut self, cx: &Context, fk: visit::FnKind, _: &ast::FnDecl,
1291 _: &ast::Block, span: Span, _: ast::NodeId) {
1293 visit::FkItemFn(_, _, ast::Unsafety::Unsafe, _) =>
1294 cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function"),
1296 visit::FkMethod(_, sig) => {
1297 if sig.unsafety == ast::Unsafety::Unsafe {
1298 cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method")
1306 fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) {
1307 if let ast::MethodTraitItem(ref sig, None) = trait_item.node {
1308 if sig.unsafety == ast::Unsafety::Unsafe {
1309 cx.span_lint(UNSAFE_CODE, trait_item.span,
1310 "declaration of an `unsafe` method")
1319 "detect mut variables which don't need to be mutable"
1323 pub struct UnusedMut;
1326 fn check_unused_mut_pat(&self, cx: &Context, pats: &[P<ast::Pat>]) {
1327 // collect all mutable pattern and group their NodeIDs by their Identifier to
1328 // avoid false warnings in match arms with multiple patterns
1330 let mut mutables = FnvHashMap();
1332 pat_util::pat_bindings(&cx.tcx.def_map, &**p, |mode, id, _, path1| {
1333 let ident = path1.node;
1334 if let ast::BindByValue(ast::MutMutable) = mode {
1335 if !token::get_ident(ident).starts_with("_") {
1336 match mutables.entry(ident.name.usize()) {
1337 Vacant(entry) => { entry.insert(vec![id]); },
1338 Occupied(mut entry) => { entry.get_mut().push(id); },
1345 let used_mutables = cx.tcx.used_mut_nodes.borrow();
1346 for (_, v) in &mutables {
1347 if !v.iter().any(|e| used_mutables.contains(e)) {
1348 cx.span_lint(UNUSED_MUT, cx.tcx.map.span(v[0]),
1349 "variable does not need to be mutable");
1355 impl LintPass for UnusedMut {
1356 fn get_lints(&self) -> LintArray {
1357 lint_array!(UNUSED_MUT)
1360 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1361 if let ast::ExprMatch(_, ref arms, _) = e.node {
1363 self.check_unused_mut_pat(cx, &a.pats)
1368 fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
1369 if let ast::StmtDecl(ref d, _) = s.node {
1370 if let ast::DeclLocal(ref l) = d.node {
1371 self.check_unused_mut_pat(cx, slice::ref_slice(&l.pat));
1376 fn check_fn(&mut self, cx: &Context,
1377 _: visit::FnKind, decl: &ast::FnDecl,
1378 _: &ast::Block, _: Span, _: ast::NodeId) {
1379 for a in &decl.inputs {
1380 self.check_unused_mut_pat(cx, slice::ref_slice(&a.pat));
1388 "detects unnecessary allocations that can be eliminated"
1392 pub struct UnusedAllocation;
1394 impl LintPass for UnusedAllocation {
1395 fn get_lints(&self) -> LintArray {
1396 lint_array!(UNUSED_ALLOCATION)
1399 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1401 ast::ExprUnary(ast::UnUniq, _) => (),
1405 if let Some(adjustment) = cx.tcx.adjustments.borrow().get(&e.id) {
1406 if let ty::AdjustDerefRef(ty::AutoDerefRef { ref autoref, .. }) = *adjustment {
1408 &Some(ty::AutoPtr(_, ast::MutImmutable, None)) => {
1409 cx.span_lint(UNUSED_ALLOCATION, e.span,
1410 "unnecessary allocation, use & instead");
1412 &Some(ty::AutoPtr(_, ast::MutMutable, None)) => {
1413 cx.span_lint(UNUSED_ALLOCATION, e.span,
1414 "unnecessary allocation, use &mut instead");
1426 "detects missing documentation for public members"
1429 pub struct MissingDoc {
1430 /// Stack of IDs of struct definitions.
1431 struct_def_stack: Vec<ast::NodeId>,
1433 /// True if inside variant definition
1436 /// Stack of whether #[doc(hidden)] is set
1437 /// at each level which has lint attributes.
1438 doc_hidden_stack: Vec<bool>,
1442 pub fn new() -> MissingDoc {
1444 struct_def_stack: vec!(),
1446 doc_hidden_stack: vec!(false),
1450 fn doc_hidden(&self) -> bool {
1451 *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
1454 fn check_missing_docs_attrs(&self,
1456 id: Option<ast::NodeId>,
1457 attrs: &[ast::Attribute],
1459 desc: &'static str) {
1460 // If we're building a test harness, then warning about
1461 // documentation is probably not really relevant right now.
1462 if cx.sess().opts.test {
1466 // `#[doc(hidden)]` disables missing_docs check.
1467 if self.doc_hidden() {
1471 // Only check publicly-visible items, using the result from the privacy pass.
1472 // It's an option so the crate root can also use this function (it doesn't
1474 if let Some(ref id) = id {
1475 if !cx.exported_items.contains(id) {
1480 let has_doc = attrs.iter().any(|a| {
1481 match a.node.value.node {
1482 ast::MetaNameValue(ref name, _) if *name == "doc" => true,
1487 cx.span_lint(MISSING_DOCS, sp,
1488 &format!("missing documentation for {}", desc));
1493 impl LintPass for MissingDoc {
1494 fn get_lints(&self) -> LintArray {
1495 lint_array!(MISSING_DOCS)
1498 fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) {
1499 let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
1500 attr.check_name("doc") && match attr.meta_item_list() {
1502 Some(l) => attr::contains_name(&l[..], "hidden"),
1505 self.doc_hidden_stack.push(doc_hidden);
1508 fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) {
1509 self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
1512 fn check_struct_def(&mut self, _: &Context, _: &ast::StructDef,
1513 _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
1514 self.struct_def_stack.push(id);
1517 fn check_struct_def_post(&mut self, _: &Context, _: &ast::StructDef,
1518 _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
1519 let popped = self.struct_def_stack.pop().expect("empty struct_def_stack");
1520 assert!(popped == id);
1523 fn check_crate(&mut self, cx: &Context, krate: &ast::Crate) {
1524 self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate");
1527 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
1528 let desc = match it.node {
1529 ast::ItemFn(..) => "a function",
1530 ast::ItemMod(..) => "a module",
1531 ast::ItemEnum(..) => "an enum",
1532 ast::ItemStruct(..) => "a struct",
1533 ast::ItemTrait(..) => "a trait",
1534 ast::ItemTy(..) => "a type alias",
1537 self.check_missing_docs_attrs(cx, Some(it.id), &it.attrs, it.span, desc);
1540 fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) {
1541 let desc = match trait_item.node {
1542 ast::MethodTraitItem(..) => "a trait method",
1543 ast::TypeTraitItem(..) => "an associated type"
1545 self.check_missing_docs_attrs(cx, Some(trait_item.id),
1547 trait_item.span, desc);
1550 fn check_impl_item(&mut self, cx: &Context, impl_item: &ast::ImplItem) {
1551 // If the method is an impl for a trait, don't doc.
1552 if method_context(cx, impl_item.id, impl_item.span) == MethodContext::TraitImpl {
1556 let desc = match impl_item.node {
1557 ast::MethodImplItem(..) => "a method",
1558 ast::TypeImplItem(_) => "an associated type",
1559 ast::MacImplItem(_) => "an impl item macro"
1561 self.check_missing_docs_attrs(cx, Some(impl_item.id),
1563 impl_item.span, desc);
1566 fn check_struct_field(&mut self, cx: &Context, sf: &ast::StructField) {
1567 if let ast::NamedField(_, vis) = sf.node.kind {
1568 if vis == ast::Public || self.in_variant {
1569 let cur_struct_def = *self.struct_def_stack.last()
1570 .expect("empty struct_def_stack");
1571 self.check_missing_docs_attrs(cx, Some(cur_struct_def),
1572 &sf.node.attrs, sf.span,
1578 fn check_variant(&mut self, cx: &Context, v: &ast::Variant, _: &ast::Generics) {
1579 self.check_missing_docs_attrs(cx, Some(v.node.id), &v.node.attrs, v.span, "a variant");
1580 assert!(!self.in_variant);
1581 self.in_variant = true;
1584 fn check_variant_post(&mut self, _: &Context, _: &ast::Variant, _: &ast::Generics) {
1585 assert!(self.in_variant);
1586 self.in_variant = false;
1591 pub MISSING_COPY_IMPLEMENTATIONS,
1593 "detects potentially-forgotten implementations of `Copy`"
1597 pub struct MissingCopyImplementations;
1599 impl LintPass for MissingCopyImplementations {
1600 fn get_lints(&self) -> LintArray {
1601 lint_array!(MISSING_COPY_IMPLEMENTATIONS)
1604 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1605 if !cx.exported_items.contains(&item.id) {
1608 if cx.tcx.destructor_for_type.borrow().contains_key(&local_def(item.id)) {
1611 let ty = match item.node {
1612 ast::ItemStruct(_, ref ast_generics) => {
1613 if ast_generics.is_parameterized() {
1616 ty::mk_struct(cx.tcx, local_def(item.id),
1617 cx.tcx.mk_substs(Substs::empty()))
1619 ast::ItemEnum(_, ref ast_generics) => {
1620 if ast_generics.is_parameterized() {
1623 ty::mk_enum(cx.tcx, local_def(item.id),
1624 cx.tcx.mk_substs(Substs::empty()))
1628 let parameter_environment = ty::empty_parameter_environment(cx.tcx);
1629 if !ty::type_moves_by_default(¶meter_environment, item.span, ty) {
1632 if ty::can_type_implement_copy(¶meter_environment, item.span, ty).is_ok() {
1633 cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
1635 "type could implement `Copy`; consider adding `impl \
1642 MISSING_DEBUG_IMPLEMENTATIONS,
1644 "detects missing implementations of fmt::Debug"
1647 pub struct MissingDebugImplementations {
1648 impling_types: Option<NodeSet>,
1651 impl MissingDebugImplementations {
1652 pub fn new() -> MissingDebugImplementations {
1653 MissingDebugImplementations {
1654 impling_types: None,
1659 impl LintPass for MissingDebugImplementations {
1660 fn get_lints(&self) -> LintArray {
1661 lint_array!(MISSING_DEBUG_IMPLEMENTATIONS)
1664 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1665 if !cx.exported_items.contains(&item.id) {
1670 ast::ItemStruct(..) | ast::ItemEnum(..) => {},
1674 let debug = match cx.tcx.lang_items.debug_trait() {
1675 Some(debug) => debug,
1679 if self.impling_types.is_none() {
1680 let impls = cx.tcx.trait_impls.borrow();
1681 let impls = match impls.get(&debug) {
1683 impls.borrow().iter()
1684 .filter(|d| d.krate == ast::LOCAL_CRATE)
1685 .filter_map(|d| ty::ty_to_def_id(ty::node_id_to_type(cx.tcx, d.node)))
1691 self.impling_types = Some(impls);
1692 debug!("{:?}", self.impling_types);
1695 if !self.impling_types.as_ref().unwrap().contains(&item.id) {
1696 cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS,
1698 "type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
1699 or a manual implementation")
1707 "detects use of #[deprecated] items"
1710 /// Checks for use of items with `#[deprecated]` attributes
1712 pub struct Stability;
1715 fn lint(&self, cx: &Context, _id: ast::DefId, span: Span, stability: &Option<attr::Stability>) {
1716 // Deprecated attributes apply in-crate and cross-crate.
1717 let (lint, label) = match *stability {
1718 Some(attr::Stability { deprecated_since: Some(_), .. }) =>
1719 (DEPRECATED, "deprecated"),
1723 output(cx, span, stability, lint, label);
1725 fn output(cx: &Context, span: Span, stability: &Option<attr::Stability>,
1726 lint: &'static Lint, label: &'static str) {
1727 let msg = match *stability {
1728 Some(attr::Stability { reason: Some(ref s), .. }) => {
1729 format!("use of {} item: {}", label, *s)
1731 _ => format!("use of {} item", label)
1734 cx.span_lint(lint, span, &msg[..]);
1739 impl LintPass for Stability {
1740 fn get_lints(&self) -> LintArray {
1741 lint_array!(DEPRECATED)
1744 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1745 stability::check_item(cx.tcx, item, false,
1746 &mut |id, sp, stab| self.lint(cx, id, sp, stab));
1749 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1750 stability::check_expr(cx.tcx, e,
1751 &mut |id, sp, stab| self.lint(cx, id, sp, stab));
1754 fn check_path(&mut self, cx: &Context, path: &ast::Path, id: ast::NodeId) {
1755 stability::check_path(cx.tcx, path, id,
1756 &mut |id, sp, stab| self.lint(cx, id, sp, stab));
1759 fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) {
1760 stability::check_pat(cx.tcx, pat,
1761 &mut |id, sp, stab| self.lint(cx, id, sp, stab))
1766 pub UNCONDITIONAL_RECURSION,
1768 "functions that cannot return without calling themselves"
1772 pub struct UnconditionalRecursion;
1775 impl LintPass for UnconditionalRecursion {
1776 fn get_lints(&self) -> LintArray {
1777 lint_array![UNCONDITIONAL_RECURSION]
1780 fn check_fn(&mut self, cx: &Context, fn_kind: visit::FnKind, _: &ast::FnDecl,
1781 blk: &ast::Block, sp: Span, id: ast::NodeId) {
1782 // FIXME(#23542) Replace with type ascription.
1783 #![allow(trivial_casts)]
1785 type F = for<'tcx> fn(&ty::ctxt<'tcx>,
1786 ast::NodeId, ast::NodeId, ast::Ident, ast::NodeId) -> bool;
1788 let (name, checker) = match fn_kind {
1789 visit::FkItemFn(name, _, _, _) => (name, id_refers_to_this_fn as F),
1790 visit::FkMethod(name, _) => (name, id_refers_to_this_method as F),
1791 // closures can't recur, so they don't matter.
1792 visit::FkFnBlock => return
1795 let impl_def_id = ty::impl_of_method(cx.tcx, local_def(id))
1796 .unwrap_or(local_def(ast::DUMMY_NODE_ID));
1797 assert!(ast_util::is_local(impl_def_id));
1798 let impl_node_id = impl_def_id.node;
1800 // Walk through this function (say `f`) looking to see if
1801 // every possible path references itself, i.e. the function is
1802 // called recursively unconditionally. This is done by trying
1803 // to find a path from the entry node to the exit node that
1804 // *doesn't* call `f` by traversing from the entry while
1805 // pretending that calls of `f` are sinks (i.e. ignoring any
1806 // exit edges from them).
1808 // NB. this has an edge case with non-returning statements,
1809 // like `loop {}` or `panic!()`: control flow never reaches
1810 // the exit node through these, so one can have a function
1811 // that never actually calls itselfs but is still picked up by
1814 // fn f(cond: bool) {
1815 // if !cond { panic!() } // could come from `assert!(cond)`
1819 // In general, functions of that form may be able to call
1820 // itself a finite number of times and then diverge. The lint
1821 // considers this to be an error for two reasons, (a) it is
1822 // easier to implement, and (b) it seems rare to actually want
1823 // to have behaviour like the above, rather than
1824 // e.g. accidentally recurring after an assert.
1826 let cfg = cfg::CFG::new(cx.tcx, blk);
1828 let mut work_queue = vec![cfg.entry];
1829 let mut reached_exit_without_self_call = false;
1830 let mut self_call_spans = vec![];
1831 let mut visited = BitSet::new();
1833 while let Some(idx) = work_queue.pop() {
1834 if idx == cfg.exit {
1836 reached_exit_without_self_call = true;
1840 let cfg_id = idx.node_id();
1841 if visited.contains(&cfg_id) {
1845 visited.insert(cfg_id);
1847 let node_id = cfg.graph.node_data(idx).id();
1849 // is this a recursive call?
1850 if node_id != ast::DUMMY_NODE_ID && checker(cx.tcx, impl_node_id, id, name, node_id) {
1851 self_call_spans.push(cx.tcx.map.span(node_id));
1852 // this is a self call, so we shouldn't explore past
1853 // this node in the CFG.
1856 // add the successors of this node to explore the graph further.
1857 cfg.graph.each_outgoing_edge(idx, |_, edge| {
1858 let target_idx = edge.target();
1859 let target_cfg_id = target_idx.node_id();
1860 if !visited.contains(&target_cfg_id) {
1861 work_queue.push(target_idx)
1867 // Check the number of self calls because a function that
1868 // doesn't return (e.g. calls a `-> !` function or `loop { /*
1869 // no break */ }`) shouldn't be linted unless it actually
1871 if !reached_exit_without_self_call && self_call_spans.len() > 0 {
1872 cx.span_lint(UNCONDITIONAL_RECURSION, sp,
1873 "function cannot return without recurring");
1875 // FIXME #19668: these could be span_lint_note's instead of this manual guard.
1876 if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow {
1877 let sess = cx.sess();
1878 // offer some help to the programmer.
1879 for call in &self_call_spans {
1880 sess.span_note(*call, "recursive call site")
1882 sess.fileline_help(sp, "a `loop` may express intention \
1883 better if this is on purpose")
1890 // Functions for identifying if the given NodeId `id`
1891 // represents a call to the function `fn_id`/method
1894 fn id_refers_to_this_fn<'tcx>(tcx: &ty::ctxt<'tcx>,
1898 id: ast::NodeId) -> bool {
1899 tcx.def_map.borrow().get(&id)
1900 .map_or(false, |def| def.def_id() == local_def(fn_id))
1903 // check if the method call `id` refers to method `method_id`
1904 // (with name `method_name` contained in impl `impl_id`).
1905 fn id_refers_to_this_method<'tcx>(tcx: &ty::ctxt<'tcx>,
1906 impl_id: ast::NodeId,
1907 method_id: ast::NodeId,
1908 method_name: ast::Ident,
1909 id: ast::NodeId) -> bool {
1910 let did = match tcx.method_map.borrow().get(&ty::MethodCall::expr(id)) {
1911 None => return false,
1912 Some(m) => match m.origin {
1913 // There's no way to know if a method call via a
1914 // vtable is recursion, so we assume it's not.
1915 ty::MethodTraitObject(_) => return false,
1917 // This `did` refers directly to the method definition.
1918 ty::MethodStatic(did) | ty::MethodStaticClosure(did) => did,
1920 // MethodTypeParam are methods from traits:
1922 // The `impl ... for ...` of this method call
1923 // isn't known, e.g. it might be a default method
1924 // in a trait, so we get the def-id of the trait
1926 ty::MethodTypeParam(
1927 ty::MethodParam { ref trait_ref, method_num, impl_def_id: None, }) => {
1928 ty::trait_item(tcx, trait_ref.def_id, method_num).def_id()
1931 // The `impl` is known, so we check that with a
1933 ty::MethodTypeParam(
1934 ty::MethodParam { impl_def_id: Some(impl_def_id), .. }) => {
1936 let name = match tcx.map.expect_expr(id).node {
1937 ast::ExprMethodCall(ref sp_ident, _, _) => sp_ident.node,
1938 _ => tcx.sess.span_bug(
1940 "non-method call expr behaving like a method call?")
1942 // It matches if it comes from the same impl,
1943 // and has the same method name.
1944 return ast_util::is_local(impl_def_id)
1945 && impl_def_id.node == impl_id
1946 && method_name.name == name.name
1951 ast_util::is_local(did) && did.node == method_id
1959 "compiler plugin used as ordinary library in non-plugin crate"
1963 pub struct PluginAsLibrary;
1965 impl LintPass for PluginAsLibrary {
1966 fn get_lints(&self) -> LintArray {
1967 lint_array![PLUGIN_AS_LIBRARY]
1970 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
1971 if cx.sess().plugin_registrar_fn.get().is_some() {
1972 // We're compiling a plugin; it's fine to link other plugins.
1977 ast::ItemExternCrate(..) => (),
1981 let md = match cx.sess().cstore.find_extern_mod_stmt_cnum(it.id) {
1982 Some(cnum) => cx.sess().cstore.get_crate_data(cnum),
1984 // Probably means we aren't linking the crate for some reason.
1986 // Not sure if / when this could happen.
1991 if decoder::get_plugin_registrar_fn(md.data()).is_some() {
1992 cx.span_lint(PLUGIN_AS_LIBRARY, it.span,
1993 "compiler plugin used as an ordinary library");
1999 PRIVATE_NO_MANGLE_FNS,
2001 "functions marked #[no_mangle] should be exported"
2005 PRIVATE_NO_MANGLE_STATICS,
2007 "statics marked #[no_mangle] should be exported"
2011 NO_MANGLE_CONST_ITEMS,
2013 "const items will not have their symbols exported"
2017 pub struct InvalidNoMangleItems;
2019 impl LintPass for InvalidNoMangleItems {
2020 fn get_lints(&self) -> LintArray {
2021 lint_array!(PRIVATE_NO_MANGLE_FNS,
2022 PRIVATE_NO_MANGLE_STATICS,
2023 NO_MANGLE_CONST_ITEMS)
2026 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
2028 ast::ItemFn(..) => {
2029 if attr::contains_name(&it.attrs, "no_mangle") &&
2030 !cx.exported_items.contains(&it.id) {
2031 let msg = format!("function {} is marked #[no_mangle], but not exported",
2033 cx.span_lint(PRIVATE_NO_MANGLE_FNS, it.span, &msg);
2036 ast::ItemStatic(..) => {
2037 if attr::contains_name(&it.attrs, "no_mangle") &&
2038 !cx.exported_items.contains(&it.id) {
2039 let msg = format!("static {} is marked #[no_mangle], but not exported",
2041 cx.span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, &msg);
2044 ast::ItemConst(..) => {
2045 if attr::contains_name(&it.attrs, "no_mangle") {
2046 // Const items do not refer to a particular location in memory, and therefore
2047 // don't have anything to attach a symbol to
2048 let msg = "const items should never be #[no_mangle], consider instead using \
2050 cx.span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
2058 /// Forbids using the `#[feature(...)]` attribute
2060 pub struct UnstableFeatures;
2065 "enabling unstable features"
2068 impl LintPass for UnstableFeatures {
2069 fn get_lints(&self) -> LintArray {
2070 lint_array!(UNSTABLE_FEATURES)
2072 fn check_attribute(&mut self, ctx: &Context, attr: &ast::Attribute) {
2073 if attr::contains_name(&[attr.node.value.clone()], "feature") {
2074 ctx.span_lint(UNSTABLE_FEATURES, attr.span, "unstable feature");