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}
;
32 use middle
::{cfg, def, infer, pat_util, stability, traits}
;
33 use middle
::def_id
::DefId
;
34 use middle
::subst
::Substs
;
35 use middle
::ty
::{self, Ty}
;
36 use middle
::ty
::adjustment
;
37 use middle
::const_eval
::{eval_const_expr_partial, ConstVal}
;
38 use middle
::const_eval
::EvalHint
::ExprTypeChecked
;
39 use rustc
::front
::map
as hir_map
;
40 use util
::nodemap
::{FnvHashMap, FnvHashSet, NodeSet}
;
41 use lint
::{Level, Context, LintPass, LintArray, Lint}
;
43 use std
::collections
::HashSet
;
44 use std
::collections
::hash_map
::Entry
::{Occupied, Vacant}
;
45 use std
::{cmp, slice}
;
46 use std
::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}
;
48 use syntax
::{abi, ast}
;
49 use syntax
::attr
as syntax_attr
;
50 use syntax
::codemap
::{self, Span}
;
51 use syntax
::feature_gate
::{KNOWN_ATTRIBUTES, AttributeType}
;
52 use rustc_front
::hir
::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64}
;
57 use rustc_front
::attr
::{self, AttrMetaMethods}
;
58 use rustc_front
::visit
::{self, FnKind, Visitor}
;
59 use rustc_front
::lowering
::unlower_attribute
;
61 use rustc_front
::util
::is_shift_binop
;
63 // hardwired lints from librustc
64 pub use lint
::builtin
::*;
69 "suggest using `loop { }` instead of `while true { }`"
72 #[derive(Copy, Clone)]
75 impl LintPass
for WhileTrue
{
76 fn get_lints(&self) -> LintArray
{
77 lint_array
!(WHILE_TRUE
)
80 fn check_expr(&mut self, cx
: &Context
, e
: &hir
::Expr
) {
81 if let hir
::ExprWhile(ref cond
, _
, _
) = e
.node
{
82 if let hir
::ExprLit(ref lit
) = cond
.node
{
83 if let hir
::LitBool(true) = lit
.node
{
84 cx
.span_lint(WHILE_TRUE
, e
.span
,
85 "denote infinite loops with loop { ... }");
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
!(UNUSED_COMPARISONS
, OVERFLOWING_LITERALS
, EXCEEDING_BITSHIFTS
)
129 fn check_expr(&mut self, cx
: &Context
, e
: &hir
::Expr
) {
131 hir
::ExprUnary(hir
::UnNeg
, ref expr
) => {
133 hir
::ExprLit(ref lit
) => {
135 hir
::LitInt(_
, hir
::UnsignedIntLit(_
)) => {
136 check_unsigned_negation_feature(cx
, e
.span
);
138 hir
::LitInt(_
, hir
::UnsuffixedIntLit(_
)) => {
139 if let ty
::TyUint(_
) = cx
.tcx
.node_id_to_type(e
.id
).sty
{
140 check_unsigned_negation_feature(cx
, e
.span
);
147 let t
= cx
.tcx
.node_id_to_type(expr
.id
);
150 check_unsigned_negation_feature(cx
, e
.span
);
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 hir
::ExprParen(ref expr
) if self.negated_expr_id
== e
.id
=> {
162 self.negated_expr_id
= expr
.id
;
164 hir
::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 cx
.tcx
.node_id_to_type(l
.id
).sty
{
172 ty
::TyInt(t
) => Some(int_ty_bits(t
, cx
.sess().target
.int_type
)),
173 ty
::TyUint(t
) => Some(uint_ty_bits(t
, cx
.sess().target
.uint_type
)),
177 if let Some(bits
) = opt_ty_bits
{
178 let exceeding
= if let hir
::ExprLit(ref lit
) = r
.node
{
179 if let hir
::LitInt(shift
, _
) = lit
.node { shift >= bits }
182 match eval_const_expr_partial(cx
.tcx
, &r
, ExprTypeChecked
) {
183 Ok(ConstVal
::Int(shift
)) => { shift as u64 >= bits }
,
184 Ok(ConstVal
::Uint(shift
)) => { shift >= bits }
,
189 cx
.span_lint(EXCEEDING_BITSHIFTS
, e
.span
,
190 "bitshift exceeds the type's number of bits");
195 hir
::ExprLit(ref lit
) => {
196 match cx
.tcx
.node_id_to_type(e
.id
).sty
{
199 hir
::LitInt(v
, hir
::SignedIntLit(_
, hir
::Plus
)) |
200 hir
::LitInt(v
, hir
::UnsuffixedIntLit(hir
::Plus
)) => {
201 let int_type
= if let hir
::TyIs
= t
{
202 cx
.sess().target
.int_type
206 let (_
, max
) = int_ty_range(int_type
);
207 let negative
= self.negated_expr_id
== e
.id
;
209 // Detect literal value out of range [min, max] inclusive
210 // avoiding use of -min to prevent overflow/panic
211 if (negative
&& v
> max
as u64 + 1) ||
212 (!negative
&& v
> max
as u64) {
213 cx
.span_lint(OVERFLOWING_LITERALS
, e
.span
,
214 &*format
!("literal out of range for {:?}", t
));
222 let uint_type
= if let hir
::TyUs
= t
{
223 cx
.sess().target
.uint_type
227 let (min
, max
) = uint_ty_range(uint_type
);
228 let lit_val
: u64 = match lit
.node
{
229 hir
::LitByte(_v
) => return, // _v is u8, within range by definition
230 hir
::LitInt(v
, _
) => v
,
233 if lit_val
< min
|| lit_val
> max
{
234 cx
.span_lint(OVERFLOWING_LITERALS
, e
.span
,
235 &*format
!("literal out of range for {:?}", t
));
239 let (min
, max
) = float_ty_range(t
);
240 let lit_val
: f64 = match lit
.node
{
241 hir
::LitFloat(ref v
, _
) |
242 hir
::LitFloatUnsuffixed(ref v
) => {
250 if lit_val
< min
|| lit_val
> max
{
251 cx
.span_lint(OVERFLOWING_LITERALS
, e
.span
,
252 &*format
!("literal out of range for {:?}", t
));
261 fn is_valid
<T
:cmp
::PartialOrd
>(binop
: hir
::BinOp
, v
: T
,
262 min
: T
, max
: T
) -> bool
{
264 hir
::BiLt
=> v
> min
&& v
<= max
,
265 hir
::BiLe
=> v
>= min
&& v
< max
,
266 hir
::BiGt
=> v
>= min
&& v
< max
,
267 hir
::BiGe
=> v
> min
&& v
<= max
,
268 hir
::BiEq
| hir
::BiNe
=> v
>= min
&& v
<= max
,
273 fn rev_binop(binop
: hir
::BinOp
) -> hir
::BinOp
{
274 codemap
::respan(binop
.span
, match binop
.node
{
275 hir
::BiLt
=> hir
::BiGt
,
276 hir
::BiLe
=> hir
::BiGe
,
277 hir
::BiGt
=> hir
::BiLt
,
278 hir
::BiGe
=> hir
::BiLe
,
283 // for isize & usize, be conservative with the warnings, so that the
284 // warnings are consistent between 32- and 64-bit platforms
285 fn int_ty_range(int_ty
: hir
::IntTy
) -> (i64, i64) {
287 hir
::TyIs
=> (i64::MIN
, i64::MAX
),
288 hir
::TyI8
=> (i8::MIN
as i64, i8::MAX
as i64),
289 hir
::TyI16
=> (i16::MIN
as i64, i16::MAX
as i64),
290 hir
::TyI32
=> (i32::MIN
as i64, i32::MAX
as i64),
291 hir
::TyI64
=> (i64::MIN
, i64::MAX
)
295 fn uint_ty_range(uint_ty
: hir
::UintTy
) -> (u64, u64) {
297 hir
::TyUs
=> (u64::MIN
, u64::MAX
),
298 hir
::TyU8
=> (u8::MIN
as u64, u8::MAX
as u64),
299 hir
::TyU16
=> (u16::MIN
as u64, u16::MAX
as u64),
300 hir
::TyU32
=> (u32::MIN
as u64, u32::MAX
as u64),
301 hir
::TyU64
=> (u64::MIN
, u64::MAX
)
305 fn float_ty_range(float_ty
: hir
::FloatTy
) -> (f64, f64) {
307 hir
::TyF32
=> (f32::MIN
as f64, f32::MAX
as f64),
308 hir
::TyF64
=> (f64::MIN
, f64::MAX
)
312 fn int_ty_bits(int_ty
: hir
::IntTy
, target_int_ty
: hir
::IntTy
) -> u64 {
314 hir
::TyIs
=> int_ty_bits(target_int_ty
, target_int_ty
),
315 hir
::TyI8
=> i8::BITS
as u64,
316 hir
::TyI16
=> i16::BITS
as u64,
317 hir
::TyI32
=> i32::BITS
as u64,
318 hir
::TyI64
=> i64::BITS
as u64
322 fn uint_ty_bits(uint_ty
: hir
::UintTy
, target_uint_ty
: hir
::UintTy
) -> u64 {
324 hir
::TyUs
=> uint_ty_bits(target_uint_ty
, target_uint_ty
),
325 hir
::TyU8
=> u8::BITS
as u64,
326 hir
::TyU16
=> u16::BITS
as u64,
327 hir
::TyU32
=> u32::BITS
as u64,
328 hir
::TyU64
=> u64::BITS
as u64
332 fn check_limits(tcx
: &ty
::ctxt
, binop
: hir
::BinOp
,
333 l
: &hir
::Expr
, r
: &hir
::Expr
) -> bool
{
334 let (lit
, expr
, swap
) = match (&l
.node
, &r
.node
) {
335 (&hir
::ExprLit(_
), _
) => (l
, r
, true),
336 (_
, &hir
::ExprLit(_
)) => (r
, l
, false),
339 // Normalize the binop so that the literal is always on the RHS in
341 let norm_binop
= if swap
{
346 match tcx
.node_id_to_type(expr
.id
).sty
{
347 ty
::TyInt(int_ty
) => {
348 let (min
, max
) = int_ty_range(int_ty
);
349 let lit_val
: i64 = match lit
.node
{
350 hir
::ExprLit(ref li
) => match li
.node
{
351 hir
::LitInt(v
, hir
::SignedIntLit(_
, hir
::Plus
)) |
352 hir
::LitInt(v
, hir
::UnsuffixedIntLit(hir
::Plus
)) => v
as i64,
353 hir
::LitInt(v
, hir
::SignedIntLit(_
, hir
::Minus
)) |
354 hir
::LitInt(v
, hir
::UnsuffixedIntLit(hir
::Minus
)) => -(v
as i64),
359 is_valid(norm_binop
, lit_val
, min
, max
)
361 ty
::TyUint(uint_ty
) => {
362 let (min
, max
): (u64, u64) = uint_ty_range(uint_ty
);
363 let lit_val
: u64 = match lit
.node
{
364 hir
::ExprLit(ref li
) => match li
.node
{
365 hir
::LitInt(v
, _
) => v
,
370 is_valid(norm_binop
, lit_val
, min
, max
)
376 fn is_comparison(binop
: hir
::BinOp
) -> bool
{
378 hir
::BiEq
| hir
::BiLt
| hir
::BiLe
|
379 hir
::BiNe
| hir
::BiGe
| hir
::BiGt
=> true,
384 fn check_unsigned_negation_feature(cx
: &Context
, span
: Span
) {
385 if !cx
.sess().features
.borrow().negate_unsigned
{
386 // FIXME(#27141): change this to syntax::feature_gate::emit_feature_err…
387 cx
.sess().span_warn(span
,
388 "unary negation of unsigned integers will be feature gated in the future");
389 // …and remove following two expressions.
390 if option_env
!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() { return; }
391 cx
.sess().fileline_help(span
, "add #![feature(negate_unsigned)] to the \
392 crate attributes to enable the gate in advance");
401 "proper use of libc types in foreign modules"
404 struct ImproperCTypesVisitor
<'a
, 'tcx
: 'a
> {
405 cx
: &'a Context
<'a
, 'tcx
>
410 FfiUnsafe(&'
static str),
411 FfiBadStruct(DefId
, &'
static str),
412 FfiBadEnum(DefId
, &'
static str)
415 /// Check if this enum can be safely exported based on the
416 /// "nullable pointer optimization". Currently restricted
417 /// to function pointers and references, but could be
418 /// expanded to cover NonZero raw pointers and newtypes.
419 /// FIXME: This duplicates code in trans.
420 fn is_repr_nullable_ptr
<'tcx
>(tcx
: &ty
::ctxt
<'tcx
>,
421 def
: ty
::AdtDef
<'tcx
>,
422 substs
: &Substs
<'tcx
>)
424 if def
.variants
.len() == 2 {
427 if def
.variants
[0].fields
.is_empty() {
429 } else if def
.variants
[1].fields
.is_empty() {
435 if def
.variants
[data_idx
].fields
.len() == 1 {
436 match def
.variants
[data_idx
].fields
[0].ty(tcx
, substs
).sty
{
437 ty
::TyBareFn(None
, _
) => { return true; }
438 ty
::TyRef(..) => { return true; }
446 fn ast_ty_to_normalized
<'tcx
>(tcx
: &ty
::ctxt
<'tcx
>,
449 let tty
= match tcx
.ast_ty_to_ty_cache
.borrow().get(&id
) {
451 None
=> panic
!("ast_ty_to_ty_cache was incomplete after typeck!")
453 infer
::normalize_associated_type(tcx
, &tty
)
456 impl<'a
, 'tcx
> ImproperCTypesVisitor
<'a
, 'tcx
> {
457 /// Check if the given type is "ffi-safe" (has a stable, well-defined
458 /// representation which can be exported to C code).
459 fn check_type_for_ffi(&self,
460 cache
: &mut FnvHashSet
<Ty
<'tcx
>>,
463 use self::FfiResult
::*;
464 let cx
= &self.cx
.tcx
;
466 // Protect against infinite recursion, for example
467 // `struct S(*mut S);`.
468 // FIXME: A recursion limit is necessary as well, for irregular
470 if !cache
.insert(ty
) {
475 ty
::TyStruct(def
, substs
) => {
476 if !cx
.lookup_repr_hints(def
.did
).contains(&attr
::ReprExtern
) {
478 "found struct without foreign-function-safe \
479 representation annotation in foreign module, \
480 consider adding a #[repr(C)] attribute to \
484 // We can't completely trust repr(C) markings; make sure the
485 // fields are actually safe.
486 if def
.struct_variant().fields
.is_empty() {
488 "found zero-size struct in foreign module, consider \
489 adding a member to this struct");
492 for field
in &def
.struct_variant().fields
{
493 let field_ty
= infer
::normalize_associated_type(cx
, &field
.ty(cx
, substs
));
494 let r
= self.check_type_for_ffi(cache
, field_ty
);
497 FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
498 FfiUnsafe(s
) => { return FfiBadStruct(def.did, s); }
503 ty
::TyEnum(def
, substs
) => {
504 if def
.variants
.is_empty() {
505 // Empty enums are okay... although sort of useless.
509 // Check for a repr() attribute to specify the size of the
511 let repr_hints
= cx
.lookup_repr_hints(def
.did
);
512 match &**repr_hints
{
514 // Special-case types like `Option<extern fn()>`.
515 if !is_repr_nullable_ptr(cx
, def
, substs
) {
517 "found enum without foreign-function-safe \
518 representation annotation in foreign module, \
519 consider adding a #[repr(...)] attribute to \
524 if !hint
.is_ffi_safe() {
525 // FIXME: This shouldn't be reachable: we should check
528 "enum has unexpected #[repr(...)] attribute")
531 // Enum with an explicitly sized discriminant; either
532 // a C-style enum or a discriminated union.
534 // The layout of enum variants is implicitly repr(C).
535 // FIXME: Is that correct?
538 // FIXME: This shouldn't be reachable: we should check
541 "enum has too many #[repr(...)] attributes");
545 // Check the contained variants.
546 for variant
in &def
.variants
{
547 for field
in &variant
.fields
{
548 let arg
= infer
::normalize_associated_type(cx
, &field
.ty(cx
, substs
));
549 let r
= self.check_type_for_ffi(cache
, arg
);
552 FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
553 FfiUnsafe(s
) => { return FfiBadEnum(def.did, s); }
561 FfiUnsafe("found Rust type `char` in foreign module, while \
562 `u32` or `libc::wchar_t` should be used")
565 // Primitive types with a stable representation.
566 ty
::TyBool
| ty
::TyInt(..) | ty
::TyUint(..) |
567 ty
::TyFloat(..) => FfiSafe
,
570 FfiUnsafe("found Rust type Box<_> in foreign module, \
571 consider using a raw pointer instead")
575 FfiUnsafe("found Rust slice type in foreign module, \
576 consider using a raw pointer instead")
580 FfiUnsafe("found Rust trait type in foreign module, \
581 consider using a raw pointer instead")
585 FfiUnsafe("found Rust type `str` in foreign module; \
586 consider using a `*const libc::c_char`")
590 FfiUnsafe("found Rust tuple type in foreign module; \
591 consider using a struct instead`")
594 ty
::TyRawPtr(ref m
) | ty
::TyRef(_
, ref m
) => {
595 self.check_type_for_ffi(cache
, m
.ty
)
598 ty
::TyArray(ty
, _
) => {
599 self.check_type_for_ffi(cache
, ty
)
602 ty
::TyBareFn(None
, bare_fn
) => {
606 abi
::PlatformIntrinsic
|
609 "found function pointer with Rust calling \
610 convention in foreign module; consider using an \
611 `extern` function pointer")
616 let sig
= cx
.erase_late_bound_regions(&bare_fn
.sig
);
618 ty
::FnDiverging
=> {}
619 ty
::FnConverging(output
) => {
620 if !output
.is_nil() {
621 let r
= self.check_type_for_ffi(cache
, output
);
629 for arg
in sig
.inputs
{
630 let r
= self.check_type_for_ffi(cache
, arg
);
639 ty
::TyParam(..) | ty
::TyInfer(..) | ty
::TyError
|
640 ty
::TyClosure(..) | ty
::TyProjection(..) |
641 ty
::TyBareFn(Some(_
), _
) => {
642 panic
!("Unexpected type in foreign function")
647 fn check_def(&mut self, sp
: Span
, id
: ast
::NodeId
) {
648 let tty
= ast_ty_to_normalized(self.cx
.tcx
, id
);
650 match ImproperCTypesVisitor
::check_type_for_ffi(self, &mut FnvHashSet(), tty
) {
651 FfiResult
::FfiSafe
=> {}
652 FfiResult
::FfiUnsafe(s
) => {
653 self.cx
.span_lint(IMPROPER_CTYPES
, sp
, s
);
655 FfiResult
::FfiBadStruct(_
, s
) => {
656 // FIXME: This diagnostic is difficult to read, and doesn't
657 // point at the relevant field.
658 self.cx
.span_lint(IMPROPER_CTYPES
, sp
,
659 &format
!("found non-foreign-function-safe member in \
660 struct marked #[repr(C)]: {}", s
));
662 FfiResult
::FfiBadEnum(_
, s
) => {
663 // FIXME: This diagnostic is difficult to read, and doesn't
664 // point at the relevant variant.
665 self.cx
.span_lint(IMPROPER_CTYPES
, sp
,
666 &format
!("found non-foreign-function-safe member in \
673 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for ImproperCTypesVisitor
<'a
, 'tcx
> {
674 fn visit_ty(&mut self, ty
: &hir
::Ty
) {
677 hir
::TyBareFn(..) => self.check_def(ty
.span
, ty
.id
),
679 self.cx
.span_lint(IMPROPER_CTYPES
, ty
.span
,
680 "found Rust slice type in foreign module, consider \
681 using a raw pointer instead");
683 hir
::TyFixedLengthVec(ref ty
, _
) => self.visit_ty(ty
),
685 self.cx
.span_lint(IMPROPER_CTYPES
, ty
.span
,
686 "found Rust tuple type in foreign module; \
687 consider using a struct instead`")
689 _
=> visit
::walk_ty(self, ty
)
694 #[derive(Copy, Clone)]
695 pub struct ImproperCTypes
;
697 impl LintPass
for ImproperCTypes
{
698 fn get_lints(&self) -> LintArray
{
699 lint_array
!(IMPROPER_CTYPES
)
702 fn check_item(&mut self, cx
: &Context
, it
: &hir
::Item
) {
703 fn check_ty(cx
: &Context
, ty
: &hir
::Ty
) {
704 let mut vis
= ImproperCTypesVisitor { cx: cx }
;
708 fn check_foreign_fn(cx
: &Context
, decl
: &hir
::FnDecl
) {
709 for input
in &decl
.inputs
{
710 check_ty(cx
, &*input
.ty
);
712 if let hir
::Return(ref ret_ty
) = decl
.output
{
713 let tty
= ast_ty_to_normalized(cx
.tcx
, ret_ty
.id
);
715 check_ty(cx
, &ret_ty
);
721 hir
::ItemForeignMod(ref nmod
)
722 if nmod
.abi
!= abi
::RustIntrinsic
&&
723 nmod
.abi
!= abi
::PlatformIntrinsic
=> {
724 for ni
in &nmod
.items
{
726 hir
::ForeignItemFn(ref decl
, _
) => check_foreign_fn(cx
, &**decl
),
727 hir
::ForeignItemStatic(ref t
, _
) => check_ty(cx
, &**t
)
739 "use of owned (Box type) heap memory"
742 #[derive(Copy, Clone)]
743 pub struct BoxPointers
;
746 fn check_heap_type
<'a
, 'tcx
>(&self, cx
: &Context
<'a
, 'tcx
>,
747 span
: Span
, ty
: Ty
<'tcx
>) {
748 for leaf_ty
in ty
.walk() {
749 if let ty
::TyBox(_
) = leaf_ty
.sty
{
750 let m
= format
!("type uses owned (Box type) pointers: {}", ty
);
751 cx
.span_lint(BOX_POINTERS
, span
, &m
);
757 impl LintPass
for BoxPointers
{
758 fn get_lints(&self) -> LintArray
{
759 lint_array
!(BOX_POINTERS
)
762 fn check_item(&mut self, cx
: &Context
, it
: &hir
::Item
) {
767 hir
::ItemStruct(..) =>
768 self.check_heap_type(cx
, it
.span
,
769 cx
.tcx
.node_id_to_type(it
.id
)),
773 // If it's a struct, we also have to check the fields' types
775 hir
::ItemStruct(ref struct_def
, _
) => {
776 for struct_field
in &struct_def
.fields
{
777 self.check_heap_type(cx
, struct_field
.span
,
778 cx
.tcx
.node_id_to_type(struct_field
.node
.id
));
785 fn check_expr(&mut self, cx
: &Context
, e
: &hir
::Expr
) {
786 let ty
= cx
.tcx
.node_id_to_type(e
.id
);
787 self.check_heap_type(cx
, e
.span
, ty
);
794 "uses of #[derive] with raw pointers are rarely correct"
797 struct RawPtrDeriveVisitor
<'a
, 'tcx
: 'a
> {
798 cx
: &'a Context
<'a
, 'tcx
>
801 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for RawPtrDeriveVisitor
<'a
, 'tcx
> {
802 fn visit_ty(&mut self, ty
: &hir
::Ty
) {
803 const MSG
: &'
static str = "use of `#[derive]` with a raw pointer";
804 if let hir
::TyPtr(..) = ty
.node
{
805 self.cx
.span_lint(RAW_POINTER_DERIVE
, ty
.span
, MSG
);
807 visit
::walk_ty(self, ty
);
809 // explicit override to a no-op to reduce code bloat
810 fn visit_expr(&mut self, _
: &hir
::Expr
) {}
811 fn visit_block(&mut self, _
: &hir
::Block
) {}
814 pub struct RawPointerDerive
{
815 checked_raw_pointers
: NodeSet
,
818 impl RawPointerDerive
{
819 pub fn new() -> RawPointerDerive
{
821 checked_raw_pointers
: NodeSet(),
826 impl LintPass
for RawPointerDerive
{
827 fn get_lints(&self) -> LintArray
{
828 lint_array
!(RAW_POINTER_DERIVE
)
831 fn check_item(&mut self, cx
: &Context
, item
: &hir
::Item
) {
832 if !attr
::contains_name(&item
.attrs
, "automatically_derived") {
835 let did
= match item
.node
{
836 hir
::ItemImpl(_
, _
, _
, ref t_ref_opt
, _
, _
) => {
837 // Deriving the Copy trait does not cause a warning
838 if let &Some(ref trait_ref
) = t_ref_opt
{
839 let def_id
= cx
.tcx
.trait_ref_to_def_id(trait_ref
);
840 if Some(def_id
) == cx
.tcx
.lang_items
.copy_trait() {
845 match cx
.tcx
.node_id_to_type(item
.id
).sty
{
846 ty
::TyEnum(def
, _
) => def
.did
,
847 ty
::TyStruct(def
, _
) => def
.did
,
856 let item
= match cx
.tcx
.map
.find(did
.node
) {
857 Some(hir_map
::NodeItem(item
)) => item
,
860 if !self.checked_raw_pointers
.insert(item
.id
) {
864 hir
::ItemStruct(..) | hir
::ItemEnum(..) => {
865 let mut visitor
= RawPtrDeriveVisitor { cx: cx }
;
866 visit
::walk_item(&mut visitor
, &item
);
876 "detects attributes that were not used by the compiler"
879 #[derive(Copy, Clone)]
880 pub struct UnusedAttributes
;
882 impl LintPass
for UnusedAttributes
{
883 fn get_lints(&self) -> LintArray
{
884 lint_array
!(UNUSED_ATTRIBUTES
)
887 fn check_attribute(&mut self, cx
: &Context
, attr
: &hir
::Attribute
) {
888 // Note that check_name() marks the attribute as used if it matches.
889 for &(ref name
, ty
, _
) in KNOWN_ATTRIBUTES
{
891 AttributeType
::Whitelisted
if attr
.check_name(name
) => {
898 let plugin_attributes
= cx
.sess().plugin_attributes
.borrow_mut();
899 for &(ref name
, ty
) in plugin_attributes
.iter() {
900 if ty
== AttributeType
::Whitelisted
&& attr
.check_name(&*name
) {
905 if !syntax_attr
::is_used(&unlower_attribute(attr
)) {
906 cx
.span_lint(UNUSED_ATTRIBUTES
, attr
.span
, "unused attribute");
907 // Is it a builtin attribute that must be used at the crate level?
908 let known_crate
= KNOWN_ATTRIBUTES
.iter().find(|&&(name
, ty
, _
)| {
909 attr
.name() == name
&&
910 ty
== AttributeType
::CrateLevel
913 // Has a plugin registered this attribute as one which must be used at
915 let plugin_crate
= plugin_attributes
.iter()
916 .find(|&&(ref x
, t
)| {
917 &*attr
.name() == &*x
&&
918 AttributeType
::CrateLevel
== t
920 if known_crate
|| plugin_crate
{
921 let msg
= match attr
.node
.style
{
922 hir
::AttrOuter
=> "crate-level attribute should be an inner \
923 attribute: add an exclamation mark: #![foo]",
924 hir
::AttrInner
=> "crate-level attribute should be in the \
927 cx
.span_lint(UNUSED_ATTRIBUTES
, attr
.span
, msg
);
936 "path statements with no effect"
939 #[derive(Copy, Clone)]
940 pub struct PathStatements
;
942 impl LintPass
for PathStatements
{
943 fn get_lints(&self) -> LintArray
{
944 lint_array
!(PATH_STATEMENTS
)
947 fn check_stmt(&mut self, cx
: &Context
, s
: &hir
::Stmt
) {
949 hir
::StmtSemi(ref expr
, _
) => {
951 hir
::ExprPath(..) => cx
.span_lint(PATH_STATEMENTS
, s
.span
,
952 "path statement with no effect"),
964 "unused result of a type flagged as #[must_use]"
970 "unused result of an expression in a statement"
973 #[derive(Copy, Clone)]
974 pub struct UnusedResults
;
976 impl LintPass
for UnusedResults
{
977 fn get_lints(&self) -> LintArray
{
978 lint_array
!(UNUSED_MUST_USE
, UNUSED_RESULTS
)
981 fn check_stmt(&mut self, cx
: &Context
, s
: &hir
::Stmt
) {
982 let expr
= match s
.node
{
983 hir
::StmtSemi(ref expr
, _
) => &**expr
,
987 if let hir
::ExprRet(..) = expr
.node
{
991 let t
= cx
.tcx
.expr_ty(&expr
);
992 let warned
= match t
.sty
{
993 ty
::TyTuple(ref tys
) if tys
.is_empty() => return,
994 ty
::TyBool
=> return,
995 ty
::TyStruct(def
, _
) |
996 ty
::TyEnum(def
, _
) => {
997 if def
.did
.is_local() {
998 if let hir_map
::NodeItem(it
) = cx
.tcx
.map
.get(def
.did
.node
) {
999 check_must_use(cx
, &it
.attrs
, s
.span
)
1004 let attrs
= csearch
::get_item_attrs(&cx
.sess().cstore
, def
.did
);
1005 check_must_use(cx
, &attrs
[..], s
.span
)
1011 cx
.span_lint(UNUSED_RESULTS
, s
.span
, "unused result");
1014 fn check_must_use(cx
: &Context
, attrs
: &[hir
::Attribute
], sp
: Span
) -> bool
{
1016 if attr
.check_name("must_use") {
1017 let mut msg
= "unused result which must be used".to_string();
1018 // check for #[must_use="..."]
1019 match attr
.value_str() {
1026 cx
.span_lint(UNUSED_MUST_USE
, sp
, &msg
);
1036 pub NON_CAMEL_CASE_TYPES
,
1038 "types, variants, traits and type parameters should have camel case names"
1041 #[derive(Copy, Clone)]
1042 pub struct NonCamelCaseTypes
;
1044 impl NonCamelCaseTypes
{
1045 fn check_case(&self, cx
: &Context
, sort
: &str, ident
: ast
::Ident
, span
: Span
) {
1046 fn is_camel_case(ident
: ast
::Ident
) -> bool
{
1047 let ident
= ident
.name
.as_str();
1048 if ident
.is_empty() {
1051 let ident
= ident
.trim_matches('_'
);
1053 // start with a non-lowercase letter rather than non-uppercase
1054 // ones (some scripts don't have a concept of upper/lowercase)
1055 !ident
.is_empty() && !ident
.char_at(0).is_lowercase() && !ident
.contains('_'
)
1058 fn to_camel_case(s
: &str) -> String
{
1059 s
.split('_'
).flat_map(|word
| word
.chars().enumerate().map(|(i
, c
)|
1061 c
.to_uppercase().collect
::<String
>()
1063 c
.to_lowercase().collect()
1065 )).collect
::<Vec
<_
>>().concat()
1068 let s
= ident
.name
.as_str();
1070 if !is_camel_case(ident
) {
1071 let c
= to_camel_case(&s
);
1072 let m
= if c
.is_empty() {
1073 format
!("{} `{}` should have a camel case name such as `CamelCase`", sort
, s
)
1075 format
!("{} `{}` should have a camel case name such as `{}`", sort
, s
, c
)
1077 cx
.span_lint(NON_CAMEL_CASE_TYPES
, span
, &m
[..]);
1082 impl LintPass
for NonCamelCaseTypes
{
1083 fn get_lints(&self) -> LintArray
{
1084 lint_array
!(NON_CAMEL_CASE_TYPES
)
1087 fn check_item(&mut self, cx
: &Context
, it
: &hir
::Item
) {
1088 let extern_repr_count
= it
.attrs
.iter().filter(|attr
| {
1089 attr
::find_repr_attrs(cx
.tcx
.sess
.diagnostic(), attr
).iter()
1090 .any(|r
| r
== &attr
::ReprExtern
)
1092 let has_extern_repr
= extern_repr_count
> 0;
1094 if has_extern_repr
{
1099 hir
::ItemTy(..) | hir
::ItemStruct(..) => {
1100 self.check_case(cx
, "type", it
.ident
, it
.span
)
1102 hir
::ItemTrait(..) => {
1103 self.check_case(cx
, "trait", it
.ident
, it
.span
)
1105 hir
::ItemEnum(ref enum_definition
, _
) => {
1106 if has_extern_repr
{
1109 self.check_case(cx
, "type", it
.ident
, it
.span
);
1110 for variant
in &enum_definition
.variants
{
1111 self.check_case(cx
, "variant", variant
.node
.name
, variant
.span
);
1118 fn check_generics(&mut self, cx
: &Context
, it
: &hir
::Generics
) {
1119 for gen
in it
.ty_params
.iter() {
1120 self.check_case(cx
, "type parameter", gen
.ident
, gen
.span
);
1125 #[derive(PartialEq)]
1126 enum MethodContext
{
1132 fn method_context(cx
: &Context
, id
: ast
::NodeId
, span
: Span
) -> MethodContext
{
1133 match cx
.tcx
.impl_or_trait_items
.borrow().get(&DefId
::local(id
)) {
1134 None
=> cx
.sess().span_bug(span
, "missing method descriptor?!"),
1135 Some(item
) => match item
.container() {
1136 ty
::TraitContainer(..) => MethodContext
::TraitDefaultImpl
,
1137 ty
::ImplContainer(cid
) => {
1138 match cx
.tcx
.impl_trait_ref(cid
) {
1139 Some(_
) => MethodContext
::TraitImpl
,
1140 None
=> MethodContext
::PlainImpl
1150 "methods, functions, lifetime parameters and modules should have snake case names"
1153 #[derive(Copy, Clone)]
1154 pub struct NonSnakeCase
;
1157 fn to_snake_case(mut str: &str) -> String
{
1158 let mut words
= vec
![];
1159 // Preserve leading underscores
1160 str = str.trim_left_matches(|c
: char| {
1162 words
.push(String
::new());
1168 for s
in str.split('_'
) {
1169 let mut last_upper
= false;
1170 let mut buf
= String
::new();
1174 for ch
in s
.chars() {
1175 if !buf
.is_empty() && buf
!= "'"
1176 && ch
.is_uppercase()
1179 buf
= String
::new();
1181 last_upper
= ch
.is_uppercase();
1182 buf
.extend(ch
.to_lowercase());
1189 fn check_snake_case(&self, cx
: &Context
, sort
: &str, name
: &str, span
: Option
<Span
>) {
1190 fn is_snake_case(ident
: &str) -> bool
{
1191 if ident
.is_empty() {
1194 let ident
= ident
.trim_left_matches('
\''
);
1195 let ident
= ident
.trim_matches('_'
);
1197 let mut allow_underscore
= true;
1198 ident
.chars().all(|c
| {
1199 allow_underscore
= match c
{
1200 '_'
if !allow_underscore
=> return false,
1202 // It would be more obvious to use `c.is_lowercase()`,
1203 // but some characters do not have a lowercase form
1204 c
if !c
.is_uppercase() => true,
1211 if !is_snake_case(name
) {
1212 let sc
= NonSnakeCase
::to_snake_case(name
);
1213 let msg
= if sc
!= name
{
1214 format
!("{} `{}` should have a snake case name such as `{}`",
1217 format
!("{} `{}` should have a snake case name",
1221 Some(span
) => cx
.span_lint(NON_SNAKE_CASE
, span
, &msg
),
1222 None
=> cx
.lint(NON_SNAKE_CASE
, &msg
),
1228 impl LintPass
for NonSnakeCase
{
1229 fn get_lints(&self) -> LintArray
{
1230 lint_array
!(NON_SNAKE_CASE
)
1233 fn check_crate(&mut self, cx
: &Context
, cr
: &hir
::Crate
) {
1234 let attr_crate_name
= cr
.attrs
.iter().find(|at
| at
.check_name("crate_name"))
1235 .and_then(|at
| at
.value_str().map(|s
| (at
, s
)));
1236 if let Some(ref name
) = cx
.tcx
.sess
.opts
.crate_name
{
1237 self.check_snake_case(cx
, "crate", name
, None
);
1238 } else if let Some((attr
, ref name
)) = attr_crate_name
{
1239 self.check_snake_case(cx
, "crate", name
, Some(attr
.span
));
1243 fn check_fn(&mut self, cx
: &Context
,
1244 fk
: FnKind
, _
: &hir
::FnDecl
,
1245 _
: &hir
::Block
, span
: Span
, id
: ast
::NodeId
) {
1247 FnKind
::Method(ident
, _
, _
) => match method_context(cx
, id
, span
) {
1248 MethodContext
::PlainImpl
=> {
1249 self.check_snake_case(cx
, "method", &ident
.name
.as_str(), Some(span
))
1251 MethodContext
::TraitDefaultImpl
=> {
1252 self.check_snake_case(cx
, "trait method", &ident
.name
.as_str(), Some(span
))
1256 FnKind
::ItemFn(ident
, _
, _
, _
, _
, _
) => {
1257 self.check_snake_case(cx
, "function", &ident
.name
.as_str(), Some(span
))
1263 fn check_item(&mut self, cx
: &Context
, it
: &hir
::Item
) {
1264 if let hir
::ItemMod(_
) = it
.node
{
1265 self.check_snake_case(cx
, "module", &it
.ident
.name
.as_str(), Some(it
.span
));
1269 fn check_trait_item(&mut self, cx
: &Context
, trait_item
: &hir
::TraitItem
) {
1270 if let hir
::MethodTraitItem(_
, None
) = trait_item
.node
{
1271 self.check_snake_case(cx
, "trait method", &trait_item
.ident
.name
.as_str(),
1272 Some(trait_item
.span
));
1276 fn check_lifetime_def(&mut self, cx
: &Context
, t
: &hir
::LifetimeDef
) {
1277 self.check_snake_case(cx
, "lifetime", &t
.lifetime
.name
.as_str(),
1278 Some(t
.lifetime
.span
));
1281 fn check_pat(&mut self, cx
: &Context
, p
: &hir
::Pat
) {
1282 if let &hir
::PatIdent(_
, ref path1
, _
) = &p
.node
{
1283 let def
= cx
.tcx
.def_map
.borrow().get(&p
.id
).map(|d
| d
.full_def());
1284 if let Some(def
::DefLocal(_
)) = def
{
1285 self.check_snake_case(cx
, "variable", &path1
.node
.name
.as_str(), Some(p
.span
));
1290 fn check_struct_def(&mut self, cx
: &Context
, s
: &hir
::StructDef
,
1291 _
: ast
::Ident
, _
: &hir
::Generics
, _
: ast
::NodeId
) {
1292 for sf
in &s
.fields
{
1293 if let hir
::StructField_ { kind: hir::NamedField(ident, _), .. }
= sf
.node
{
1294 self.check_snake_case(cx
, "structure field", &ident
.name
.as_str(),
1302 pub NON_UPPER_CASE_GLOBALS
,
1304 "static constants should have uppercase identifiers"
1307 #[derive(Copy, Clone)]
1308 pub struct NonUpperCaseGlobals
;
1310 impl NonUpperCaseGlobals
{
1311 fn check_upper_case(cx
: &Context
, sort
: &str, ident
: ast
::Ident
, span
: Span
) {
1312 let s
= ident
.name
.as_str();
1314 if s
.chars().any(|c
| c
.is_lowercase()) {
1315 let uc
= NonSnakeCase
::to_snake_case(&s
).to_uppercase();
1317 cx
.span_lint(NON_UPPER_CASE_GLOBALS
, span
,
1318 &format
!("{} `{}` should have an upper case name such as `{}`",
1321 cx
.span_lint(NON_UPPER_CASE_GLOBALS
, span
,
1322 &format
!("{} `{}` should have an upper case name",
1329 impl LintPass
for NonUpperCaseGlobals
{
1330 fn get_lints(&self) -> LintArray
{
1331 lint_array
!(NON_UPPER_CASE_GLOBALS
)
1334 fn check_item(&mut self, cx
: &Context
, it
: &hir
::Item
) {
1336 // only check static constants
1337 hir
::ItemStatic(_
, hir
::MutImmutable
, _
) => {
1338 NonUpperCaseGlobals
::check_upper_case(cx
, "static constant", it
.ident
, it
.span
);
1340 hir
::ItemConst(..) => {
1341 NonUpperCaseGlobals
::check_upper_case(cx
, "constant", it
.ident
, it
.span
);
1347 fn check_trait_item(&mut self, cx
: &Context
, ti
: &hir
::TraitItem
) {
1349 hir
::ConstTraitItem(..) => {
1350 NonUpperCaseGlobals
::check_upper_case(cx
, "associated constant",
1357 fn check_impl_item(&mut self, cx
: &Context
, ii
: &hir
::ImplItem
) {
1359 hir
::ConstImplItem(..) => {
1360 NonUpperCaseGlobals
::check_upper_case(cx
, "associated constant",
1367 fn check_pat(&mut self, cx
: &Context
, p
: &hir
::Pat
) {
1368 // Lint for constants that look like binding identifiers (#7526)
1369 match (&p
.node
, cx
.tcx
.def_map
.borrow().get(&p
.id
).map(|d
| d
.full_def())) {
1370 (&hir
::PatIdent(_
, ref path1
, _
), Some(def
::DefConst(..))) => {
1371 NonUpperCaseGlobals
::check_upper_case(cx
, "constant in pattern",
1372 path1
.node
, p
.span
);
1382 "`if`, `match`, `while` and `return` do not need parentheses"
1385 #[derive(Copy, Clone)]
1386 pub struct UnusedParens
;
1389 fn check_unused_parens_core(&self, cx
: &Context
, value
: &hir
::Expr
, msg
: &str,
1390 struct_lit_needs_parens
: bool
) {
1391 if let hir
::ExprParen(ref inner
) = value
.node
{
1392 let necessary
= struct_lit_needs_parens
&& contains_exterior_struct_lit(&**inner
);
1394 cx
.span_lint(UNUSED_PARENS
, value
.span
,
1395 &format
!("unnecessary parentheses around {}", msg
))
1399 /// Expressions that syntactically contain an "exterior" struct
1400 /// literal i.e. not surrounded by any parens or other
1401 /// delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo
1402 /// == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X {
1403 /// y: 1 }) == foo` does not.
1404 fn contains_exterior_struct_lit(value
: &hir
::Expr
) -> bool
{
1406 hir
::ExprStruct(..) => true,
1408 hir
::ExprAssign(ref lhs
, ref rhs
) |
1409 hir
::ExprAssignOp(_
, ref lhs
, ref rhs
) |
1410 hir
::ExprBinary(_
, ref lhs
, ref rhs
) => {
1411 // X { y: 1 } + X { y: 2 }
1412 contains_exterior_struct_lit(&**lhs
) ||
1413 contains_exterior_struct_lit(&**rhs
)
1415 hir
::ExprUnary(_
, ref x
) |
1416 hir
::ExprCast(ref x
, _
) |
1417 hir
::ExprField(ref x
, _
) |
1418 hir
::ExprTupField(ref x
, _
) |
1419 hir
::ExprIndex(ref x
, _
) => {
1420 // &X { y: 1 }, X { y: 1 }.y
1421 contains_exterior_struct_lit(&**x
)
1424 hir
::ExprMethodCall(_
, _
, ref exprs
) => {
1425 // X { y: 1 }.bar(...)
1426 contains_exterior_struct_lit(&*exprs
[0])
1435 impl LintPass
for UnusedParens
{
1436 fn get_lints(&self) -> LintArray
{
1437 lint_array
!(UNUSED_PARENS
)
1440 fn check_expr(&mut self, cx
: &Context
, e
: &hir
::Expr
) {
1441 let (value
, msg
, struct_lit_needs_parens
) = match e
.node
{
1442 hir
::ExprIf(ref cond
, _
, _
) => (cond
, "`if` condition", true),
1443 hir
::ExprWhile(ref cond
, _
, _
) => (cond
, "`while` condition", true),
1444 hir
::ExprMatch(ref head
, _
, source
) => match source
{
1445 hir
::MatchSource
::Normal
=> (head
, "`match` head expression", true),
1446 hir
::MatchSource
::IfLetDesugar { .. }
=> (head
, "`if let` head expression", true),
1447 hir
::MatchSource
::WhileLetDesugar
=> (head
, "`while let` head expression", true),
1448 hir
::MatchSource
::ForLoopDesugar
=> (head
, "`for` head expression", true),
1450 hir
::ExprRet(Some(ref value
)) => (value
, "`return` value", false),
1451 hir
::ExprAssign(_
, ref value
) => (value
, "assigned value", false),
1452 hir
::ExprAssignOp(_
, _
, ref value
) => (value
, "assigned value", false),
1455 self.check_unused_parens_core(cx
, &**value
, msg
, struct_lit_needs_parens
);
1458 fn check_stmt(&mut self, cx
: &Context
, s
: &hir
::Stmt
) {
1459 let (value
, msg
) = match s
.node
{
1460 hir
::StmtDecl(ref decl
, _
) => match decl
.node
{
1461 hir
::DeclLocal(ref local
) => match local
.init
{
1462 Some(ref value
) => (value
, "assigned value"),
1469 self.check_unused_parens_core(cx
, &**value
, msg
, false);
1474 UNUSED_IMPORT_BRACES
,
1476 "unnecessary braces around an imported item"
1479 #[derive(Copy, Clone)]
1480 pub struct UnusedImportBraces
;
1482 impl LintPass
for UnusedImportBraces
{
1483 fn get_lints(&self) -> LintArray
{
1484 lint_array
!(UNUSED_IMPORT_BRACES
)
1487 fn check_item(&mut self, cx
: &Context
, item
: &hir
::Item
) {
1488 if let hir
::ItemUse(ref view_path
) = item
.node
{
1489 if let hir
::ViewPathList(_
, ref items
) = view_path
.node
{
1490 if items
.len() == 1 {
1491 if let hir
::PathListIdent {ref name, ..}
= items
[0].node
{
1492 let m
= format
!("braces around {} is unnecessary",
1494 cx
.span_lint(UNUSED_IMPORT_BRACES
, item
.span
,
1504 NON_SHORTHAND_FIELD_PATTERNS
,
1506 "using `Struct { x: x }` instead of `Struct { x }`"
1509 #[derive(Copy, Clone)]
1510 pub struct NonShorthandFieldPatterns
;
1512 impl LintPass
for NonShorthandFieldPatterns
{
1513 fn get_lints(&self) -> LintArray
{
1514 lint_array
!(NON_SHORTHAND_FIELD_PATTERNS
)
1517 fn check_pat(&mut self, cx
: &Context
, pat
: &hir
::Pat
) {
1518 let def_map
= cx
.tcx
.def_map
.borrow();
1519 if let hir
::PatStruct(_
, ref v
, _
) = pat
.node
{
1520 let field_pats
= v
.iter().filter(|fieldpat
| {
1521 if fieldpat
.node
.is_shorthand
{
1524 let def
= def_map
.get(&fieldpat
.node
.pat
.id
).map(|d
| d
.full_def());
1525 def
== Some(def
::DefLocal(fieldpat
.node
.pat
.id
))
1527 for fieldpat
in field_pats
{
1528 if let hir
::PatIdent(_
, ident
, None
) = fieldpat
.node
.pat
.node
{
1529 if ident
.node
.name
== fieldpat
.node
.ident
.name
{
1530 // FIXME: should this comparison really be done on the name?
1531 // doing it on the ident will fail during compilation of libcore
1532 cx
.span_lint(NON_SHORTHAND_FIELD_PATTERNS
, fieldpat
.span
,
1533 &format
!("the `{}:` in this pattern is redundant and can \
1534 be removed", ident
.node
))
1545 "unnecessary use of an `unsafe` block"
1548 #[derive(Copy, Clone)]
1549 pub struct UnusedUnsafe
;
1551 impl LintPass
for UnusedUnsafe
{
1552 fn get_lints(&self) -> LintArray
{
1553 lint_array
!(UNUSED_UNSAFE
)
1556 fn check_expr(&mut self, cx
: &Context
, e
: &hir
::Expr
) {
1557 if let hir
::ExprBlock(ref blk
) = e
.node
{
1558 // Don't warn about generated blocks, that'll just pollute the output.
1559 if blk
.rules
== hir
::UnsafeBlock(hir
::UserProvided
) &&
1560 !cx
.tcx
.used_unsafe
.borrow().contains(&blk
.id
) {
1561 cx
.span_lint(UNUSED_UNSAFE
, blk
.span
, "unnecessary `unsafe` block");
1570 "usage of `unsafe` code"
1573 #[derive(Copy, Clone)]
1574 pub struct UnsafeCode
;
1576 impl LintPass
for UnsafeCode
{
1577 fn get_lints(&self) -> LintArray
{
1578 lint_array
!(UNSAFE_CODE
)
1581 fn check_expr(&mut self, cx
: &Context
, e
: &hir
::Expr
) {
1582 if let hir
::ExprBlock(ref blk
) = e
.node
{
1583 // Don't warn about generated blocks, that'll just pollute the output.
1584 if blk
.rules
== hir
::UnsafeBlock(hir
::UserProvided
) {
1585 cx
.span_lint(UNSAFE_CODE
, blk
.span
, "usage of an `unsafe` block");
1590 fn check_item(&mut self, cx
: &Context
, it
: &hir
::Item
) {
1592 hir
::ItemTrait(hir
::Unsafety
::Unsafe
, _
, _
, _
) =>
1593 cx
.span_lint(UNSAFE_CODE
, it
.span
, "declaration of an `unsafe` trait"),
1595 hir
::ItemImpl(hir
::Unsafety
::Unsafe
, _
, _
, _
, _
, _
) =>
1596 cx
.span_lint(UNSAFE_CODE
, it
.span
, "implementation of an `unsafe` trait"),
1602 fn check_fn(&mut self, cx
: &Context
, fk
: FnKind
, _
: &hir
::FnDecl
,
1603 _
: &hir
::Block
, span
: Span
, _
: ast
::NodeId
) {
1605 FnKind
::ItemFn(_
, _
, hir
::Unsafety
::Unsafe
, _
, _
, _
) =>
1606 cx
.span_lint(UNSAFE_CODE
, span
, "declaration of an `unsafe` function"),
1608 FnKind
::Method(_
, sig
, _
) => {
1609 if sig
.unsafety
== hir
::Unsafety
::Unsafe
{
1610 cx
.span_lint(UNSAFE_CODE
, span
, "implementation of an `unsafe` method")
1618 fn check_trait_item(&mut self, cx
: &Context
, trait_item
: &hir
::TraitItem
) {
1619 if let hir
::MethodTraitItem(ref sig
, None
) = trait_item
.node
{
1620 if sig
.unsafety
== hir
::Unsafety
::Unsafe
{
1621 cx
.span_lint(UNSAFE_CODE
, trait_item
.span
,
1622 "declaration of an `unsafe` method")
1631 "detect mut variables which don't need to be mutable"
1634 #[derive(Copy, Clone)]
1635 pub struct UnusedMut
;
1638 fn check_unused_mut_pat(&self, cx
: &Context
, pats
: &[P
<hir
::Pat
>]) {
1639 // collect all mutable pattern and group their NodeIDs by their Identifier to
1640 // avoid false warnings in match arms with multiple patterns
1642 let mut mutables
= FnvHashMap();
1644 pat_util
::pat_bindings(&cx
.tcx
.def_map
, p
, |mode
, id
, _
, path1
| {
1645 let ident
= path1
.node
;
1646 if let hir
::BindByValue(hir
::MutMutable
) = mode
{
1647 if !ident
.name
.as_str().starts_with("_") {
1648 match mutables
.entry(ident
.name
.usize()) {
1649 Vacant(entry
) => { entry.insert(vec![id]); }
,
1650 Occupied(mut entry
) => { entry.get_mut().push(id); }
,
1657 let used_mutables
= cx
.tcx
.used_mut_nodes
.borrow();
1658 for (_
, v
) in &mutables
{
1659 if !v
.iter().any(|e
| used_mutables
.contains(e
)) {
1660 cx
.span_lint(UNUSED_MUT
, cx
.tcx
.map
.span(v
[0]),
1661 "variable does not need to be mutable");
1667 impl LintPass
for UnusedMut
{
1668 fn get_lints(&self) -> LintArray
{
1669 lint_array
!(UNUSED_MUT
)
1672 fn check_expr(&mut self, cx
: &Context
, e
: &hir
::Expr
) {
1673 if let hir
::ExprMatch(_
, ref arms
, _
) = e
.node
{
1675 self.check_unused_mut_pat(cx
, &a
.pats
)
1680 fn check_stmt(&mut self, cx
: &Context
, s
: &hir
::Stmt
) {
1681 if let hir
::StmtDecl(ref d
, _
) = s
.node
{
1682 if let hir
::DeclLocal(ref l
) = d
.node
{
1683 self.check_unused_mut_pat(cx
, slice
::ref_slice(&l
.pat
));
1688 fn check_fn(&mut self, cx
: &Context
,
1689 _
: FnKind
, decl
: &hir
::FnDecl
,
1690 _
: &hir
::Block
, _
: Span
, _
: ast
::NodeId
) {
1691 for a
in &decl
.inputs
{
1692 self.check_unused_mut_pat(cx
, slice
::ref_slice(&a
.pat
));
1700 "detects unnecessary allocations that can be eliminated"
1703 #[derive(Copy, Clone)]
1704 pub struct UnusedAllocation
;
1706 impl LintPass
for UnusedAllocation
{
1707 fn get_lints(&self) -> LintArray
{
1708 lint_array
!(UNUSED_ALLOCATION
)
1711 fn check_expr(&mut self, cx
: &Context
, e
: &hir
::Expr
) {
1713 hir
::ExprUnary(hir
::UnUniq
, _
) => (),
1717 if let Some(adjustment
) = cx
.tcx
.tables
.borrow().adjustments
.get(&e
.id
) {
1718 if let adjustment
::AdjustDerefRef(adjustment
::AutoDerefRef
{
1722 &Some(adjustment
::AutoPtr(_
, hir
::MutImmutable
)) => {
1723 cx
.span_lint(UNUSED_ALLOCATION
, e
.span
,
1724 "unnecessary allocation, use & instead");
1726 &Some(adjustment
::AutoPtr(_
, hir
::MutMutable
)) => {
1727 cx
.span_lint(UNUSED_ALLOCATION
, e
.span
,
1728 "unnecessary allocation, use &mut instead");
1740 "detects missing documentation for public members"
1743 pub struct MissingDoc
{
1744 /// Stack of IDs of struct definitions.
1745 struct_def_stack
: Vec
<ast
::NodeId
>,
1747 /// True if inside variant definition
1750 /// Stack of whether #[doc(hidden)] is set
1751 /// at each level which has lint attributes.
1752 doc_hidden_stack
: Vec
<bool
>,
1754 /// Private traits or trait items that leaked through. Don't check their methods.
1755 private_traits
: HashSet
<ast
::NodeId
>,
1759 pub fn new() -> MissingDoc
{
1761 struct_def_stack
: vec
!(),
1763 doc_hidden_stack
: vec
!(false),
1764 private_traits
: HashSet
::new(),
1768 fn doc_hidden(&self) -> bool
{
1769 *self.doc_hidden_stack
.last().expect("empty doc_hidden_stack")
1772 fn check_missing_docs_attrs(&self,
1774 id
: Option
<ast
::NodeId
>,
1775 attrs
: &[hir
::Attribute
],
1777 desc
: &'
static str) {
1778 // If we're building a test harness, then warning about
1779 // documentation is probably not really relevant right now.
1780 if cx
.sess().opts
.test
{
1784 // `#[doc(hidden)]` disables missing_docs check.
1785 if self.doc_hidden() {
1789 // Only check publicly-visible items, using the result from the privacy pass.
1790 // It's an option so the crate root can also use this function (it doesn't
1792 if let Some(ref id
) = id
{
1793 if !cx
.exported_items
.contains(id
) {
1798 let has_doc
= attrs
.iter().any(|a
| {
1799 match a
.node
.value
.node
{
1800 hir
::MetaNameValue(ref name
, _
) if *name
== "doc" => true,
1805 cx
.span_lint(MISSING_DOCS
, sp
,
1806 &format
!("missing documentation for {}", desc
));
1811 impl LintPass
for MissingDoc
{
1812 fn get_lints(&self) -> LintArray
{
1813 lint_array
!(MISSING_DOCS
)
1816 fn enter_lint_attrs(&mut self, _
: &Context
, attrs
: &[hir
::Attribute
]) {
1817 let doc_hidden
= self.doc_hidden() || attrs
.iter().any(|attr
| {
1818 attr
.check_name("doc") && match attr
.meta_item_list() {
1820 Some(l
) => attr
::contains_name(&l
[..], "hidden"),
1823 self.doc_hidden_stack
.push(doc_hidden
);
1826 fn exit_lint_attrs(&mut self, _
: &Context
, _
: &[hir
::Attribute
]) {
1827 self.doc_hidden_stack
.pop().expect("empty doc_hidden_stack");
1830 fn check_struct_def(&mut self, _
: &Context
, _
: &hir
::StructDef
,
1831 _
: ast
::Ident
, _
: &hir
::Generics
, id
: ast
::NodeId
) {
1832 self.struct_def_stack
.push(id
);
1835 fn check_struct_def_post(&mut self, _
: &Context
, _
: &hir
::StructDef
,
1836 _
: ast
::Ident
, _
: &hir
::Generics
, id
: ast
::NodeId
) {
1837 let popped
= self.struct_def_stack
.pop().expect("empty struct_def_stack");
1838 assert
!(popped
== id
);
1841 fn check_crate(&mut self, cx
: &Context
, krate
: &hir
::Crate
) {
1842 self.check_missing_docs_attrs(cx
, None
, &krate
.attrs
, krate
.span
, "crate");
1845 fn check_item(&mut self, cx
: &Context
, it
: &hir
::Item
) {
1846 let desc
= match it
.node
{
1847 hir
::ItemFn(..) => "a function",
1848 hir
::ItemMod(..) => "a module",
1849 hir
::ItemEnum(..) => "an enum",
1850 hir
::ItemStruct(..) => "a struct",
1851 hir
::ItemTrait(_
, _
, _
, ref items
) => {
1852 // Issue #11592, traits are always considered exported, even when private.
1853 if it
.vis
== hir
::Visibility
::Inherited
{
1854 self.private_traits
.insert(it
.id
);
1856 self.private_traits
.insert(itm
.id
);
1862 hir
::ItemTy(..) => "a type alias",
1863 hir
::ItemImpl(_
, _
, _
, Some(ref trait_ref
), _
, ref impl_items
) => {
1864 // If the trait is private, add the impl items to private_traits so they don't get
1865 // reported for missing docs.
1866 let real_trait
= cx
.tcx
.trait_ref_to_def_id(trait_ref
);
1867 match cx
.tcx
.map
.find(real_trait
.node
) {
1868 Some(hir_map
::NodeItem(item
)) => if item
.vis
== hir
::Visibility
::Inherited
{
1869 for itm
in impl_items
{
1870 self.private_traits
.insert(itm
.id
);
1877 hir
::ItemConst(..) => "a constant",
1878 hir
::ItemStatic(..) => "a static",
1882 self.check_missing_docs_attrs(cx
, Some(it
.id
), &it
.attrs
, it
.span
, desc
);
1885 fn check_trait_item(&mut self, cx
: &Context
, trait_item
: &hir
::TraitItem
) {
1886 if self.private_traits
.contains(&trait_item
.id
) { return }
1888 let desc
= match trait_item
.node
{
1889 hir
::ConstTraitItem(..) => "an associated constant",
1890 hir
::MethodTraitItem(..) => "a trait method",
1891 hir
::TypeTraitItem(..) => "an associated type",
1894 self.check_missing_docs_attrs(cx
, Some(trait_item
.id
),
1896 trait_item
.span
, desc
);
1899 fn check_impl_item(&mut self, cx
: &Context
, impl_item
: &hir
::ImplItem
) {
1900 // If the method is an impl for a trait, don't doc.
1901 if method_context(cx
, impl_item
.id
, impl_item
.span
) == MethodContext
::TraitImpl
{
1905 let desc
= match impl_item
.node
{
1906 hir
::ConstImplItem(..) => "an associated constant",
1907 hir
::MethodImplItem(..) => "a method",
1908 hir
::TypeImplItem(_
) => "an associated type",
1910 self.check_missing_docs_attrs(cx
, Some(impl_item
.id
),
1912 impl_item
.span
, desc
);
1915 fn check_struct_field(&mut self, cx
: &Context
, sf
: &hir
::StructField
) {
1916 if let hir
::NamedField(_
, vis
) = sf
.node
.kind
{
1917 if vis
== hir
::Public
|| self.in_variant
{
1918 let cur_struct_def
= *self.struct_def_stack
.last()
1919 .expect("empty struct_def_stack");
1920 self.check_missing_docs_attrs(cx
, Some(cur_struct_def
),
1921 &sf
.node
.attrs
, sf
.span
,
1927 fn check_variant(&mut self, cx
: &Context
, v
: &hir
::Variant
, _
: &hir
::Generics
) {
1928 self.check_missing_docs_attrs(cx
, Some(v
.node
.id
), &v
.node
.attrs
, v
.span
, "a variant");
1929 assert
!(!self.in_variant
);
1930 self.in_variant
= true;
1933 fn check_variant_post(&mut self, _
: &Context
, _
: &hir
::Variant
, _
: &hir
::Generics
) {
1934 assert
!(self.in_variant
);
1935 self.in_variant
= false;
1940 pub MISSING_COPY_IMPLEMENTATIONS
,
1942 "detects potentially-forgotten implementations of `Copy`"
1945 #[derive(Copy, Clone)]
1946 pub struct MissingCopyImplementations
;
1948 impl LintPass
for MissingCopyImplementations
{
1949 fn get_lints(&self) -> LintArray
{
1950 lint_array
!(MISSING_COPY_IMPLEMENTATIONS
)
1953 fn check_item(&mut self, cx
: &Context
, item
: &hir
::Item
) {
1954 if !cx
.exported_items
.contains(&item
.id
) {
1957 let (def
, ty
) = match item
.node
{
1958 hir
::ItemStruct(_
, ref ast_generics
) => {
1959 if ast_generics
.is_parameterized() {
1962 let def
= cx
.tcx
.lookup_adt_def(DefId
::local(item
.id
));
1963 (def
, cx
.tcx
.mk_struct(def
,
1964 cx
.tcx
.mk_substs(Substs
::empty())))
1966 hir
::ItemEnum(_
, ref ast_generics
) => {
1967 if ast_generics
.is_parameterized() {
1970 let def
= cx
.tcx
.lookup_adt_def(DefId
::local(item
.id
));
1971 (def
, cx
.tcx
.mk_enum(def
,
1972 cx
.tcx
.mk_substs(Substs
::empty())))
1976 if def
.has_dtor() { return; }
1977 let parameter_environment
= cx
.tcx
.empty_parameter_environment();
1978 // FIXME (@jroesch) should probably inver this so that the parameter env still impls this
1980 if !ty
.moves_by_default(¶meter_environment
, item
.span
) {
1983 if parameter_environment
.can_type_implement_copy(ty
, item
.span
).is_ok() {
1984 cx
.span_lint(MISSING_COPY_IMPLEMENTATIONS
,
1986 "type could implement `Copy`; consider adding `impl \
1993 MISSING_DEBUG_IMPLEMENTATIONS
,
1995 "detects missing implementations of fmt::Debug"
1998 pub struct MissingDebugImplementations
{
1999 impling_types
: Option
<NodeSet
>,
2002 impl MissingDebugImplementations
{
2003 pub fn new() -> MissingDebugImplementations
{
2004 MissingDebugImplementations
{
2005 impling_types
: None
,
2010 impl LintPass
for MissingDebugImplementations
{
2011 fn get_lints(&self) -> LintArray
{
2012 lint_array
!(MISSING_DEBUG_IMPLEMENTATIONS
)
2015 fn check_item(&mut self, cx
: &Context
, item
: &hir
::Item
) {
2016 if !cx
.exported_items
.contains(&item
.id
) {
2021 hir
::ItemStruct(..) | hir
::ItemEnum(..) => {}
,
2025 let debug
= match cx
.tcx
.lang_items
.debug_trait() {
2026 Some(debug
) => debug
,
2030 if self.impling_types
.is_none() {
2031 let debug_def
= cx
.tcx
.lookup_trait_def(debug
);
2032 let mut impls
= NodeSet();
2033 debug_def
.for_each_impl(cx
.tcx
, |d
| {
2035 if let Some(ty_def
) = cx
.tcx
.node_id_to_type(d
.node
).ty_to_def_id() {
2036 impls
.insert(ty_def
.node
);
2041 self.impling_types
= Some(impls
);
2042 debug
!("{:?}", self.impling_types
);
2045 if !self.impling_types
.as_ref().unwrap().contains(&item
.id
) {
2046 cx
.span_lint(MISSING_DEBUG_IMPLEMENTATIONS
,
2048 "type does not implement `fmt::Debug`; consider adding #[derive(Debug)] \
2049 or a manual implementation")
2057 "detects use of #[deprecated] items"
2060 /// Checks for use of items with `#[deprecated]` attributes
2061 #[derive(Copy, Clone)]
2062 pub struct Stability
;
2065 fn lint(&self, cx
: &Context
, _id
: DefId
,
2066 span
: Span
, stability
: &Option
<&attr
::Stability
>) {
2067 // Deprecated attributes apply in-crate and cross-crate.
2068 let (lint
, label
) = match *stability
{
2069 Some(&attr
::Stability { deprecated_since: Some(_), .. }
) =>
2070 (DEPRECATED
, "deprecated"),
2074 output(cx
, span
, stability
, lint
, label
);
2076 fn output(cx
: &Context
, span
: Span
, stability
: &Option
<&attr
::Stability
>,
2077 lint
: &'
static Lint
, label
: &'
static str) {
2078 let msg
= match *stability
{
2079 Some(&attr
::Stability { reason: Some(ref s), .. }
) => {
2080 format
!("use of {} item: {}", label
, *s
)
2082 _
=> format
!("use of {} item", label
)
2085 cx
.span_lint(lint
, span
, &msg
[..]);
2090 fn hir_to_ast_stability(stab
: &attr
::Stability
) -> attr
::Stability
{
2092 level
: match stab
.level
{
2093 attr
::Unstable
=> attr
::Unstable
,
2094 attr
::Stable
=> attr
::Stable
,
2096 feature
: stab
.feature
.clone(),
2097 since
: stab
.since
.clone(),
2098 deprecated_since
: stab
.deprecated_since
.clone(),
2099 reason
: stab
.reason
.clone(),
2104 impl LintPass
for Stability
{
2105 fn get_lints(&self) -> LintArray
{
2106 lint_array
!(DEPRECATED
)
2109 fn check_item(&mut self, cx
: &Context
, item
: &hir
::Item
) {
2110 stability
::check_item(cx
.tcx
, item
, false,
2112 self.lint(cx
, id
, sp
,
2113 &stab
.map(|s
| hir_to_ast_stability(s
)).as_ref()));
2116 fn check_expr(&mut self, cx
: &Context
, e
: &hir
::Expr
) {
2117 stability
::check_expr(cx
.tcx
, e
,
2119 self.lint(cx
, id
, sp
,
2120 &stab
.map(|s
| hir_to_ast_stability(s
)).as_ref()));
2123 fn check_path(&mut self, cx
: &Context
, path
: &hir
::Path
, id
: ast
::NodeId
) {
2124 stability
::check_path(cx
.tcx
, path
, id
,
2126 self.lint(cx
, id
, sp
,
2127 &stab
.map(|s
| hir_to_ast_stability(s
)).as_ref()));
2130 fn check_pat(&mut self, cx
: &Context
, pat
: &hir
::Pat
) {
2131 stability
::check_pat(cx
.tcx
, pat
,
2133 self.lint(cx
, id
, sp
,
2134 &stab
.map(|s
| hir_to_ast_stability(s
)).as_ref()));
2139 pub UNCONDITIONAL_RECURSION
,
2141 "functions that cannot return without calling themselves"
2144 #[derive(Copy, Clone)]
2145 pub struct UnconditionalRecursion
;
2148 impl LintPass
for UnconditionalRecursion
{
2149 fn get_lints(&self) -> LintArray
{
2150 lint_array
![UNCONDITIONAL_RECURSION
]
2153 fn check_fn(&mut self, cx
: &Context
, fn_kind
: FnKind
, _
: &hir
::FnDecl
,
2154 blk
: &hir
::Block
, sp
: Span
, id
: ast
::NodeId
) {
2155 type F
= for<'tcx
> fn(&ty
::ctxt
<'tcx
>,
2156 ast
::NodeId
, ast
::NodeId
, ast
::Ident
, ast
::NodeId
) -> bool
;
2158 let method
= match fn_kind
{
2159 FnKind
::ItemFn(..) => None
,
2160 FnKind
::Method(..) => {
2161 cx
.tcx
.impl_or_trait_item(DefId
::local(id
)).as_opt_method()
2163 // closures can't recur, so they don't matter.
2164 FnKind
::Closure
=> return
2167 // Walk through this function (say `f`) looking to see if
2168 // every possible path references itself, i.e. the function is
2169 // called recursively unconditionally. This is done by trying
2170 // to find a path from the entry node to the exit node that
2171 // *doesn't* call `f` by traversing from the entry while
2172 // pretending that calls of `f` are sinks (i.e. ignoring any
2173 // exit edges from them).
2175 // NB. this has an edge case with non-returning statements,
2176 // like `loop {}` or `panic!()`: control flow never reaches
2177 // the exit node through these, so one can have a function
2178 // that never actually calls itselfs but is still picked up by
2181 // fn f(cond: bool) {
2182 // if !cond { panic!() } // could come from `assert!(cond)`
2186 // In general, functions of that form may be able to call
2187 // itself a finite number of times and then diverge. The lint
2188 // considers this to be an error for two reasons, (a) it is
2189 // easier to implement, and (b) it seems rare to actually want
2190 // to have behaviour like the above, rather than
2191 // e.g. accidentally recurring after an assert.
2193 let cfg
= cfg
::CFG
::new(cx
.tcx
, blk
);
2195 let mut work_queue
= vec
![cfg
.entry
];
2196 let mut reached_exit_without_self_call
= false;
2197 let mut self_call_spans
= vec
![];
2198 let mut visited
= HashSet
::new();
2200 while let Some(idx
) = work_queue
.pop() {
2201 if idx
== cfg
.exit
{
2203 reached_exit_without_self_call
= true;
2207 let cfg_id
= idx
.node_id();
2208 if visited
.contains(&cfg_id
) {
2212 visited
.insert(cfg_id
);
2214 let node_id
= cfg
.graph
.node_data(idx
).id();
2216 // is this a recursive call?
2217 let self_recursive
= if node_id
!= ast
::DUMMY_NODE_ID
{
2219 Some(ref method
) => {
2220 expr_refers_to_this_method(cx
.tcx
, method
, node_id
)
2222 None
=> expr_refers_to_this_fn(cx
.tcx
, id
, node_id
)
2228 self_call_spans
.push(cx
.tcx
.map
.span(node_id
));
2229 // this is a self call, so we shouldn't explore past
2230 // this node in the CFG.
2233 // add the successors of this node to explore the graph further.
2234 for (_
, edge
) in cfg
.graph
.outgoing_edges(idx
) {
2235 let target_idx
= edge
.target();
2236 let target_cfg_id
= target_idx
.node_id();
2237 if !visited
.contains(&target_cfg_id
) {
2238 work_queue
.push(target_idx
)
2243 // Check the number of self calls because a function that
2244 // doesn't return (e.g. calls a `-> !` function or `loop { /*
2245 // no break */ }`) shouldn't be linted unless it actually
2247 if !reached_exit_without_self_call
&& !self_call_spans
.is_empty() {
2248 cx
.span_lint(UNCONDITIONAL_RECURSION
, sp
,
2249 "function cannot return without recurring");
2251 // FIXME #19668: these could be span_lint_note's instead of this manual guard.
2252 if cx
.current_level(UNCONDITIONAL_RECURSION
) != Level
::Allow
{
2253 let sess
= cx
.sess();
2254 // offer some help to the programmer.
2255 for call
in &self_call_spans
{
2256 sess
.span_note(*call
, "recursive call site")
2258 sess
.fileline_help(sp
, "a `loop` may express intention \
2259 better if this is on purpose")
2266 // Functions for identifying if the given Expr NodeId `id`
2267 // represents a call to the function `fn_id`/method `method`.
2269 fn expr_refers_to_this_fn(tcx
: &ty
::ctxt
,
2271 id
: ast
::NodeId
) -> bool
{
2272 match tcx
.map
.get(id
) {
2273 hir_map
::NodeExpr(&hir
::Expr { node: hir::ExprCall(ref callee, _), .. }
) => {
2274 tcx
.def_map
.borrow().get(&callee
.id
)
2275 .map_or(false, |def
| def
.def_id() == DefId
::local(fn_id
))
2281 // Check if the expression `id` performs a call to `method`.
2282 fn expr_refers_to_this_method(tcx
: &ty
::ctxt
,
2283 method
: &ty
::Method
,
2284 id
: ast
::NodeId
) -> bool
{
2285 let tables
= tcx
.tables
.borrow();
2287 // Check for method calls and overloaded operators.
2288 if let Some(m
) = tables
.method_map
.get(&ty
::MethodCall
::expr(id
)) {
2289 if method_call_refers_to_method(tcx
, method
, m
.def_id
, m
.substs
, id
) {
2294 // Check for overloaded autoderef method calls.
2295 if let Some(&adjustment
::AdjustDerefRef(ref adj
)) = tables
.adjustments
.get(&id
) {
2296 for i
in 0..adj
.autoderefs
{
2297 let method_call
= ty
::MethodCall
::autoderef(id
, i
as u32);
2298 if let Some(m
) = tables
.method_map
.get(&method_call
) {
2299 if method_call_refers_to_method(tcx
, method
, m
.def_id
, m
.substs
, id
) {
2306 // Check for calls to methods via explicit paths (e.g. `T::method()`).
2307 match tcx
.map
.get(id
) {
2308 hir_map
::NodeExpr(&hir
::Expr { node: hir::ExprCall(ref callee, _), .. }
) => {
2309 match tcx
.def_map
.borrow().get(&callee
.id
).map(|d
| d
.full_def()) {
2310 Some(def
::DefMethod(def_id
)) => {
2311 let no_substs
= &ty
::ItemSubsts
::empty();
2312 let ts
= tables
.item_substs
.get(&callee
.id
).unwrap_or(no_substs
);
2313 method_call_refers_to_method(tcx
, method
, def_id
, &ts
.substs
, id
)
2322 // Check if the method call to the method with the ID `callee_id`
2323 // and instantiated with `callee_substs` refers to method `method`.
2324 fn method_call_refers_to_method
<'tcx
>(tcx
: &ty
::ctxt
<'tcx
>,
2325 method
: &ty
::Method
,
2327 callee_substs
: &Substs
<'tcx
>,
2328 expr_id
: ast
::NodeId
) -> bool
{
2329 let callee_item
= tcx
.impl_or_trait_item(callee_id
);
2331 match callee_item
.container() {
2332 // This is an inherent method, so the `def_id` refers
2333 // directly to the method definition.
2334 ty
::ImplContainer(_
) => {
2335 callee_id
== method
.def_id
2338 // A trait method, from any number of possible sources.
2339 // Attempt to select a concrete impl before checking.
2340 ty
::TraitContainer(trait_def_id
) => {
2341 let trait_substs
= callee_substs
.clone().method_to_trait();
2342 let trait_substs
= tcx
.mk_substs(trait_substs
);
2343 let trait_ref
= ty
::TraitRef
::new(trait_def_id
, trait_substs
);
2344 let trait_ref
= ty
::Binder(trait_ref
);
2345 let span
= tcx
.map
.span(expr_id
);
2347 traits
::Obligation
::new(traits
::ObligationCause
::misc(span
, expr_id
),
2348 trait_ref
.to_poly_trait_predicate());
2350 let param_env
= ty
::ParameterEnvironment
::for_item(tcx
, method
.def_id
.node
);
2351 let infcx
= infer
::new_infer_ctxt(tcx
, &tcx
.tables
, Some(param_env
), false);
2352 let mut selcx
= traits
::SelectionContext
::new(&infcx
);
2353 match selcx
.select(&obligation
) {
2354 // The method comes from a `T: Trait` bound.
2355 // If `T` is `Self`, then this call is inside
2356 // a default method definition.
2357 Ok(Some(traits
::VtableParam(_
))) => {
2358 let self_ty
= callee_substs
.self_ty();
2359 let on_self
= self_ty
.map_or(false, |t
| t
.is_self());
2360 // We can only be recurring in a default
2361 // method if we're being called literally
2362 // on the `Self` type.
2363 on_self
&& callee_id
== method
.def_id
2366 // The `impl` is known, so we check that with a
2368 Ok(Some(traits
::VtableImpl(vtable_impl
))) => {
2369 let container
= ty
::ImplContainer(vtable_impl
.impl_def_id
);
2370 // It matches if it comes from the same impl,
2371 // and has the same method name.
2372 container
== method
.container
2373 && callee_item
.name() == method
.name
2376 // There's no way to know if this call is
2377 // recursive, so we assume it's not.
2389 "compiler plugin used as ordinary library in non-plugin crate"
2392 #[derive(Copy, Clone)]
2393 pub struct PluginAsLibrary
;
2395 impl LintPass
for PluginAsLibrary
{
2396 fn get_lints(&self) -> LintArray
{
2397 lint_array
![PLUGIN_AS_LIBRARY
]
2400 fn check_item(&mut self, cx
: &Context
, it
: &hir
::Item
) {
2401 if cx
.sess().plugin_registrar_fn
.get().is_some() {
2402 // We're compiling a plugin; it's fine to link other plugins.
2407 hir
::ItemExternCrate(..) => (),
2411 let md
= match cx
.sess().cstore
.find_extern_mod_stmt_cnum(it
.id
) {
2412 Some(cnum
) => cx
.sess().cstore
.get_crate_data(cnum
),
2414 // Probably means we aren't linking the crate for some reason.
2416 // Not sure if / when this could happen.
2421 if decoder
::get_plugin_registrar_fn(md
.data()).is_some() {
2422 cx
.span_lint(PLUGIN_AS_LIBRARY
, it
.span
,
2423 "compiler plugin used as an ordinary library");
2429 PRIVATE_NO_MANGLE_FNS
,
2431 "functions marked #[no_mangle] should be exported"
2435 PRIVATE_NO_MANGLE_STATICS
,
2437 "statics marked #[no_mangle] should be exported"
2441 NO_MANGLE_CONST_ITEMS
,
2443 "const items will not have their symbols exported"
2446 #[derive(Copy, Clone)]
2447 pub struct InvalidNoMangleItems
;
2449 impl LintPass
for InvalidNoMangleItems
{
2450 fn get_lints(&self) -> LintArray
{
2451 lint_array
!(PRIVATE_NO_MANGLE_FNS
,
2452 PRIVATE_NO_MANGLE_STATICS
,
2453 NO_MANGLE_CONST_ITEMS
)
2456 fn check_item(&mut self, cx
: &Context
, it
: &hir
::Item
) {
2458 hir
::ItemFn(..) => {
2459 if attr
::contains_name(&it
.attrs
, "no_mangle") &&
2460 !cx
.exported_items
.contains(&it
.id
) {
2461 let msg
= format
!("function {} is marked #[no_mangle], but not exported",
2463 cx
.span_lint(PRIVATE_NO_MANGLE_FNS
, it
.span
, &msg
);
2466 hir
::ItemStatic(..) => {
2467 if attr
::contains_name(&it
.attrs
, "no_mangle") &&
2468 !cx
.exported_items
.contains(&it
.id
) {
2469 let msg
= format
!("static {} is marked #[no_mangle], but not exported",
2471 cx
.span_lint(PRIVATE_NO_MANGLE_STATICS
, it
.span
, &msg
);
2474 hir
::ItemConst(..) => {
2475 if attr
::contains_name(&it
.attrs
, "no_mangle") {
2476 // Const items do not refer to a particular location in memory, and therefore
2477 // don't have anything to attach a symbol to
2478 let msg
= "const items should never be #[no_mangle], consider instead using \
2480 cx
.span_lint(NO_MANGLE_CONST_ITEMS
, it
.span
, msg
);
2488 #[derive(Clone, Copy)]
2489 pub struct MutableTransmutes
;
2494 "mutating transmuted &mut T from &T may cause undefined behavior"
2497 impl LintPass
for MutableTransmutes
{
2498 fn get_lints(&self) -> LintArray
{
2499 lint_array
!(MUTABLE_TRANSMUTES
)
2502 fn check_expr(&mut self, cx
: &Context
, expr
: &hir
::Expr
) {
2503 use syntax
::abi
::RustIntrinsic
;
2505 let msg
= "mutating transmuted &mut T from &T may cause undefined behavior,\
2506 consider instead using an UnsafeCell";
2507 match get_transmute_from_to(cx
, expr
) {
2508 Some((&ty
::TyRef(_
, from_mt
), &ty
::TyRef(_
, to_mt
))) => {
2509 if to_mt
.mutbl
== hir
::Mutability
::MutMutable
2510 && from_mt
.mutbl
== hir
::Mutability
::MutImmutable
{
2511 cx
.span_lint(MUTABLE_TRANSMUTES
, expr
.span
, msg
);
2517 fn get_transmute_from_to
<'a
, 'tcx
>(cx
: &Context
<'a
, 'tcx
>, expr
: &hir
::Expr
)
2518 -> Option
<(&'tcx ty
::TypeVariants
<'tcx
>, &'tcx ty
::TypeVariants
<'tcx
>)> {
2520 hir
::ExprPath(..) => (),
2523 if let def
::DefFn(did
, _
) = cx
.tcx
.resolve_expr(expr
) {
2524 if !def_id_is_transmute(cx
, did
) {
2527 let typ
= cx
.tcx
.node_id_to_type(expr
.id
);
2529 ty
::TyBareFn(_
, ref bare_fn
) if bare_fn
.abi
== RustIntrinsic
=> {
2530 if let ty
::FnConverging(to
) = bare_fn
.sig
.0.output
{
2531 let from
= bare_fn
.sig
.0.inputs
[0];
2532 return Some((&from
.sty
, &to
.sty
));
2541 fn def_id_is_transmute(cx
: &Context
, def_id
: DefId
) -> bool
{
2542 match cx
.tcx
.lookup_item_type(def_id
).ty
.sty
{
2543 ty
::TyBareFn(_
, ref bfty
) if bfty
.abi
== RustIntrinsic
=> (),
2546 cx
.tcx
.with_path(def_id
, |path
| match path
.last() {
2547 Some(ref last
) => last
.name().as_str() == "transmute",
2554 /// Forbids using the `#[feature(...)]` attribute
2555 #[derive(Copy, Clone)]
2556 pub struct UnstableFeatures
;
2561 "enabling unstable features (deprecated. do not use)"
2564 impl LintPass
for UnstableFeatures
{
2565 fn get_lints(&self) -> LintArray
{
2566 lint_array
!(UNSTABLE_FEATURES
)
2568 fn check_attribute(&mut self, ctx
: &Context
, attr
: &hir
::Attribute
) {
2569 if attr
::contains_name(&[attr
.node
.value
.clone()], "feature") {
2570 if let Some(items
) = attr
.node
.value
.meta_item_list() {
2572 ctx
.span_lint(UNSTABLE_FEATURES
, item
.span
, "unstable feature");
2579 /// Lints for attempts to impl Drop on types that have `#[repr(C)]`
2580 /// attribute (see issue #24585).
2581 #[derive(Copy, Clone)]
2582 pub struct DropWithReprExtern
;
2585 DROP_WITH_REPR_EXTERN
,
2587 "use of #[repr(C)] on a type that implements Drop"
2590 impl LintPass
for DropWithReprExtern
{
2591 fn get_lints(&self) -> LintArray
{
2592 lint_array
!(DROP_WITH_REPR_EXTERN
)
2595 fn check_crate(&mut self, ctx
: &Context
, _
: &hir
::Crate
) {
2596 let drop_trait
= match ctx
.tcx
.lang_items
.drop_trait() {
2597 Some(id
) => ctx
.tcx
.lookup_trait_def(id
), None
=> { return }
2599 drop_trait
.for_each_impl(ctx
.tcx
, |drop_impl_did
| {
2600 if !drop_impl_did
.is_local() {
2603 let dtor_self_type
= ctx
.tcx
.lookup_item_type(drop_impl_did
).ty
;
2605 match dtor_self_type
.sty
{
2606 ty
::TyEnum(self_type_def
, _
) |
2607 ty
::TyStruct(self_type_def
, _
) => {
2608 let self_type_did
= self_type_def
.did
;
2609 let hints
= ctx
.tcx
.lookup_repr_hints(self_type_did
);
2610 if hints
.iter().any(|attr
| *attr
== attr
::ReprExtern
) &&
2611 self_type_def
.dtor_kind().has_drop_flag() {
2612 let drop_impl_span
= ctx
.tcx
.map
.def_id_span(drop_impl_did
,
2614 let self_defn_span
= ctx
.tcx
.map
.def_id_span(self_type_did
,
2616 ctx
.span_lint(DROP_WITH_REPR_EXTERN
,
2618 "implementing Drop adds hidden state to types, \
2619 possibly conflicting with `#[repr(C)]`");
2620 // FIXME #19668: could be span_lint_note instead of manual guard.
2621 if ctx
.current_level(DROP_WITH_REPR_EXTERN
) != Level
::Allow
{
2622 ctx
.sess().span_note(self_defn_span
,
2623 "the `#[repr(C)]` attribute is attached here");