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 { }`"
66 #[derive(Copy, Clone)]
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"
110 #[derive(Copy, Clone)]
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 && min != i64::MIN && v > -min as u64) ||
210 (!negative && v > max 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);
434 #[derive(Copy, Clone)]
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"
477 #[derive(Copy, Clone)]
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"
624 #[derive(Copy, Clone)]
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"
665 #[derive(Copy, Clone)]
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"
699 #[derive(Copy, Clone)]
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"
767 #[derive(Copy, Clone)]
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.is_empty() && !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"
877 #[derive(Copy, Clone)]
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, name: &str, span: Option<Span>) {
914 fn is_snake_case(ident: &str) -> bool {
915 if ident.is_empty() {
918 let ident = ident.trim_left_matches('\'');
919 let ident = ident.trim_matches('_');
921 let mut allow_underscore = true;
922 ident.chars().all(|c| {
923 allow_underscore = match c {
924 '_' if !allow_underscore => return false,
926 // It would be more obvious to use `c.is_lowercase()`,
927 // but some characters do not have a lowercase form
928 c if !c.is_uppercase() => true,
935 if !is_snake_case(name) {
936 let sc = NonSnakeCase::to_snake_case(name);
937 let msg = if sc != name {
938 format!("{} `{}` should have a snake case name such as `{}`",
941 format!("{} `{}` should have a snake case name",
945 Some(span) => cx.span_lint(NON_SNAKE_CASE, span, &msg),
946 None => cx.lint(NON_SNAKE_CASE, &msg),
952 impl LintPass for NonSnakeCase {
953 fn get_lints(&self) -> LintArray {
954 lint_array!(NON_SNAKE_CASE)
957 fn check_crate(&mut self, cx: &Context, cr: &ast::Crate) {
958 let attr_crate_name = cr.attrs.iter().find(|at| at.check_name("crate_name"))
959 .and_then(|at| at.value_str().map(|s| (at, s)));
960 if let Some(ref name) = cx.tcx.sess.opts.crate_name {
961 self.check_snake_case(cx, "crate", name, None);
962 } else if let Some((attr, ref name)) = attr_crate_name {
963 self.check_snake_case(cx, "crate", name, Some(attr.span));
967 fn check_fn(&mut self, cx: &Context,
968 fk: visit::FnKind, _: &ast::FnDecl,
969 _: &ast::Block, span: Span, id: ast::NodeId) {
971 visit::FkMethod(ident, _, _) => match method_context(cx, id, span) {
972 MethodContext::PlainImpl => {
973 self.check_snake_case(cx, "method", &token::get_ident(ident), Some(span))
975 MethodContext::TraitDefaultImpl => {
976 self.check_snake_case(cx, "trait method", &token::get_ident(ident), Some(span))
980 visit::FkItemFn(ident, _, _, _, _) => {
981 self.check_snake_case(cx, "function", &token::get_ident(ident), Some(span))
987 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
988 if let ast::ItemMod(_) = it.node {
989 self.check_snake_case(cx, "module", &token::get_ident(it.ident), Some(it.span));
993 fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) {
994 if let ast::MethodTraitItem(_, None) = trait_item.node {
995 self.check_snake_case(cx, "trait method", &token::get_ident(trait_item.ident),
996 Some(trait_item.span));
1000 fn check_lifetime_def(&mut self, cx: &Context, t: &ast::LifetimeDef) {
1001 self.check_snake_case(cx, "lifetime", &token::get_ident(t.lifetime.name.ident()),
1002 Some(t.lifetime.span));
1005 fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
1006 if let &ast::PatIdent(_, ref path1, _) = &p.node {
1007 let def = cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def());
1008 if let Some(def::DefLocal(_)) = def {
1009 self.check_snake_case(cx, "variable", &token::get_ident(path1.node), Some(p.span));
1014 fn check_struct_def(&mut self, cx: &Context, s: &ast::StructDef,
1015 _: ast::Ident, _: &ast::Generics, _: ast::NodeId) {
1016 for sf in &s.fields {
1017 if let ast::StructField_ { kind: ast::NamedField(ident, _), .. } = sf.node {
1018 self.check_snake_case(cx, "structure field", &token::get_ident(ident),
1026 pub NON_UPPER_CASE_GLOBALS,
1028 "static constants should have uppercase identifiers"
1031 #[derive(Copy, Clone)]
1032 pub struct NonUpperCaseGlobals;
1034 impl NonUpperCaseGlobals {
1035 fn check_upper_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
1036 let s = token::get_ident(ident);
1038 if s.chars().any(|c| c.is_lowercase()) {
1039 let uc = NonSnakeCase::to_snake_case(&s).to_uppercase();
1041 cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
1042 &format!("{} `{}` should have an upper case name such as `{}`",
1045 cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
1046 &format!("{} `{}` should have an upper case name",
1053 impl LintPass for NonUpperCaseGlobals {
1054 fn get_lints(&self) -> LintArray {
1055 lint_array!(NON_UPPER_CASE_GLOBALS)
1058 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
1060 // only check static constants
1061 ast::ItemStatic(_, ast::MutImmutable, _) => {
1062 NonUpperCaseGlobals::check_upper_case(cx, "static constant", it.ident, it.span);
1064 ast::ItemConst(..) => {
1065 NonUpperCaseGlobals::check_upper_case(cx, "constant", it.ident, it.span);
1071 fn check_trait_item(&mut self, cx: &Context, ti: &ast::TraitItem) {
1073 ast::ConstTraitItem(..) => {
1074 NonUpperCaseGlobals::check_upper_case(cx, "associated constant",
1081 fn check_impl_item(&mut self, cx: &Context, ii: &ast::ImplItem) {
1083 ast::ConstImplItem(..) => {
1084 NonUpperCaseGlobals::check_upper_case(cx, "associated constant",
1091 fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
1092 // Lint for constants that look like binding identifiers (#7526)
1093 match (&p.node, cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def())) {
1094 (&ast::PatIdent(_, ref path1, _), Some(def::DefConst(..))) => {
1095 NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern",
1096 path1.node, p.span);
1106 "`if`, `match`, `while` and `return` do not need parentheses"
1109 #[derive(Copy, Clone)]
1110 pub struct UnusedParens;
1113 fn check_unused_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str,
1114 struct_lit_needs_parens: bool) {
1115 if let ast::ExprParen(ref inner) = value.node {
1116 let necessary = struct_lit_needs_parens && contains_exterior_struct_lit(&**inner);
1118 cx.span_lint(UNUSED_PARENS, value.span,
1119 &format!("unnecessary parentheses around {}", msg))
1123 /// Expressions that syntactically contain an "exterior" struct
1124 /// literal i.e. not surrounded by any parens or other
1125 /// delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo
1126 /// == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X {
1127 /// y: 1 }) == foo` does not.
1128 fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
1130 ast::ExprStruct(..) => true,
1132 ast::ExprAssign(ref lhs, ref rhs) |
1133 ast::ExprAssignOp(_, ref lhs, ref rhs) |
1134 ast::ExprBinary(_, ref lhs, ref rhs) => {
1135 // X { y: 1 } + X { y: 2 }
1136 contains_exterior_struct_lit(&**lhs) ||
1137 contains_exterior_struct_lit(&**rhs)
1139 ast::ExprUnary(_, ref x) |
1140 ast::ExprCast(ref x, _) |
1141 ast::ExprField(ref x, _) |
1142 ast::ExprTupField(ref x, _) |
1143 ast::ExprIndex(ref x, _) => {
1144 // &X { y: 1 }, X { y: 1 }.y
1145 contains_exterior_struct_lit(&**x)
1148 ast::ExprMethodCall(_, _, ref exprs) => {
1149 // X { y: 1 }.bar(...)
1150 contains_exterior_struct_lit(&*exprs[0])
1159 impl LintPass for UnusedParens {
1160 fn get_lints(&self) -> LintArray {
1161 lint_array!(UNUSED_PARENS)
1164 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1165 let (value, msg, struct_lit_needs_parens) = match e.node {
1166 ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true),
1167 ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true),
1168 ast::ExprMatch(ref head, _, source) => match source {
1169 ast::MatchSource::Normal => (head, "`match` head expression", true),
1170 ast::MatchSource::IfLetDesugar { .. } => (head, "`if let` head expression", true),
1171 ast::MatchSource::WhileLetDesugar => (head, "`while let` head expression", true),
1172 ast::MatchSource::ForLoopDesugar => (head, "`for` head expression", true),
1174 ast::ExprRet(Some(ref value)) => (value, "`return` value", false),
1175 ast::ExprAssign(_, ref value) => (value, "assigned value", false),
1176 ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false),
1179 self.check_unused_parens_core(cx, &**value, msg, struct_lit_needs_parens);
1182 fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
1183 let (value, msg) = match s.node {
1184 ast::StmtDecl(ref decl, _) => match decl.node {
1185 ast::DeclLocal(ref local) => match local.init {
1186 Some(ref value) => (value, "assigned value"),
1193 self.check_unused_parens_core(cx, &**value, msg, false);
1198 UNUSED_IMPORT_BRACES,
1200 "unnecessary braces around an imported item"
1203 #[derive(Copy, Clone)]
1204 pub struct UnusedImportBraces;
1206 impl LintPass for UnusedImportBraces {
1207 fn get_lints(&self) -> LintArray {
1208 lint_array!(UNUSED_IMPORT_BRACES)
1211 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1212 if let ast::ItemUse(ref view_path) = item.node {
1213 if let ast::ViewPathList(_, ref items) = view_path.node {
1214 if items.len() == 1 {
1215 if let ast::PathListIdent {ref name, ..} = items[0].node {
1216 let m = format!("braces around {} is unnecessary",
1217 &token::get_ident(*name));
1218 cx.span_lint(UNUSED_IMPORT_BRACES, item.span,
1228 NON_SHORTHAND_FIELD_PATTERNS,
1230 "using `Struct { x: x }` instead of `Struct { x }`"
1233 #[derive(Copy, Clone)]
1234 pub struct NonShorthandFieldPatterns;
1236 impl LintPass for NonShorthandFieldPatterns {
1237 fn get_lints(&self) -> LintArray {
1238 lint_array!(NON_SHORTHAND_FIELD_PATTERNS)
1241 fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) {
1242 let def_map = cx.tcx.def_map.borrow();
1243 if let ast::PatStruct(_, ref v, _) = pat.node {
1244 let field_pats = v.iter().filter(|fieldpat| {
1245 if fieldpat.node.is_shorthand {
1248 let def = def_map.get(&fieldpat.node.pat.id).map(|d| d.full_def());
1249 def == Some(def::DefLocal(fieldpat.node.pat.id))
1251 for fieldpat in field_pats {
1252 if let ast::PatIdent(_, ident, None) = fieldpat.node.pat.node {
1253 if ident.node.as_str() == fieldpat.node.ident.as_str() {
1254 cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span,
1255 &format!("the `{}:` in this pattern is redundant and can \
1256 be removed", ident.node.as_str()))
1267 "unnecessary use of an `unsafe` block"
1270 #[derive(Copy, Clone)]
1271 pub struct UnusedUnsafe;
1273 impl LintPass for UnusedUnsafe {
1274 fn get_lints(&self) -> LintArray {
1275 lint_array!(UNUSED_UNSAFE)
1278 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1279 if let ast::ExprBlock(ref blk) = e.node {
1280 // Don't warn about generated blocks, that'll just pollute the output.
1281 if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
1282 !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
1283 cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block");
1292 "usage of `unsafe` code"
1295 #[derive(Copy, Clone)]
1296 pub struct UnsafeCode;
1298 impl LintPass for UnsafeCode {
1299 fn get_lints(&self) -> LintArray {
1300 lint_array!(UNSAFE_CODE)
1303 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1304 if let ast::ExprBlock(ref blk) = e.node {
1305 // Don't warn about generated blocks, that'll just pollute the output.
1306 if blk.rules == ast::UnsafeBlock(ast::UserProvided) {
1307 cx.span_lint(UNSAFE_CODE, blk.span, "usage of an `unsafe` block");
1312 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
1314 ast::ItemTrait(ast::Unsafety::Unsafe, _, _, _) =>
1315 cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait"),
1317 ast::ItemImpl(ast::Unsafety::Unsafe, _, _, _, _, _) =>
1318 cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait"),
1324 fn check_fn(&mut self, cx: &Context, fk: visit::FnKind, _: &ast::FnDecl,
1325 _: &ast::Block, span: Span, _: ast::NodeId) {
1327 visit::FkItemFn(_, _, ast::Unsafety::Unsafe, _, _) =>
1328 cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function"),
1330 visit::FkMethod(_, sig, _) => {
1331 if sig.unsafety == ast::Unsafety::Unsafe {
1332 cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method")
1340 fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) {
1341 if let ast::MethodTraitItem(ref sig, None) = trait_item.node {
1342 if sig.unsafety == ast::Unsafety::Unsafe {
1343 cx.span_lint(UNSAFE_CODE, trait_item.span,
1344 "declaration of an `unsafe` method")
1353 "detect mut variables which don't need to be mutable"
1356 #[derive(Copy, Clone)]
1357 pub struct UnusedMut;
1360 fn check_unused_mut_pat(&self, cx: &Context, pats: &[P<ast::Pat>]) {
1361 // collect all mutable pattern and group their NodeIDs by their Identifier to
1362 // avoid false warnings in match arms with multiple patterns
1364 let mut mutables = FnvHashMap();
1366 pat_util::pat_bindings(&cx.tcx.def_map, &**p, |mode, id, _, path1| {
1367 let ident = path1.node;
1368 if let ast::BindByValue(ast::MutMutable) = mode {
1369 if !token::get_ident(ident).starts_with("_") {
1370 match mutables.entry(ident.name.usize()) {
1371 Vacant(entry) => { entry.insert(vec![id]); },
1372 Occupied(mut entry) => { entry.get_mut().push(id); },
1379 let used_mutables = cx.tcx.used_mut_nodes.borrow();
1380 for (_, v) in &mutables {
1381 if !v.iter().any(|e| used_mutables.contains(e)) {
1382 cx.span_lint(UNUSED_MUT, cx.tcx.map.span(v[0]),
1383 "variable does not need to be mutable");
1389 impl LintPass for UnusedMut {
1390 fn get_lints(&self) -> LintArray {
1391 lint_array!(UNUSED_MUT)
1394 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1395 if let ast::ExprMatch(_, ref arms, _) = e.node {
1397 self.check_unused_mut_pat(cx, &a.pats)
1402 fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
1403 if let ast::StmtDecl(ref d, _) = s.node {
1404 if let ast::DeclLocal(ref l) = d.node {
1405 self.check_unused_mut_pat(cx, slice::ref_slice(&l.pat));
1410 fn check_fn(&mut self, cx: &Context,
1411 _: visit::FnKind, decl: &ast::FnDecl,
1412 _: &ast::Block, _: Span, _: ast::NodeId) {
1413 for a in &decl.inputs {
1414 self.check_unused_mut_pat(cx, slice::ref_slice(&a.pat));
1422 "detects unnecessary allocations that can be eliminated"
1425 #[derive(Copy, Clone)]
1426 pub struct UnusedAllocation;
1428 impl LintPass for UnusedAllocation {
1429 fn get_lints(&self) -> LintArray {
1430 lint_array!(UNUSED_ALLOCATION)
1433 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1435 ast::ExprUnary(ast::UnUniq, _) => (),
1439 if let Some(adjustment) = cx.tcx.adjustments.borrow().get(&e.id) {
1440 if let ty::AdjustDerefRef(ty::AutoDerefRef { ref autoref, .. }) = *adjustment {
1442 &Some(ty::AutoPtr(_, ast::MutImmutable)) => {
1443 cx.span_lint(UNUSED_ALLOCATION, e.span,
1444 "unnecessary allocation, use & instead");
1446 &Some(ty::AutoPtr(_, ast::MutMutable)) => {
1447 cx.span_lint(UNUSED_ALLOCATION, e.span,
1448 "unnecessary allocation, use &mut instead");
1460 "detects missing documentation for public members"
1463 pub struct MissingDoc {
1464 /// Stack of IDs of struct definitions.
1465 struct_def_stack: Vec<ast::NodeId>,
1467 /// True if inside variant definition
1470 /// Stack of whether #[doc(hidden)] is set
1471 /// at each level which has lint attributes.
1472 doc_hidden_stack: Vec<bool>,
1474 /// Private traits or trait items that leaked through. Don't check their methods.
1475 private_traits: HashSet<ast::NodeId>,
1479 pub fn new() -> MissingDoc {
1481 struct_def_stack: vec!(),
1483 doc_hidden_stack: vec!(false),
1484 private_traits: HashSet::new(),
1488 fn doc_hidden(&self) -> bool {
1489 *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
1492 fn check_missing_docs_attrs(&self,
1494 id: Option<ast::NodeId>,
1495 attrs: &[ast::Attribute],
1497 desc: &'static str) {
1498 // If we're building a test harness, then warning about
1499 // documentation is probably not really relevant right now.
1500 if cx.sess().opts.test {
1504 // `#[doc(hidden)]` disables missing_docs check.
1505 if self.doc_hidden() {
1509 // Only check publicly-visible items, using the result from the privacy pass.
1510 // It's an option so the crate root can also use this function (it doesn't
1512 if let Some(ref id) = id {
1513 if !cx.exported_items.contains(id) {
1518 let has_doc = attrs.iter().any(|a| {
1519 match a.node.value.node {
1520 ast::MetaNameValue(ref name, _) if *name == "doc" => true,
1525 cx.span_lint(MISSING_DOCS, sp,
1526 &format!("missing documentation for {}", desc));
1531 impl LintPass for MissingDoc {
1532 fn get_lints(&self) -> LintArray {
1533 lint_array!(MISSING_DOCS)
1536 fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) {
1537 let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
1538 attr.check_name("doc") && match attr.meta_item_list() {
1540 Some(l) => attr::contains_name(&l[..], "hidden"),
1543 self.doc_hidden_stack.push(doc_hidden);
1546 fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) {
1547 self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
1550 fn check_struct_def(&mut self, _: &Context, _: &ast::StructDef,
1551 _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
1552 self.struct_def_stack.push(id);
1555 fn check_struct_def_post(&mut self, _: &Context, _: &ast::StructDef,
1556 _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
1557 let popped = self.struct_def_stack.pop().expect("empty struct_def_stack");
1558 assert!(popped == id);
1561 fn check_crate(&mut self, cx: &Context, krate: &ast::Crate) {
1562 self.check_missing_docs_attrs(cx, None, &krate.attrs, krate.span, "crate");
1565 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
1566 let desc = match it.node {
1567 ast::ItemFn(..) => "a function",
1568 ast::ItemMod(..) => "a module",
1569 ast::ItemEnum(..) => "an enum",
1570 ast::ItemStruct(..) => "a struct",
1571 ast::ItemTrait(_, _, _, ref items) => {
1572 // Issue #11592, traits are always considered exported, even when private.
1573 if it.vis == ast::Visibility::Inherited {
1574 self.private_traits.insert(it.id);
1576 self.private_traits.insert(itm.id);
1582 ast::ItemTy(..) => "a type alias",
1583 ast::ItemImpl(_, _, _, Some(ref trait_ref), _, ref impl_items) => {
1584 // If the trait is private, add the impl items to private_traits so they don't get
1585 // reported for missing docs.
1586 let real_trait = ty::trait_ref_to_def_id(cx.tcx, trait_ref);
1587 match cx.tcx.map.find(real_trait.node) {
1588 Some(ast_map::NodeItem(item)) => if item.vis == ast::Visibility::Inherited {
1589 for itm in impl_items {
1590 self.private_traits.insert(itm.id);
1600 self.check_missing_docs_attrs(cx, Some(it.id), &it.attrs, it.span, desc);
1603 fn check_trait_item(&mut self, cx: &Context, trait_item: &ast::TraitItem) {
1604 if self.private_traits.contains(&trait_item.id) { return }
1606 let desc = match trait_item.node {
1607 ast::ConstTraitItem(..) => "an associated constant",
1608 ast::MethodTraitItem(..) => "a trait method",
1609 ast::TypeTraitItem(..) => "an associated type",
1612 self.check_missing_docs_attrs(cx, Some(trait_item.id),
1614 trait_item.span, desc);
1617 fn check_impl_item(&mut self, cx: &Context, impl_item: &ast::ImplItem) {
1618 // If the method is an impl for a trait, don't doc.
1619 if method_context(cx, impl_item.id, impl_item.span) == MethodContext::TraitImpl {
1623 let desc = match impl_item.node {
1624 ast::ConstImplItem(..) => "an associated constant",
1625 ast::MethodImplItem(..) => "a method",
1626 ast::TypeImplItem(_) => "an associated type",
1627 ast::MacImplItem(_) => "an impl item macro",
1629 self.check_missing_docs_attrs(cx, Some(impl_item.id),
1631 impl_item.span, desc);
1634 fn check_struct_field(&mut self, cx: &Context, sf: &ast::StructField) {
1635 if let ast::NamedField(_, vis) = sf.node.kind {
1636 if vis == ast::Public || self.in_variant {
1637 let cur_struct_def = *self.struct_def_stack.last()
1638 .expect("empty struct_def_stack");
1639 self.check_missing_docs_attrs(cx, Some(cur_struct_def),
1640 &sf.node.attrs, sf.span,
1646 fn check_variant(&mut self, cx: &Context, v: &ast::Variant, _: &ast::Generics) {
1647 self.check_missing_docs_attrs(cx, Some(v.node.id), &v.node.attrs, v.span, "a variant");
1648 assert!(!self.in_variant);
1649 self.in_variant = true;
1652 fn check_variant_post(&mut self, _: &Context, _: &ast::Variant, _: &ast::Generics) {
1653 assert!(self.in_variant);
1654 self.in_variant = false;
1659 pub MISSING_COPY_IMPLEMENTATIONS,
1661 "detects potentially-forgotten implementations of `Copy`"
1664 #[derive(Copy, Clone)]
1665 pub struct MissingCopyImplementations;
1667 impl LintPass for MissingCopyImplementations {
1668 fn get_lints(&self) -> LintArray {
1669 lint_array!(MISSING_COPY_IMPLEMENTATIONS)
1672 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1673 if !cx.exported_items.contains(&item.id) {
1676 if cx.tcx.destructor_for_type.borrow().contains_key(&local_def(item.id)) {
1679 let ty = match item.node {
1680 ast::ItemStruct(_, ref ast_generics) => {
1681 if ast_generics.is_parameterized() {
1684 ty::mk_struct(cx.tcx, local_def(item.id),
1685 cx.tcx.mk_substs(Substs::empty()))
1687 ast::ItemEnum(_, ref ast_generics) => {
1688 if ast_generics.is_parameterized() {
1691 ty::mk_enum(cx.tcx, local_def(item.id),
1692 cx.tcx.mk_substs(Substs::empty()))
1696 let parameter_environment = ty::empty_parameter_environment(cx.tcx);
1697 if !ty::type_moves_by_default(¶meter_environment, item.span, ty) {
1700 if ty::can_type_implement_copy(¶meter_environment, item.span, ty).is_ok() {
1701 cx.span_lint(MISSING_COPY_IMPLEMENTATIONS,
1703 "type could implement `Copy`; consider adding `impl \
1710 MISSING_DEBUG_IMPLEMENTATIONS,
1712 "detects missing implementations of fmt::Debug"
1715 pub struct MissingDebugImplementations {
1716 impling_types: Option<NodeSet>,
1719 impl MissingDebugImplementations {
1720 pub fn new() -> MissingDebugImplementations {
1721 MissingDebugImplementations {
1722 impling_types: None,
1727 impl LintPass for MissingDebugImplementations {
1728 fn get_lints(&self) -> LintArray {
1729 lint_array!(MISSING_DEBUG_IMPLEMENTATIONS)
1732 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1733 if !cx.exported_items.contains(&item.id) {
1738 ast::ItemStruct(..) | ast::ItemEnum(..) => {},
1742 let debug = match cx.tcx.lang_items.debug_trait() {
1743 Some(debug) => debug,
1747 if self.impling_types.is_none() {
1748 let debug_def = ty::lookup_trait_def(cx.tcx, debug);
1749 let mut impls = NodeSet();
1750 debug_def.for_each_impl(cx.tcx, |d| {
1751 if d.krate == ast::LOCAL_CRATE {
1752 if let Some(ty_def) = ty::ty_to_def_id(ty::node_id_to_type(cx.tcx, d.node)) {
1753 impls.insert(ty_def.node);
1758 self.impling_types = Some(impls);
1759 debug!("{:?}", self.impling_types);
1762 if !self.impling_types.as_ref().unwrap().contains(&item.id) {
1763 cx.span_lint(MISSING_DEBUG_IMPLEMENTATIONS,
1765 "type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
1766 or a manual implementation")
1774 "detects use of #[deprecated] items"
1777 /// Checks for use of items with `#[deprecated]` attributes
1778 #[derive(Copy, Clone)]
1779 pub struct Stability;
1782 fn lint(&self, cx: &Context, _id: ast::DefId, span: Span, stability: &Option<attr::Stability>) {
1783 // Deprecated attributes apply in-crate and cross-crate.
1784 let (lint, label) = match *stability {
1785 Some(attr::Stability { deprecated_since: Some(_), .. }) =>
1786 (DEPRECATED, "deprecated"),
1790 output(cx, span, stability, lint, label);
1792 fn output(cx: &Context, span: Span, stability: &Option<attr::Stability>,
1793 lint: &'static Lint, label: &'static str) {
1794 let msg = match *stability {
1795 Some(attr::Stability { reason: Some(ref s), .. }) => {
1796 format!("use of {} item: {}", label, *s)
1798 _ => format!("use of {} item", label)
1801 cx.span_lint(lint, span, &msg[..]);
1806 impl LintPass for Stability {
1807 fn get_lints(&self) -> LintArray {
1808 lint_array!(DEPRECATED)
1811 fn check_item(&mut self, cx: &Context, item: &ast::Item) {
1812 stability::check_item(cx.tcx, item, false,
1813 &mut |id, sp, stab| self.lint(cx, id, sp, stab));
1816 fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1817 stability::check_expr(cx.tcx, e,
1818 &mut |id, sp, stab| self.lint(cx, id, sp, stab));
1821 fn check_path(&mut self, cx: &Context, path: &ast::Path, id: ast::NodeId) {
1822 stability::check_path(cx.tcx, path, id,
1823 &mut |id, sp, stab| self.lint(cx, id, sp, stab));
1826 fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) {
1827 stability::check_pat(cx.tcx, pat,
1828 &mut |id, sp, stab| self.lint(cx, id, sp, stab))
1833 pub UNCONDITIONAL_RECURSION,
1835 "functions that cannot return without calling themselves"
1838 #[derive(Copy, Clone)]
1839 pub struct UnconditionalRecursion;
1842 impl LintPass for UnconditionalRecursion {
1843 fn get_lints(&self) -> LintArray {
1844 lint_array![UNCONDITIONAL_RECURSION]
1847 fn check_fn(&mut self, cx: &Context, fn_kind: visit::FnKind, _: &ast::FnDecl,
1848 blk: &ast::Block, sp: Span, id: ast::NodeId) {
1849 // FIXME(#23542) Replace with type ascription.
1850 #![allow(trivial_casts)]
1852 type F = for<'tcx> fn(&ty::ctxt<'tcx>,
1853 ast::NodeId, ast::NodeId, ast::Ident, ast::NodeId) -> bool;
1855 let (name, checker) = match fn_kind {
1856 visit::FkItemFn(name, _, _, _, _) => (name, id_refers_to_this_fn as F),
1857 visit::FkMethod(name, _, _) => (name, id_refers_to_this_method as F),
1858 // closures can't recur, so they don't matter.
1859 visit::FkFnBlock => return
1862 let impl_def_id = ty::impl_of_method(cx.tcx, local_def(id))
1863 .unwrap_or(local_def(ast::DUMMY_NODE_ID));
1864 assert!(ast_util::is_local(impl_def_id));
1865 let impl_node_id = impl_def_id.node;
1867 // Walk through this function (say `f`) looking to see if
1868 // every possible path references itself, i.e. the function is
1869 // called recursively unconditionally. This is done by trying
1870 // to find a path from the entry node to the exit node that
1871 // *doesn't* call `f` by traversing from the entry while
1872 // pretending that calls of `f` are sinks (i.e. ignoring any
1873 // exit edges from them).
1875 // NB. this has an edge case with non-returning statements,
1876 // like `loop {}` or `panic!()`: control flow never reaches
1877 // the exit node through these, so one can have a function
1878 // that never actually calls itselfs but is still picked up by
1881 // fn f(cond: bool) {
1882 // if !cond { panic!() } // could come from `assert!(cond)`
1886 // In general, functions of that form may be able to call
1887 // itself a finite number of times and then diverge. The lint
1888 // considers this to be an error for two reasons, (a) it is
1889 // easier to implement, and (b) it seems rare to actually want
1890 // to have behaviour like the above, rather than
1891 // e.g. accidentally recurring after an assert.
1893 let cfg = cfg::CFG::new(cx.tcx, blk);
1895 let mut work_queue = vec![cfg.entry];
1896 let mut reached_exit_without_self_call = false;
1897 let mut self_call_spans = vec![];
1898 let mut visited = BitSet::new();
1900 while let Some(idx) = work_queue.pop() {
1901 if idx == cfg.exit {
1903 reached_exit_without_self_call = true;
1907 let cfg_id = idx.node_id();
1908 if visited.contains(&cfg_id) {
1912 visited.insert(cfg_id);
1914 let node_id = cfg.graph.node_data(idx).id();
1916 // is this a recursive call?
1917 if node_id != ast::DUMMY_NODE_ID && checker(cx.tcx, impl_node_id, id, name, node_id) {
1918 self_call_spans.push(cx.tcx.map.span(node_id));
1919 // this is a self call, so we shouldn't explore past
1920 // this node in the CFG.
1923 // add the successors of this node to explore the graph further.
1924 for (_, edge) in cfg.graph.outgoing_edges(idx) {
1925 let target_idx = edge.target();
1926 let target_cfg_id = target_idx.node_id();
1927 if !visited.contains(&target_cfg_id) {
1928 work_queue.push(target_idx)
1933 // Check the number of self calls because a function that
1934 // doesn't return (e.g. calls a `-> !` function or `loop { /*
1935 // no break */ }`) shouldn't be linted unless it actually
1937 if !reached_exit_without_self_call && !self_call_spans.is_empty() {
1938 cx.span_lint(UNCONDITIONAL_RECURSION, sp,
1939 "function cannot return without recurring");
1941 // FIXME #19668: these could be span_lint_note's instead of this manual guard.
1942 if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow {
1943 let sess = cx.sess();
1944 // offer some help to the programmer.
1945 for call in &self_call_spans {
1946 sess.span_note(*call, "recursive call site")
1948 sess.fileline_help(sp, "a `loop` may express intention \
1949 better if this is on purpose")
1956 // Functions for identifying if the given NodeId `id`
1957 // represents a call to the function `fn_id`/method
1960 fn id_refers_to_this_fn<'tcx>(tcx: &ty::ctxt<'tcx>,
1964 id: ast::NodeId) -> bool {
1965 tcx.def_map.borrow().get(&id)
1966 .map_or(false, |def| def.def_id() == local_def(fn_id))
1969 // check if the method call `id` refers to method `method_id`
1970 // (with name `method_name` contained in impl `impl_id`).
1971 fn id_refers_to_this_method<'tcx>(tcx: &ty::ctxt<'tcx>,
1972 impl_id: ast::NodeId,
1973 method_id: ast::NodeId,
1974 method_name: ast::Ident,
1975 id: ast::NodeId) -> bool {
1976 let did = match tcx.method_map.borrow().get(&ty::MethodCall::expr(id)) {
1977 None => return false,
1978 Some(m) => match m.origin {
1979 // There's no way to know if a method call via a
1980 // vtable is recursion, so we assume it's not.
1981 ty::MethodTraitObject(_) => return false,
1983 // This `did` refers directly to the method definition.
1984 ty::MethodStatic(did) | ty::MethodStaticClosure(did) => did,
1986 // MethodTypeParam are methods from traits:
1988 // The `impl ... for ...` of this method call
1989 // isn't known, e.g. it might be a default method
1990 // in a trait, so we get the def-id of the trait
1992 ty::MethodTypeParam(
1993 ty::MethodParam { ref trait_ref, method_num, impl_def_id: None, }) => {
1994 ty::trait_item(tcx, trait_ref.def_id, method_num).def_id()
1997 // The `impl` is known, so we check that with a
1999 ty::MethodTypeParam(
2000 ty::MethodParam { impl_def_id: Some(impl_def_id), .. }) => {
2002 let name = match tcx.map.expect_expr(id).node {
2003 ast::ExprMethodCall(ref sp_ident, _, _) => sp_ident.node,
2004 _ => tcx.sess.span_bug(
2006 "non-method call expr behaving like a method call?")
2008 // It matches if it comes from the same impl,
2009 // and has the same method name.
2010 return ast_util::is_local(impl_def_id)
2011 && impl_def_id.node == impl_id
2012 && method_name.name == name.name
2017 ast_util::is_local(did) && did.node == method_id
2025 "compiler plugin used as ordinary library in non-plugin crate"
2028 #[derive(Copy, Clone)]
2029 pub struct PluginAsLibrary;
2031 impl LintPass for PluginAsLibrary {
2032 fn get_lints(&self) -> LintArray {
2033 lint_array![PLUGIN_AS_LIBRARY]
2036 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
2037 if cx.sess().plugin_registrar_fn.get().is_some() {
2038 // We're compiling a plugin; it's fine to link other plugins.
2043 ast::ItemExternCrate(..) => (),
2047 let md = match cx.sess().cstore.find_extern_mod_stmt_cnum(it.id) {
2048 Some(cnum) => cx.sess().cstore.get_crate_data(cnum),
2050 // Probably means we aren't linking the crate for some reason.
2052 // Not sure if / when this could happen.
2057 if decoder::get_plugin_registrar_fn(md.data()).is_some() {
2058 cx.span_lint(PLUGIN_AS_LIBRARY, it.span,
2059 "compiler plugin used as an ordinary library");
2065 PRIVATE_NO_MANGLE_FNS,
2067 "functions marked #[no_mangle] should be exported"
2071 PRIVATE_NO_MANGLE_STATICS,
2073 "statics marked #[no_mangle] should be exported"
2077 NO_MANGLE_CONST_ITEMS,
2079 "const items will not have their symbols exported"
2082 #[derive(Copy, Clone)]
2083 pub struct InvalidNoMangleItems;
2085 impl LintPass for InvalidNoMangleItems {
2086 fn get_lints(&self) -> LintArray {
2087 lint_array!(PRIVATE_NO_MANGLE_FNS,
2088 PRIVATE_NO_MANGLE_STATICS,
2089 NO_MANGLE_CONST_ITEMS)
2092 fn check_item(&mut self, cx: &Context, it: &ast::Item) {
2094 ast::ItemFn(..) => {
2095 if attr::contains_name(&it.attrs, "no_mangle") &&
2096 !cx.exported_items.contains(&it.id) {
2097 let msg = format!("function {} is marked #[no_mangle], but not exported",
2099 cx.span_lint(PRIVATE_NO_MANGLE_FNS, it.span, &msg);
2102 ast::ItemStatic(..) => {
2103 if attr::contains_name(&it.attrs, "no_mangle") &&
2104 !cx.exported_items.contains(&it.id) {
2105 let msg = format!("static {} is marked #[no_mangle], but not exported",
2107 cx.span_lint(PRIVATE_NO_MANGLE_STATICS, it.span, &msg);
2110 ast::ItemConst(..) => {
2111 if attr::contains_name(&it.attrs, "no_mangle") {
2112 // Const items do not refer to a particular location in memory, and therefore
2113 // don't have anything to attach a symbol to
2114 let msg = "const items should never be #[no_mangle], consider instead using \
2116 cx.span_lint(NO_MANGLE_CONST_ITEMS, it.span, msg);
2124 #[derive(Clone, Copy)]
2125 pub struct MutableTransmutes;
2130 "mutating transmuted &mut T from &T may cause undefined behavior"
2133 impl LintPass for MutableTransmutes {
2134 fn get_lints(&self) -> LintArray {
2135 lint_array!(MUTABLE_TRANSMUTES)
2138 fn check_expr(&mut self, cx: &Context, expr: &ast::Expr) {
2139 use syntax::ast::DefId;
2140 use syntax::abi::RustIntrinsic;
2141 let msg = "mutating transmuted &mut T from &T may cause undefined behavior,\
2142 consider instead using an UnsafeCell";
2143 match get_transmute_from_to(cx, expr) {
2144 Some((&ty::ty_rptr(_, from_mt), &ty::ty_rptr(_, to_mt))) => {
2145 if to_mt.mutbl == ast::Mutability::MutMutable
2146 && from_mt.mutbl == ast::Mutability::MutImmutable {
2147 cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg);
2153 fn get_transmute_from_to<'a, 'tcx>(cx: &Context<'a, 'tcx>, expr: &ast::Expr)
2154 -> Option<(&'tcx ty::sty<'tcx>, &'tcx ty::sty<'tcx>)> {
2156 ast::ExprPath(..) => (),
2159 if let DefFn(did, _) = ty::resolve_expr(cx.tcx, expr) {
2160 if !def_id_is_transmute(cx, did) {
2163 let typ = ty::node_id_to_type(cx.tcx, expr.id);
2165 ty::ty_bare_fn(_, ref bare_fn) if bare_fn.abi == RustIntrinsic => {
2166 if let ty::FnConverging(to) = bare_fn.sig.0.output {
2167 let from = bare_fn.sig.0.inputs[0];
2168 return Some((&from.sty, &to.sty));
2177 fn def_id_is_transmute(cx: &Context, def_id: DefId) -> bool {
2178 match ty::lookup_item_type(cx.tcx, def_id).ty.sty {
2179 ty::ty_bare_fn(_, ref bfty) if bfty.abi == RustIntrinsic => (),
2182 ty::with_path(cx.tcx, def_id, |path| match path.last() {
2183 Some(ref last) => last.name().as_str() == "transmute",
2190 /// Forbids using the `#[feature(...)]` attribute
2191 #[derive(Copy, Clone)]
2192 pub struct UnstableFeatures;
2197 "enabling unstable features"
2200 impl LintPass for UnstableFeatures {
2201 fn get_lints(&self) -> LintArray {
2202 lint_array!(UNSTABLE_FEATURES)
2204 fn check_attribute(&mut self, ctx: &Context, attr: &ast::Attribute) {
2205 if attr::contains_name(&[attr.node.value.clone()], "feature") {
2206 ctx.span_lint(UNSTABLE_FEATURES, attr.span, "unstable feature");
2211 /// Lints for attempts to impl Drop on types that have `#[repr(C)]`
2212 /// attribute (see issue #24585).
2213 #[derive(Copy, Clone)]
2214 pub struct DropWithReprExtern;
2217 DROP_WITH_REPR_EXTERN,
2219 "use of #[repr(C)] on a type that implements Drop"
2222 impl LintPass for DropWithReprExtern {
2223 fn get_lints(&self) -> LintArray {
2224 lint_array!(DROP_WITH_REPR_EXTERN)
2226 fn check_crate(&mut self, ctx: &Context, _: &ast::Crate) {
2227 for dtor_did in ctx.tcx.destructors.borrow().iter() {
2228 let (drop_impl_did, dtor_self_type) =
2229 if dtor_did.krate == ast::LOCAL_CRATE {
2230 let impl_did = ctx.tcx.map.get_parent_did(dtor_did.node);
2231 let ty = ty::lookup_item_type(ctx.tcx, impl_did).ty;
2237 match dtor_self_type.sty {
2238 ty::ty_enum(self_type_did, _) |
2239 ty::ty_struct(self_type_did, _) |
2240 ty::ty_closure(self_type_did, _) => {
2241 let hints = ty::lookup_repr_hints(ctx.tcx, self_type_did);
2242 if hints.iter().any(|attr| *attr == attr::ReprExtern) &&
2243 ty::ty_dtor(ctx.tcx, self_type_did).has_drop_flag() {
2244 let drop_impl_span = ctx.tcx.map.def_id_span(drop_impl_did,
2246 let self_defn_span = ctx.tcx.map.def_id_span(self_type_did,
2248 ctx.span_lint(DROP_WITH_REPR_EXTERN,
2250 "implementing Drop adds hidden state to types, \
2251 possibly conflicting with `#[repr(C)]`");
2252 // FIXME #19668: could be span_lint_note instead of manual guard.
2253 if ctx.current_level(DROP_WITH_REPR_EXTERN) != Level::Allow {
2254 ctx.sess().span_note(self_defn_span,
2255 "the `#[repr(C)]` attribute is attached here");