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::{HashSet, 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>,
1440 /// Private traits or trait items that leaked through. Don't check their methods.
1441 private_traits: HashSet<ast::NodeId>,
1445 pub fn new() -> MissingDoc {
1447 struct_def_stack: vec!(),
1449 doc_hidden_stack: vec!(false),
1450 private_traits: HashSet::new(),
1454 fn doc_hidden(&self) -> bool {
1455 *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
1458 fn check_missing_docs_attrs(&self,
1460 id: Option<ast::NodeId>,
1461 attrs: &[ast::Attribute],
1463 desc: &'static str) {
1464 // If we're building a test harness, then warning about
1465 // documentation is probably not really relevant right now.
1466 if cx.sess().opts.test {
1470 // `#[doc(hidden)]` disables missing_docs check.
1471 if self.doc_hidden() {
1475 // Only check publicly-visible items, using the result from the privacy pass.
1476 // It's an option so the crate root can also use this function (it doesn't
1478 if let Some(ref id) = id {
1479 if !cx.exported_items.contains(id) {
1484 let has_doc = attrs.iter().any(|a| {
1485 match a.node.value.node {
1486 ast::MetaNameValue(ref name, _) if *name == "doc" => true,
1491 cx.span_lint(MISSING_DOCS, sp,
1492 &format!("missing documentation for {}", desc));
1497 impl LintPass for MissingDoc {
1498 fn get_lints(&self) -> LintArray {
1499 lint_array!(MISSING_DOCS)
1502 fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) {
1503 let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
1504 attr.check_name("doc") && match attr.meta_item_list() {
1506 Some(l) => attr::contains_name(&l[..], "hidden"),
1509 self.doc_hidden_stack.push(doc_hidden);
1512 fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) {
1513 self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
1516 fn check_struct_def(&mut self, _: &Context, _: &ast::StructDef,
1517 _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
1518 self.struct_def_stack.push(id);
1521 fn check_struct_def_post(&mut self, _: &Context, _: &ast::StructDef,
1522 _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
1523 let popped = self.struct_def_stack.pop().expect("empty struct_def_stack");
1524 assert!(popped == id);
1527 fn check_crate(&mut self, cx: &Context, krate: &ast::Crate) {
1528 self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate");
1531 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
1532 let desc = match it.node {
1533 ast::ItemFn(..) => "a function",
1534 ast::ItemMod(..) => "a module",
1535 ast::ItemEnum(..) => "an enum",
1536 ast::ItemStruct(..) => "a struct",
1537 ast::ItemTrait(_, _, _, ref items) => {
1538 // Issue #11592, traits are always considered exported, even when private.
1539 if it.vis == ast::Visibility::Inherited {
1540 self.private_traits.insert(it.id);
1542 self.private_traits.insert(itm.id);
1548 ast::ItemTy(..) => "a type alias",
1549 ast::ItemImpl(_, _, _, Some(ref trait_ref), _, ref impl_items) => {
1550 // If the trait is private, add the impl items to private_traits so they don't get
1551 // reported for missing docs.
1552 let real_trait = ty::trait_ref_to_def_id(cx.tcx, trait_ref);
1553 match cx.tcx.map.find(real_trait.node) {
1554 Some(ast_map::NodeItem(item)) => if item.vis == ast::Visibility::Inherited {
1555 for itm in impl_items {
1556 self.private_traits.insert(itm.id);
1566 self.check_missing_docs_attrs(cx, Some(it.id), &it.attrs, it.span, desc);
1569 fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) {
1570 if self.private_traits.contains(&trait_item.id) { return }
1572 let desc = match trait_item.node {
1573 ast::MethodTraitItem(..) => "a trait method",
1574 ast::TypeTraitItem(..) => "an associated type"
1577 self.check_missing_docs_attrs(cx, Some(trait_item.id),
1579 trait_item.span, desc);
1582 fn check_impl_item(&mut self, cx: &Context, impl_item: &ast::ImplItem) {
1583 // If the method is an impl for a trait, don't doc.
1584 if method_context(cx, impl_item.id, impl_item.span) == MethodContext::TraitImpl {
1588 let desc = match impl_item.node {
1589 ast::MethodImplItem(..) => "a method",
1590 ast::TypeImplItem(_) => "an associated type",
1591 ast::MacImplItem(_) => "an impl item macro"
1593 self.check_missing_docs_attrs(cx, Some(impl_item.id),
1595 impl_item.span, desc);
1598 fn check_struct_field(&mut self, cx: &Context, sf: &ast::StructField) {
1599 if let ast::NamedField(_, vis) = sf.node.kind {
1600 if vis == ast::Public || self.in_variant {
1601 let cur_struct_def = *self.struct_def_stack.last()
1602 .expect("empty struct_def_stack");
1603 self.check_missing_docs_attrs(cx, Some(cur_struct_def),
1604 &sf.node.attrs, sf.span,
1610 fn check_variant(&mut self, cx: &Context, v: &ast::Variant, _: &ast::Generics) {
1611 self.check_missing_docs_attrs(cx, Some(v.node.id), &v.node.attrs, v.span, "a variant");
1612 assert!(!self.in_variant);
1613 self.in_variant = true;
1616 fn check_variant_post(&mut self, _: &Context, _: &ast::Variant, _: &ast::Generics) {
1617 assert!(self.in_variant);
1618 self.in_variant = false;
1623 pub MISSING_COPY_IMPLEMENTATIONS,
1625 "detects potentially-forgotten implementations of `Copy`"
1629 pub struct MissingCopyImplementations;
1631 impl LintPass for MissingCopyImplementations {
1632 fn get_lints(&self) -> LintArray {
1633 lint_array!(MISSING_COPY_IMPLEMENTATIONS)
1636 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1637 if !cx.exported_items.contains(&item.id) {
1640 if cx.tcx.destructor_for_type.borrow().contains_key(&local_def(item.id)) {
1643 let ty = match item.node {
1644 ast::ItemStruct(_, ref ast_generics) => {
1645 if ast_generics.is_parameterized() {
1648 ty::mk_struct(cx.tcx, local_def(item.id),
1649 cx.tcx.mk_substs(Substs::empty()))
1651 ast::ItemEnum(_, ref ast_generics) => {
1652 if ast_generics.is_parameterized() {
1655 ty::mk_enum(cx.tcx, local_def(item.id),
1656 cx.tcx.mk_substs(Substs::empty()))
1660 let parameter_environment = ty::empty_parameter_environment(cx.tcx);
1661 if !ty::type_moves_by_default(¶meter_environment, item.span, ty) {
1664 if ty::can_type_implement_copy(¶meter_environment, item.span, ty).is_ok() {
1665 cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
1667 "type could implement `Copy`; consider adding `impl \
1674 MISSING_DEBUG_IMPLEMENTATIONS,
1676 "detects missing implementations of fmt::Debug"
1679 pub struct MissingDebugImplementations {
1680 impling_types: Option<NodeSet>,
1683 impl MissingDebugImplementations {
1684 pub fn new() -> MissingDebugImplementations {
1685 MissingDebugImplementations {
1686 impling_types: None,
1691 impl LintPass for MissingDebugImplementations {
1692 fn get_lints(&self) -> LintArray {
1693 lint_array!(MISSING_DEBUG_IMPLEMENTATIONS)
1696 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1697 if !cx.exported_items.contains(&item.id) {
1702 ast::ItemStruct(..) | ast::ItemEnum(..) => {},
1706 let debug = match cx.tcx.lang_items.debug_trait() {
1707 Some(debug) => debug,
1711 if self.impling_types.is_none() {
1712 let impls = cx.tcx.trait_impls.borrow();
1713 let impls = match impls.get(&debug) {
1715 impls.borrow().iter()
1716 .filter(|d| d.krate == ast::LOCAL_CRATE)
1717 .filter_map(|d| ty::ty_to_def_id(ty::node_id_to_type(cx.tcx, d.node)))
1723 self.impling_types = Some(impls);
1724 debug!("{:?}", self.impling_types);
1727 if !self.impling_types.as_ref().unwrap().contains(&item.id) {
1728 cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS,
1730 "type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
1731 or a manual implementation")
1739 "detects use of #[deprecated] items"
1742 /// Checks for use of items with `#[deprecated]` attributes
1744 pub struct Stability;
1747 fn lint(&self, cx: &Context, _id: ast::DefId, span: Span, stability: &Option<attr::Stability>) {
1748 // Deprecated attributes apply in-crate and cross-crate.
1749 let (lint, label) = match *stability {
1750 Some(attr::Stability { deprecated_since: Some(_), .. }) =>
1751 (DEPRECATED, "deprecated"),
1755 output(cx, span, stability, lint, label);
1757 fn output(cx: &Context, span: Span, stability: &Option<attr::Stability>,
1758 lint: &'static Lint, label: &'static str) {
1759 let msg = match *stability {
1760 Some(attr::Stability { reason: Some(ref s), .. }) => {
1761 format!("use of {} item: {}", label, *s)
1763 _ => format!("use of {} item", label)
1766 cx.span_lint(lint, span, &msg[..]);
1771 impl LintPass for Stability {
1772 fn get_lints(&self) -> LintArray {
1773 lint_array!(DEPRECATED)
1776 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1777 stability::check_item(cx.tcx, item, false,
1778 &mut |id, sp, stab| self.lint(cx, id, sp, stab));
1781 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1782 stability::check_expr(cx.tcx, e,
1783 &mut |id, sp, stab| self.lint(cx, id, sp, stab));
1786 fn check_path(&mut self, cx: &Context, path: &ast::Path, id: ast::NodeId) {
1787 stability::check_path(cx.tcx, path, id,
1788 &mut |id, sp, stab| self.lint(cx, id, sp, stab));
1791 fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) {
1792 stability::check_pat(cx.tcx, pat,
1793 &mut |id, sp, stab| self.lint(cx, id, sp, stab))
1798 pub UNCONDITIONAL_RECURSION,
1800 "functions that cannot return without calling themselves"
1804 pub struct UnconditionalRecursion;
1807 impl LintPass for UnconditionalRecursion {
1808 fn get_lints(&self) -> LintArray {
1809 lint_array![UNCONDITIONAL_RECURSION]
1812 fn check_fn(&mut self, cx: &Context, fn_kind: visit::FnKind, _: &ast::FnDecl,
1813 blk: &ast::Block, sp: Span, id: ast::NodeId) {
1814 // FIXME(#23542) Replace with type ascription.
1815 #![allow(trivial_casts)]
1817 type F = for<'tcx> fn(&ty::ctxt<'tcx>,
1818 ast::NodeId, ast::NodeId, ast::Ident, ast::NodeId) -> bool;
1820 let (name, checker) = match fn_kind {
1821 visit::FkItemFn(name, _, _, _) => (name, id_refers_to_this_fn as F),
1822 visit::FkMethod(name, _) => (name, id_refers_to_this_method as F),
1823 // closures can't recur, so they don't matter.
1824 visit::FkFnBlock => return
1827 let impl_def_id = ty::impl_of_method(cx.tcx, local_def(id))
1828 .unwrap_or(local_def(ast::DUMMY_NODE_ID));
1829 assert!(ast_util::is_local(impl_def_id));
1830 let impl_node_id = impl_def_id.node;
1832 // Walk through this function (say `f`) looking to see if
1833 // every possible path references itself, i.e. the function is
1834 // called recursively unconditionally. This is done by trying
1835 // to find a path from the entry node to the exit node that
1836 // *doesn't* call `f` by traversing from the entry while
1837 // pretending that calls of `f` are sinks (i.e. ignoring any
1838 // exit edges from them).
1840 // NB. this has an edge case with non-returning statements,
1841 // like `loop {}` or `panic!()`: control flow never reaches
1842 // the exit node through these, so one can have a function
1843 // that never actually calls itselfs but is still picked up by
1846 // fn f(cond: bool) {
1847 // if !cond { panic!() } // could come from `assert!(cond)`
1851 // In general, functions of that form may be able to call
1852 // itself a finite number of times and then diverge. The lint
1853 // considers this to be an error for two reasons, (a) it is
1854 // easier to implement, and (b) it seems rare to actually want
1855 // to have behaviour like the above, rather than
1856 // e.g. accidentally recurring after an assert.
1858 let cfg = cfg::CFG::new(cx.tcx, blk);
1860 let mut work_queue = vec![cfg.entry];
1861 let mut reached_exit_without_self_call = false;
1862 let mut self_call_spans = vec![];
1863 let mut visited = BitSet::new();
1865 while let Some(idx) = work_queue.pop() {
1866 if idx == cfg.exit {
1868 reached_exit_without_self_call = true;
1872 let cfg_id = idx.node_id();
1873 if visited.contains(&cfg_id) {
1877 visited.insert(cfg_id);
1879 let node_id = cfg.graph.node_data(idx).id();
1881 // is this a recursive call?
1882 if node_id != ast::DUMMY_NODE_ID && checker(cx.tcx, impl_node_id, id, name, node_id) {
1883 self_call_spans.push(cx.tcx.map.span(node_id));
1884 // this is a self call, so we shouldn't explore past
1885 // this node in the CFG.
1888 // add the successors of this node to explore the graph further.
1889 cfg.graph.each_outgoing_edge(idx, |_, edge| {
1890 let target_idx = edge.target();
1891 let target_cfg_id = target_idx.node_id();
1892 if !visited.contains(&target_cfg_id) {
1893 work_queue.push(target_idx)
1899 // Check the number of self calls because a function that
1900 // doesn't return (e.g. calls a `-> !` function or `loop { /*
1901 // no break */ }`) shouldn't be linted unless it actually
1903 if !reached_exit_without_self_call && self_call_spans.len() > 0 {
1904 cx.span_lint(UNCONDITIONAL_RECURSION, sp,
1905 "function cannot return without recurring");
1907 // FIXME #19668: these could be span_lint_note's instead of this manual guard.
1908 if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow {
1909 let sess = cx.sess();
1910 // offer some help to the programmer.
1911 for call in &self_call_spans {
1912 sess.span_note(*call, "recursive call site")
1914 sess.fileline_help(sp, "a `loop` may express intention \
1915 better if this is on purpose")
1922 // Functions for identifying if the given NodeId `id`
1923 // represents a call to the function `fn_id`/method
1926 fn id_refers_to_this_fn<'tcx>(tcx: &ty::ctxt<'tcx>,
1930 id: ast::NodeId) -> bool {
1931 tcx.def_map.borrow().get(&id)
1932 .map_or(false, |def| def.def_id() == local_def(fn_id))
1935 // check if the method call `id` refers to method `method_id`
1936 // (with name `method_name` contained in impl `impl_id`).
1937 fn id_refers_to_this_method<'tcx>(tcx: &ty::ctxt<'tcx>,
1938 impl_id: ast::NodeId,
1939 method_id: ast::NodeId,
1940 method_name: ast::Ident,
1941 id: ast::NodeId) -> bool {
1942 let did = match tcx.method_map.borrow().get(&ty::MethodCall::expr(id)) {
1943 None => return false,
1944 Some(m) => match m.origin {
1945 // There's no way to know if a method call via a
1946 // vtable is recursion, so we assume it's not.
1947 ty::MethodTraitObject(_) => return false,
1949 // This `did` refers directly to the method definition.
1950 ty::MethodStatic(did) | ty::MethodStaticClosure(did) => did,
1952 // MethodTypeParam are methods from traits:
1954 // The `impl ... for ...` of this method call
1955 // isn't known, e.g. it might be a default method
1956 // in a trait, so we get the def-id of the trait
1958 ty::MethodTypeParam(
1959 ty::MethodParam { ref trait_ref, method_num, impl_def_id: None, }) => {
1960 ty::trait_item(tcx, trait_ref.def_id, method_num).def_id()
1963 // The `impl` is known, so we check that with a
1965 ty::MethodTypeParam(
1966 ty::MethodParam { impl_def_id: Some(impl_def_id), .. }) => {
1968 let name = match tcx.map.expect_expr(id).node {
1969 ast::ExprMethodCall(ref sp_ident, _, _) => sp_ident.node,
1970 _ => tcx.sess.span_bug(
1972 "non-method call expr behaving like a method call?")
1974 // It matches if it comes from the same impl,
1975 // and has the same method name.
1976 return ast_util::is_local(impl_def_id)
1977 && impl_def_id.node == impl_id
1978 && method_name.name == name.name
1983 ast_util::is_local(did) && did.node == method_id
1991 "compiler plugin used as ordinary library in non-plugin crate"
1995 pub struct PluginAsLibrary;
1997 impl LintPass for PluginAsLibrary {
1998 fn get_lints(&self) -> LintArray {
1999 lint_array![PLUGIN_AS_LIBRARY]
2002 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
2003 if cx.sess().plugin_registrar_fn.get().is_some() {
2004 // We're compiling a plugin; it's fine to link other plugins.
2009 ast::ItemExternCrate(..) => (),
2013 let md = match cx.sess().cstore.find_extern_mod_stmt_cnum(it.id) {
2014 Some(cnum) => cx.sess().cstore.get_crate_data(cnum),
2016 // Probably means we aren't linking the crate for some reason.
2018 // Not sure if / when this could happen.
2023 if decoder::get_plugin_registrar_fn(md.data()).is_some() {
2024 cx.span_lint(PLUGIN_AS_LIBRARY, it.span,
2025 "compiler plugin used as an ordinary library");
2031 PRIVATE_NO_MANGLE_FNS,
2033 "functions marked #[no_mangle] should be exported"
2037 PRIVATE_NO_MANGLE_STATICS,
2039 "statics marked #[no_mangle] should be exported"
2043 NO_MANGLE_CONST_ITEMS,
2045 "const items will not have their symbols exported"
2049 pub struct InvalidNoMangleItems;
2051 impl LintPass for InvalidNoMangleItems {
2052 fn get_lints(&self) -> LintArray {
2053 lint_array!(PRIVATE_NO_MANGLE_FNS,
2054 PRIVATE_NO_MANGLE_STATICS,
2055 NO_MANGLE_CONST_ITEMS)
2058 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
2060 ast::ItemFn(..) => {
2061 if attr::contains_name(&it.attrs, "no_mangle") &&
2062 !cx.exported_items.contains(&it.id) {
2063 let msg = format!("function {} is marked #[no_mangle], but not exported",
2065 cx.span_lint(PRIVATE_NO_MANGLE_FNS, it.span, &msg);
2068 ast::ItemStatic(..) => {
2069 if attr::contains_name(&it.attrs, "no_mangle") &&
2070 !cx.exported_items.contains(&it.id) {
2071 let msg = format!("static {} is marked #[no_mangle], but not exported",
2073 cx.span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, &msg);
2076 ast::ItemConst(..) => {
2077 if attr::contains_name(&it.attrs, "no_mangle") {
2078 // Const items do not refer to a particular location in memory, and therefore
2079 // don't have anything to attach a symbol to
2080 let msg = "const items should never be #[no_mangle], consider instead using \
2082 cx.span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
2090 /// Forbids using the `#[feature(...)]` attribute
2092 pub struct UnstableFeatures;
2097 "enabling unstable features"
2100 impl LintPass for UnstableFeatures {
2101 fn get_lints(&self) -> LintArray {
2102 lint_array!(UNSTABLE_FEATURES)
2104 fn check_attribute(&mut self, ctx: &Context, attr: &ast::Attribute) {
2105 if attr::contains_name(&[attr.node.value.clone()], "feature") {
2106 ctx.span_lint(UNSTABLE_FEATURES, attr.span, "unstable feature");