diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 6a54cb4766b2..4dcbe4831be2 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -526,7 +526,7 @@ impl MetaItemKind { fn list_from_tokens(tokens: TokenStream) -> Option { let mut tokens = tokens.into_trees().peekable(); let mut result = Vec::new(); - while let Some(..) = tokens.peek() { + while tokens.peek().is_some() { let item = NestedMetaItem::from_tokens(&mut tokens)?; result.push(item); match tokens.next() { diff --git a/compiler/rustc_data_structures/src/steal.rs b/compiler/rustc_data_structures/src/steal.rs index e532a84cea3f..7f9e4160fcdd 100644 --- a/compiler/rustc_data_structures/src/steal.rs +++ b/compiler/rustc_data_structures/src/steal.rs @@ -30,6 +30,7 @@ impl Steal { Steal { value: RwLock::new(Some(value)) } } + #[track_caller] pub fn borrow(&self) -> MappedReadGuard<'_, T> { ReadGuard::map(self.value.borrow(), |opt| match *opt { None => panic!("attempted to read from stolen value"), @@ -37,10 +38,11 @@ impl Steal { }) } + #[track_caller] pub fn steal(&self) -> T { let value_ref = &mut *self.value.try_write().expect("stealing value which is locked"); let value = value_ref.take(); - value.expect("attempt to read from stolen value") + value.expect("attempt to steal from stolen value") } } diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index b47d9c50e1d0..d737d1ebf56c 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -22,15 +22,16 @@ pub enum CastTy<'tcx> { /// Various types that are represented as ints and handled mostly /// in the same way, merged for easier matching. Int(IntTy), - /// Floating-Point types + /// Floating-point types. Float, - /// Function Pointers + /// Function pointers. FnPtr, - /// Raw pointers + /// Raw pointers. Ptr(ty::TypeAndMut<'tcx>), } -/// Cast Kind. See RFC 401 (or librustc_typeck/check/cast.rs) +/// Cast Kind. See [RFC 401](https://rust-lang.github.io/rfcs/0401-coercions.html) +/// (or librustc_typeck/check/cast.rs). #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub enum CastKind { CoercionCast, @@ -48,7 +49,7 @@ pub enum CastKind { impl<'tcx> CastTy<'tcx> { /// Returns `Some` for integral/pointer casts. - /// casts like unsizing casts will return `None` + /// Casts like unsizing casts will return `None`. pub fn from_ty(t: Ty<'tcx>) -> Option> { match *t.kind() { ty::Bool => Some(CastTy::Int(IntTy::Bool)), diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index a7b0ff45b976..0dad5df48551 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -18,7 +18,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::Span; -use std::convert::{TryFrom, TryInto}; use std::hash::Hash; use std::intrinsics; use std::marker::DiscriminantKind; @@ -81,7 +80,8 @@ where E: TyEncoder<'tcx>, M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, T: EncodableWithShorthand<'tcx, E>, - ::Discriminant: Ord + TryFrom, + // The discriminant and shorthand must have the same size. + T::Variant: DiscriminantKind, { let existing_shorthand = cache(encoder).get(value).copied(); if let Some(shorthand) = existing_shorthand { @@ -97,7 +97,7 @@ where // The shorthand encoding uses the same usize as the // discriminant, with an offset so they can't conflict. let discriminant = intrinsics::discriminant_value(variant); - assert!(discriminant < SHORTHAND_OFFSET.try_into().ok().unwrap()); + assert!(SHORTHAND_OFFSET > discriminant as usize); let shorthand = start + SHORTHAND_OFFSET; diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs index aa5835686a29..b740dfaca328 100644 --- a/compiler/rustc_mir/src/shim.rs +++ b/compiler/rustc_mir/src/shim.rs @@ -165,7 +165,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) let mut body = new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); - if let Some(..) = ty { + if ty.is_some() { // The first argument (index 0), but add 1 for the return value. let dropee_ptr = Place::from(Local::new(1 + 0)); if tcx.sess.opts.debugging_opts.mir_emit_retag { diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index a311e262dd4d..354d213689ec 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -139,7 +139,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { Default::default(), body.arg_count, Default::default(), - tcx.def_span(def_id), + body.span, body.generator_kind, ); diff --git a/compiler/rustc_mir/src/transform/simplify.rs b/compiler/rustc_mir/src/transform/simplify.rs index b7c9a3a8688e..289231e52cb4 100644 --- a/compiler/rustc_mir/src/transform/simplify.rs +++ b/compiler/rustc_mir/src/transform/simplify.rs @@ -61,7 +61,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { } fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body); + debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body.source); simplify_cfg(body); } } diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index f7b16bd991bf..f150f7a41ae8 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -736,7 +736,7 @@ fn find_skips_from_snippet( fn find_skips(snippet: &str, is_raw: bool) -> Vec { let mut eat_ws = false; - let mut s = snippet.chars().enumerate().peekable(); + let mut s = snippet.char_indices().peekable(); let mut skips = vec![]; while let Some((pos, c)) = s.next() { match (c, s.peek()) { diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 1be06a446875..64cc113dffe7 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -1398,12 +1398,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { fn lifetime_deletion_span(&self, name: Ident, generics: &hir::Generics<'_>) -> Option { generics.params.iter().enumerate().find_map(|(i, param)| { if param.name.ident() == name { - let mut in_band = false; - if let hir::GenericParamKind::Lifetime { kind } = param.kind { - if let hir::LifetimeParamKind::InBand = kind { - in_band = true; - } - } + let in_band = matches!( + param.kind, + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::InBand } + ); if in_band { Some(param.span) } else if generics.params.len() == 1 { @@ -1433,12 +1431,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { lifetime: &hir::Lifetime, ) { let name = lifetime.name.ident(); - let mut remove_decl = None; - if let Some(parent_def_id) = self.tcx.parent(def_id) { - if let Some(generics) = self.tcx.hir().get_generics(parent_def_id) { - remove_decl = self.lifetime_deletion_span(name, generics); - } - } + let remove_decl = self + .tcx + .parent(def_id) + .and_then(|parent_def_id| self.tcx.hir().get_generics(parent_def_id)) + .and_then(|generics| self.lifetime_deletion_span(name, generics)); let mut remove_use = None; let mut elide_use = None; diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 38c6bfb0ef36..b983f49eb178 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -541,7 +541,7 @@ impl Ordering { /// assert_eq!(result, Ordering::Equal); /// /// let x: (i64, i64, i64) = (1, 2, 7); - /// let y: (i64, i64, i64) = (1, 5, 3); + /// let y: (i64, i64, i64) = (1, 5, 3); /// let result = x.0.cmp(&y.0).then_with(|| x.1.cmp(&y.1)).then_with(|| x.2.cmp(&y.2)); /// /// assert_eq!(result, Ordering::Less); diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.rs b/src/test/ui/consts/const-eval/ub-wide-ptr.rs index bcd05b4cd7ec..2975118cdb7f 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.rs +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.rs @@ -8,6 +8,12 @@ use std::mem; // normalize-stderr-test "alloc\d+" -> "allocN" // normalize-stderr-test "size \d+" -> "size N" +/// A newtype wrapper to prevent MIR generation from inserting reborrows that would affect the error +/// message. Use this whenever the message is "any use of this value will cause an error" instead of +/// "it is undefined behavior to use this value". +#[repr(transparent)] +struct W(T); + #[repr(C)] union MaybeUninit { uninit: (), @@ -95,26 +101,22 @@ const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { // # trait object // bad trait object -#[warn(const_err)] -const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { mem::transmute((&92u8, &3u8)) }; -//~^ WARN any use of this value will cause an error [const_err] +const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; +//~^ ERROR it is undefined behavior to use this value // bad trait object -#[warn(const_err)] -const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; -//~^ WARN any use of this value will cause an error [const_err] +const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; +//~^ ERROR it is undefined behavior to use this value // bad trait object -#[warn(const_err)] -const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) }; -//~^ WARN any use of this value will cause an error [const_err] +const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; +//~^ ERROR it is undefined behavior to use this value const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; //~^ ERROR it is undefined behavior to use this value const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; //~^ ERROR it is undefined behavior to use this value const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; //~^ ERROR it is undefined behavior to use this value -#[warn(const_err)] -const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) }; -//~^ WARN any use of this value will cause an error [const_err] +const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; +//~^ ERROR it is undefined behavior to use this value // bad data *inside* the trait object const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr index ec5d465c8825..be9ec16a06fe 100644 --- a/src/test/ui/consts/const-eval/ub-wide-ptr.stderr +++ b/src/test/ui/consts/const-eval/ub-wide-ptr.stderr @@ -1,5 +1,5 @@ error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:31:1 + --> $DIR/ub-wide-ptr.rs:37:1 | LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) @@ -7,7 +7,7 @@ LL | const STR_TOO_LONG: &str = unsafe { mem::transmute((&42u8, 999usize)) }; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:33:1 + --> $DIR/ub-wide-ptr.rs:39:1 | LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, usize::MAX)) },); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object at .0 @@ -15,7 +15,7 @@ LL | const NESTED_STR_MUCH_TOO_LONG: (&str,) = (unsafe { mem::transmute((&42, us = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:36:1 + --> $DIR/ub-wide-ptr.rs:42:1 | LL | const STR_LENGTH_PTR: &str = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer @@ -23,7 +23,7 @@ LL | const STR_LENGTH_PTR: &str = unsafe { mem::transmute((&42u8, &3)) }; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:39:1 + --> $DIR/ub-wide-ptr.rs:45:1 | LL | const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer @@ -31,7 +31,7 @@ LL | const MY_STR_LENGTH_PTR: &MyStr = unsafe { mem::transmute((&42u8, &3)) }; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:41:1 + --> $DIR/ub-wide-ptr.rs:47:1 | LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize::MAX)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid reference metadata: slice is bigger than largest supported object @@ -39,7 +39,7 @@ LL | const MY_STR_MUCH_TOO_LONG: &MyStr = unsafe { mem::transmute((&42u8, usize: = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:45:1 + --> $DIR/ub-wide-ptr.rs:51:1 | LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at . @@ -47,7 +47,7 @@ LL | const STR_NO_INIT: &str = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit: = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:48:1 + --> $DIR/ub-wide-ptr.rs:54:1 | LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUninit:: { uninit: () }]) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized data in `str` at ..0 @@ -55,7 +55,7 @@ LL | const MYSTR_NO_INIT: &MyStr = unsafe { mem::transmute::<&[_], _>(&[MaybeUni = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:55:1 + --> $DIR/ub-wide-ptr.rs:61:1 | LL | / const SLICE_LENGTH_UNINIT: &[u8] = unsafe { LL | | @@ -67,7 +67,7 @@ LL | | }; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:61:1 + --> $DIR/ub-wide-ptr.rs:67:1 | LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling reference (going beyond the bounds of its allocation) @@ -75,7 +75,7 @@ LL | const SLICE_TOO_LONG: &[u8] = unsafe { mem::transmute((&42u8, 999usize)) }; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:64:1 + --> $DIR/ub-wide-ptr.rs:70:1 | LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer @@ -83,7 +83,7 @@ LL | const SLICE_LENGTH_PTR: &[u8] = unsafe { mem::transmute((&42u8, &3)) }; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:67:1 + --> $DIR/ub-wide-ptr.rs:73:1 | LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a dangling box (going beyond the bounds of its allocation) @@ -91,7 +91,7 @@ LL | const SLICE_TOO_LONG_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, 999us = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:70:1 + --> $DIR/ub-wide-ptr.rs:76:1 | LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in wide pointer @@ -99,7 +99,7 @@ LL | const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3) = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:74:1 + --> $DIR/ub-wide-ptr.rs:80:1 | LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .[0], but expected a boolean @@ -107,7 +107,7 @@ LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }]; = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:80:1 + --> $DIR/ub-wide-ptr.rs:86:1 | LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at ..0, but expected a boolean @@ -115,7 +115,7 @@ LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3 = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:83:1 + --> $DIR/ub-wide-ptr.rs:89:1 | LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at ..1[0], but expected a boolean @@ -123,7 +123,7 @@ LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::tran = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:90:1 + --> $DIR/ub-wide-ptr.rs:96:1 | LL | / const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { LL | | @@ -134,50 +134,32 @@ LL | | }; | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. -warning: any use of this value will cause an error - --> $DIR/ub-wide-ptr.rs:99:55 +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:104:1 | -LL | const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { mem::transmute((&92u8, &3u8)) }; - | ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N +LL | const TRAIT_OBJ_SHORT_VTABLE_1: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u8))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable at .0 | -note: the lint level is defined here - --> $DIR/ub-wide-ptr.rs:98:8 - | -LL | #[warn(const_err)] - | ^^^^^^^^^ - -warning: any use of this value will cause an error - --> $DIR/ub-wide-ptr.rs:103:55 - | -LL | const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; - | ------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N - | -note: the lint level is defined here - --> $DIR/ub-wide-ptr.rs:102:8 - | -LL | #[warn(const_err)] - | ^^^^^^^^^ - -warning: any use of this value will cause an error - --> $DIR/ub-wide-ptr.rs:107:51 - | -LL | const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, 4usize)) }; - | --------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | unable to turn bytes into a pointer - | -note: the lint level is defined here - --> $DIR/ub-wide-ptr.rs:106:8 - | -LL | #[warn(const_err)] - | ^^^^^^^^^ + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:109:1 + --> $DIR/ub-wide-ptr.rs:107:1 + | +LL | const TRAIT_OBJ_SHORT_VTABLE_2: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &3u64))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:110:1 + | +LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, 4usize))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer at .0 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:112:1 | LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered unaligned vtable pointer in wide pointer @@ -185,7 +167,7 @@ LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92 = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:111:1 + --> $DIR/ub-wide-ptr.rs:114:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) @@ -193,29 +175,23 @@ LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92 = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:113:1 + --> $DIR/ub-wide-ptr.rs:116:1 | LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. -warning: any use of this value will cause an error - --> $DIR/ub-wide-ptr.rs:116:63 +error[E0080]: it is undefined behavior to use this value + --> $DIR/ub-wide-ptr.rs:118:1 | -LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: &dyn Trait = unsafe { mem::transmute((&92u8, &[&42u8; 8])) }; - | --------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- - | | - | "pointer-to-integer cast" needs an rfc before being allowed inside constants +LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop function pointer in vtable (not pointing to a function) at .0 | -note: the lint level is defined here - --> $DIR/ub-wide-ptr.rs:115:8 - | -LL | #[warn(const_err)] - | ^^^^^^^^^ + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:120:1 + --> $DIR/ub-wide-ptr.rs:122:1 | LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 0x03 at .., but expected a boolean @@ -223,7 +199,7 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_, = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:124:1 + --> $DIR/ub-wide-ptr.rs:126:1 | LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling vtable pointer in wide pointer @@ -231,7 +207,7 @@ LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-wide-ptr.rs:126:1 + --> $DIR/ub-wide-ptr.rs:128:1 | LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered too small vtable @@ -239,17 +215,17 @@ LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transm = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:132:5 + --> $DIR/ub-wide-ptr.rs:134:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inbounds test failed: 0x0 is not a valid pointer error[E0080]: could not evaluate static initializer - --> $DIR/ub-wide-ptr.rs:136:5 + --> $DIR/ub-wide-ptr.rs:138:5 | LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: pointer must be in-bounds at offset N, but is outside bounds of allocN which has size N -error: aborting due to 24 previous errors; 4 warnings emitted +error: aborting due to 28 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/macros/issue-81006.rs b/src/test/ui/macros/issue-81006.rs new file mode 100644 index 000000000000..602eb5974287 --- /dev/null +++ b/src/test/ui/macros/issue-81006.rs @@ -0,0 +1,10 @@ +// check-fail + +// First format below would cause a panic, second would generate error with incorrect span + +fn main() { + let _ = format!("→{}→\n"); + //~^ ERROR 1 positional argument in format string, but no arguments were given + let _ = format!("→{} \n"); + //~^ ERROR 1 positional argument in format string, but no arguments were given +} diff --git a/src/test/ui/macros/issue-81006.stderr b/src/test/ui/macros/issue-81006.stderr new file mode 100644 index 000000000000..14a8cbe0155f --- /dev/null +++ b/src/test/ui/macros/issue-81006.stderr @@ -0,0 +1,14 @@ +error: 1 positional argument in format string, but no arguments were given + --> $DIR/issue-81006.rs:6:23 + | +LL | let _ = format!("→{}→\n"); + | ^^ + +error: 1 positional argument in format string, but no arguments were given + --> $DIR/issue-81006.rs:8:23 + | +LL | let _ = format!("→{} \n"); + | ^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index de8da99cdee1..64864c2e2780 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,138 @@ document. ## Unreleased / In Rust Nightly -[b20d4c1...master](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...master) +[4911ab1...master](https://github.com/rust-lang/rust-clippy/compare/4911ab1...master) + +## Rust 1.50 + +Current beta, release 2021-02-11 + +[b20d4c1...4911ab1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4911ab1) + +### New Lints + +* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086) +* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394) +* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070) +* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305) +* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402) +* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330) +* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218) +* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367) +* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134) + +### Moves and Deprecations + +* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated + as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333) +* Deprecate [`panic_params`] lint. This is now available in rustc as `panic_fmt` + [#6351](https://github.com/rust-lang/rust-clippy/pull/6351) +* Move [`map_err_ignore`] to `restriction` + [#6416](https://github.com/rust-lang/rust-clippy/pull/6416) +* Move [`await_holding_refcell_ref`] to `pedantic` + [#6354](https://github.com/rust-lang/rust-clippy/pull/6354) +* Move [`await_holding_lock`] to `pedantic` + [#6354](https://github.com/rust-lang/rust-clippy/pull/6354) + +### Enhancements + +* Add the `unreadable-literal-lint-fractions` configuration to disable + the `unreadable_literal` lint for fractions + [#6421](https://github.com/rust-lang/rust-clippy/pull/6421) +* [`clone_on_copy`]: Now shows the type in the lint message + [#6443](https://github.com/rust-lang/rust-clippy/pull/6443) +* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll` + [#6339](https://github.com/rust-lang/rust-clippy/pull/6339) +* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr` + [#6377](https://github.com/rust-lang/rust-clippy/pull/6377) +* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()` + [#6119](https://github.com/rust-lang/rust-clippy/pull/6119) +* [`clone_double_ref`]: Now prints the reference type in the lint message + [#6442](https://github.com/rust-lang/rust-clippy/pull/6442) +* [`modulo_one`]: Now also lints on -1. + [#6360](https://github.com/rust-lang/rust-clippy/pull/6360) +* [`empty_loop`]: Now lints no_std crates, too + [#6205](https://github.com/rust-lang/rust-clippy/pull/6205) +* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap` + [#6267](https://github.com/rust-lang/rust-clippy/pull/6267) +* [`wrong_self_convention`]: Now also lints in trait definitions + [#6316](https://github.com/rust-lang/rust-clippy/pull/6316) +* [`needless_borrow`]: Print the type in the lint message + [#6449](https://github.com/rust-lang/rust-clippy/pull/6449) + +[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version + +### False Positive Fixes + +* [`manual_range_contains`]: No longer lints in `const fn` + [#6382](https://github.com/rust-lang/rust-clippy/pull/6382) +* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used + [#6370](https://github.com/rust-lang/rust-clippy/pull/6370) +* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros + [#6435](https://github.com/rust-lang/rust-clippy/pull/6435) +* [`match_like_matches_macro`]: No longer lints on arms with attributes + [#6290](https://github.com/rust-lang/rust-clippy/pull/6290) +* [`map_clone`]: No longer lints with deref and clone + [#6269](https://github.com/rust-lang/rust-clippy/pull/6269) +* [`map_clone`]: No longer lints in the case of &mut + [#6301](https://github.com/rust-lang/rust-clippy/pull/6301) +* [`needless_update`]: Now ignores `non_exhaustive` structs + [#6464](https://github.com/rust-lang/rust-clippy/pull/6464) +* [`needless_collect`]: No longer lints when a collect is needed multiple times + [#6313](https://github.com/rust-lang/rust-clippy/pull/6313) +* [`unnecessary_cast`] No longer lints cfg-dependent types + [#6369](https://github.com/rust-lang/rust-clippy/pull/6369) +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: + Both now ignore enums with frozen variants + [#6110](https://github.com/rust-lang/rust-clippy/pull/6110) + + +### Suggestion Fixes/Improvements + +* [`vec_box`]: Provide correct type scope suggestion + [#6271](https://github.com/rust-lang/rust-clippy/pull/6271) +* [`manual_range_contains`]: Give correct suggestion when using floats + [#6320](https://github.com/rust-lang/rust-clippy/pull/6320) +* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable + [#6272](https://github.com/rust-lang/rust-clippy/pull/6272) +* [`manual_async_fn`]: Improve suggestion formatting + [#6294](https://github.com/rust-lang/rust-clippy/pull/6294) +* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion + [#6362](https://github.com/rust-lang/rust-clippy/pull/6362) + +### ICE Fixes + +* Fix a crash in [`from_iter_instead_of_collect`] + [#6304](https://github.com/rust-lang/rust-clippy/pull/6304) +* Fix a silent crash when parsing doc comments in [`needless_doctest_main`] + [#6458](https://github.com/rust-lang/rust-clippy/pull/6458) + +### Documentation Improvements + +* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)): + * Searching for lints with dashes and spaces is possible now. For example + `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names + * Improved fuzzy search in lint descriptions +* Various README improvements + [#6287](https://github.com/rust-lang/rust-clippy/pull/6287) +* Add known problems to [`comparison_chain`] documentation + [#6390](https://github.com/rust-lang/rust-clippy/pull/6390) +* Fix example used in [`cargo_common_metadata`] + [#6293](https://github.com/rust-lang/rust-clippy/pull/6293) +* Improve [`map_clone`] documentation + [#6340](https://github.com/rust-lang/rust-clippy/pull/6340) + +### Others + +* You can now tell Clippy about the MSRV your project supports. Please refer to + the specific README section to learn more about MSRV support [here][msrv_readme] + [#6201](https://github.com/rust-lang/rust-clippy/pull/6201) +* Add `--no-deps` option to avoid running on path dependencies in workspaces + [#6188](https://github.com/rust-lang/rust-clippy/pull/6188) ## Rust 1.49 -Current beta, release 2020-12-31 +Current stable, released 2020-12-31 [e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1) @@ -116,7 +243,7 @@ Current beta, release 2020-12-31 ## Rust 1.48 -Current stable, released 2020-11-19 +Released 2020-11-19 [09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88) @@ -1769,6 +1896,7 @@ Released 2018-09-13 [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain @@ -1973,6 +2101,7 @@ Released 2018-09-13 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value +[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update @@ -2012,6 +2141,7 @@ Released 2018-09-13 [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string [`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names @@ -2152,6 +2282,7 @@ Released 2018-09-13 [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push [`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads diff --git a/src/tools/clippy/clippy_dev/src/bless.rs b/src/tools/clippy/clippy_dev/src/bless.rs index 645098e4cfcd..b877806946cf 100644 --- a/src/tools/clippy/clippy_dev/src/bless.rs +++ b/src/tools/clippy/clippy_dev/src/bless.rs @@ -5,7 +5,7 @@ use std::env; use std::ffi::OsStr; use std::fs; use std::lazy::SyncLazy; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use walkdir::WalkDir; use crate::clippy_project_root; @@ -16,27 +16,41 @@ pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var None => env::current_dir().unwrap().join("target"), }); -pub fn bless() { - let test_dirs = [ +static CLIPPY_BUILD_TIME: SyncLazy> = SyncLazy::new(|| { + let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string()); + let mut path = PathBuf::from(&**CARGO_TARGET_DIR); + path.push(profile); + path.push("cargo-clippy"); + fs::metadata(path).ok()?.modified().ok() +}); + +pub fn bless(ignore_timestamp: bool) { + let test_suite_dirs = [ clippy_project_root().join("tests").join("ui"), + clippy_project_root().join("tests").join("ui-internal"), clippy_project_root().join("tests").join("ui-toml"), clippy_project_root().join("tests").join("ui-cargo"), ]; - for test_dir in &test_dirs { - WalkDir::new(test_dir) + for test_suite_dir in &test_suite_dirs { + WalkDir::new(test_suite_dir) .into_iter() .filter_map(Result::ok) .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) .for_each(|f| { - update_reference_file(f.path().with_extension("stdout")); - update_reference_file(f.path().with_extension("stderr")); - update_reference_file(f.path().with_extension("fixed")); + let test_name = f.path().strip_prefix(test_suite_dir).unwrap(); + for &ext in &["stdout", "stderr", "fixed"] { + update_reference_file( + f.path().with_extension(ext), + test_name.with_extension(ext), + ignore_timestamp, + ); + } }); } } -fn update_reference_file(reference_file_path: PathBuf) { - let test_output_path = build_dir().join(PathBuf::from(reference_file_path.file_name().unwrap())); +fn update_reference_file(reference_file_path: PathBuf, test_name: PathBuf, ignore_timestamp: bool) { + let test_output_path = build_dir().join(test_name); let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap(); // If compiletest did not write any changes during the test run, @@ -45,6 +59,11 @@ fn update_reference_file(reference_file_path: PathBuf) { return; } + // If the test output was not updated since the last clippy build, it may be outdated + if !ignore_timestamp && !updated_since_clippy_build(&test_output_path).unwrap_or(true) { + return; + } + let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file"); let reference_file = fs::read(&reference_file_path).unwrap_or_default(); @@ -64,6 +83,12 @@ fn update_reference_file(reference_file_path: PathBuf) { } } +fn updated_since_clippy_build(path: &Path) -> Option { + let clippy_build_time = (*CLIPPY_BUILD_TIME)?; + let modified = fs::metadata(path).ok()?.modified().ok()?; + Some(modified >= clippy_build_time) +} + fn build_dir() -> PathBuf { let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string()); let mut path = PathBuf::new(); diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 4fdae38e3ab7..2ea56c42fafd 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -7,8 +7,8 @@ fn main() { let matches = get_clap_config(); match matches.subcommand() { - ("bless", Some(_)) => { - bless::bless(); + ("bless", Some(matches)) => { + bless::bless(matches.is_present("ignore-timestamp")); }, ("fmt", Some(matches)) => { fmt::run(matches.is_present("check"), matches.is_present("verbose")); @@ -47,7 +47,15 @@ fn main() { fn get_clap_config<'a>() -> ArgMatches<'a> { App::new("Clippy developer tooling") - .subcommand(SubCommand::with_name("bless").about("bless the test output changes")) + .subcommand( + SubCommand::with_name("bless") + .about("bless the test output changes") + .arg( + Arg::with_name("ignore-timestamp") + .long("ignore-timestamp") + .help("Include files updated before clippy was built"), + ), + ) .subcommand( SubCommand::with_name("fmt") .about("Run rustfmt on all projects and tests") diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index 7607394b2fe9..652d1fa16b6d 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -370,7 +370,7 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { if let Some(meta_item) = lint.meta_item(); if meta_item.path.segments.len() > 1; if let tool_name = meta_item.path.segments[0].ident; - if tool_name.as_str() == "clippy"; + if tool_name.name == sym::clippy; let lint_name = meta_item.path.segments.last().unwrap().ident.name; then { return Some(lint_name.as_str()); diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 42bff564de03..93ccc76d0c9c 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -23,9 +23,7 @@ use rustc_errors::Applicability; declare_clippy_lint! { /// **What it does:** Checks for nested `if` statements which can be collapsed - /// by `&&`-combining their conditions and for `else { if ... }` expressions - /// that - /// can be collapsed to `else if ...`. + /// by `&&`-combining their conditions. /// /// **Why is this bad?** Each `if`-statement adds one level of nesting, which /// makes code look more complex than it really is. @@ -40,7 +38,31 @@ declare_clippy_lint! { /// } /// } /// - /// // or + /// ``` + /// + /// Should be written: + /// + /// ```rust.ignore + /// if x && y { + /// … + /// } + /// ``` + pub COLLAPSIBLE_IF, + style, + "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for collapsible `else { if ... }` expressions + /// that can be collapsed to `else if ...`. + /// + /// **Why is this bad?** Each `if`-statement adds one level of nesting, which + /// makes code look more complex than it really is. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore /// /// if x { /// … @@ -54,24 +76,18 @@ declare_clippy_lint! { /// Should be written: /// /// ```rust.ignore - /// if x && y { - /// … - /// } - /// - /// // or - /// /// if x { /// … /// } else if y { /// … /// } /// ``` - pub COLLAPSIBLE_IF, + pub COLLAPSIBLE_ELSE_IF, style, - "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)" + "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" } -declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]); +declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); impl EarlyLintPass for CollapsibleIf { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { @@ -112,7 +128,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - COLLAPSIBLE_IF, + COLLAPSIBLE_ELSE_IF, block.span, "this `else { if .. }` block can be collapsed", "collapse nested if block", diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index ae1143b2c50c..90d31dece131 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// repetitive /// /// **Known problems:** The match statement may be slower due to the compiler - /// not inlining the call to cmp. See issue #5354 + /// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354) /// /// **Example:** /// ```rust,ignore diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 6f48ffeb0e9c..944aaafb46de 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -112,7 +112,8 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { if let Some(&Expr { kind: ExprKind::If(_, _, Some(ref else_expr)), .. - }) = get_parent_expr(cx, expr) { + }) = get_parent_expr(cx, expr) + { if else_expr.hir_id == expr.hir_id { return; } diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs index a7aa2cb35c1c..48899b338993 100644 --- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs @@ -1,5 +1,5 @@ use crate::utils::{is_copy, match_path, paths, span_lint_and_note}; -use rustc_hir::{Item, ItemKind, Impl}; +use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index b0d7c7b3baab..f7224811e6e7 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -1,4 +1,6 @@ -use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet}; +use crate::utils::{ + any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet_with_macro_callsite, +}; use crate::utils::{span_lint_and_note, span_lint_and_sugg}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; @@ -6,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; @@ -118,6 +121,8 @@ impl LateLintPass<'_> for Default { // only take `let ...` statements if let StmtKind::Local(local) = stmt.kind; if let Some(expr) = local.init; + if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); + if !in_external_macro(cx.tcx.sess, expr.span); // only take bindings to identifiers if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; // only when assigning `... = Default::default()` @@ -187,7 +192,7 @@ impl LateLintPass<'_> for Default { .into_iter() .map(|(field, rhs)| { // extract and store the assigned value for help message - let value_snippet = snippet(cx, rhs.span, ".."); + let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); format!("{}: {}", field, value_snippet) }) .collect::>() diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index b55f59f021df..b1e363663bb2 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -7,7 +7,7 @@ use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ - BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Impl, TraitRef, UnsafeSource, Unsafety, + BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs index a249117d182f..853b3afdc3ae 100644 --- a/src/tools/clippy/clippy_lints/src/empty_enum.rs +++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs @@ -8,8 +8,12 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for `enum`s with no variants. /// + /// As of this writing, the `never_type` is still a + /// nightly-only experimental API. Therefore, this lint is only triggered + /// if the `never_type` is enabled. + /// /// **Why is this bad?** If you want to introduce a type which - /// can't be instantiated, you should use `!` (the never type), + /// can't be instantiated, you should use `!` (the primitive type "never"), /// or a wrapper around it, because `!` has more extensive /// compiler support (type inference, etc...) and wrappers /// around it are the conventional way to define an uninhabited type. @@ -40,6 +44,11 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); impl<'tcx> LateLintPass<'tcx> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + // Only suggest the `never_type` if the feature is enabled + if !cx.tcx.features().never_type { + return; + } + let did = cx.tcx.hir().local_def_id(item.hir_id); if let ItemKind::Enum(..) = item.kind { let ty = cx.tcx.type_of(did); diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 550876978129..40e93da8dffb 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -1,15 +1,16 @@ use rustc_hir::intravisit; -use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Impl, Node}; +use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::symbol::kw; use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; -use crate::utils::span_lint; +use crate::utils::{contains_ty, span_lint}; #[derive(Copy, Clone)] pub struct BoxedLocal { @@ -51,6 +52,7 @@ fn is_non_trait_box(ty: Ty<'_>) -> bool { struct EscapeDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, set: HirIdSet, + trait_self_ty: Option>, too_large_for_stack: u64, } @@ -72,19 +74,34 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { } } - // If the method is an impl for a trait, don't warn. let parent_id = cx.tcx.hir().get_parent_item(hir_id); let parent_node = cx.tcx.hir().find(parent_id); + let mut trait_self_ty = None; if let Some(Node::Item(item)) = parent_node { + // If the method is an impl for a trait, don't warn. if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = item.kind { return; } + + // find `self` ty for this trait if relevant + if let ItemKind::Trait(_, _, _, _, items) = item.kind { + for trait_item in items { + if trait_item.id.hir_id == hir_id { + // be sure we have `self` parameter in this function + if let AssocItemKind::Fn { has_self: true } = trait_item.kind { + trait_self_ty = + Some(TraitRef::identity(cx.tcx, trait_item.id.hir_id.owner.to_def_id()).self_ty()); + } + } + } + } } let mut v = EscapeDelegate { cx, set: HirIdSet::default(), + trait_self_ty, too_large_for_stack: self.too_large_for_stack, }; @@ -153,10 +170,17 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { return; } + // skip if there is a `self` parameter binding to a type + // that contains `Self` (i.e.: `self: Box`), see #4804 + if let Some(trait_self_ty) = self.trait_self_ty { + if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) { + return; + } + } + if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { self.set.insert(cmt.hir_id); } - return; } } } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 53df3abbf543..1a722d39f730 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// **Known problems:** If creating the closure inside the closure has a side- /// effect then moving the closure creation out will change when that side- /// effect runs. - /// See rust-lang/rust-clippy#1439 for more details. + /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details. /// /// **Example:** /// ```rust,ignore @@ -45,8 +45,9 @@ declare_clippy_lint! { /// /// **Why is this bad?** It's unnecessary to create the closure. /// - /// **Known problems:** rust-lang/rust-clippy#3071, rust-lang/rust-clippy#4002, - /// rust-lang/rust-clippy#3942 + /// **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071), + /// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942), + /// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002) /// /// /// **Example:** diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 1e7e5f53cc2a..b010abda24d1 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -70,7 +70,7 @@ impl LateLintPass<'_> for FromOverInto { span_lint_and_help( cx, FROM_OVER_INTO, - item.span, + cx.tcx.sess.source_map().guess_head_span(item.span), "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", None, "consider to implement `From` instead", diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index 2e55094d90c6..58511c6d57c6 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -145,7 +145,7 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if_chain! { if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind; - if path.ident.to_string() == "lock"; + if path.ident.as_str() == "lock"; let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, ty, sym!(mutex_type)); then { diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index e287aecb044f..ea26c84cde16 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -2,7 +2,7 @@ use crate::utils::{in_macro, span_lint_and_then}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{def_id, Crate, Item, ItemKind, Impl}; +use rustc_hir::{def_id, Crate, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 5474b30bdec8..e95caf6a35f9 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -3,7 +3,7 @@ use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, Impl, TraitItemRef}; +use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, Impl, ImplItemRef, Item, ItemKind, TraitItemRef}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index aaa17561f06d..781282cdfbd0 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -271,6 +271,7 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_pass_by_value; +mod needless_question_mark; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -341,6 +342,7 @@ mod unwrap_in_result; mod use_self; mod useless_conversion; mod vec; +mod vec_init_then_push; mod vec_resize_to_zero; mod verbose_file_reads; mod wildcard_dependencies; @@ -528,6 +530,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal-lints")] &utils::internal_lints::PRODUCE_ICE, + #[cfg(feature = "internal-lints")] + &utils::internal_lints::UNNECESSARY_SYMBOL_STR, &approx_const::APPROX_CONSTANT, &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, @@ -559,6 +563,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cargo_common_metadata::CARGO_COMMON_METADATA, &checked_conversions::CHECKED_CONVERSIONS, &cognitive_complexity::COGNITIVE_COMPLEXITY, + &collapsible_if::COLLAPSIBLE_ELSE_IF, &collapsible_if::COLLAPSIBLE_IF, &collapsible_match::COLLAPSIBLE_MATCH, &comparison_chain::COMPARISON_CHAIN, @@ -802,6 +807,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, + &needless_question_mark::NEEDLESS_QUESTION_MARK, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -911,6 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::LET_UNIT_VALUE, &types::LINKEDLIST, &types::OPTION_OPTION, + &types::PTR_AS_PTR, &types::RC_BUFFER, &types::REDUNDANT_ALLOCATION, &types::TYPE_COMPLEXITY, @@ -938,6 +945,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, &vec::USELESS_VEC, + &vec_init_then_push::VEC_INIT_THEN_PUSH, &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, &verbose_file_reads::VERBOSE_FILE_READS, &wildcard_dependencies::WILDCARD_DEPENDENCIES, @@ -1022,6 +1030,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); + store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); @@ -1218,6 +1227,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); + store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); + store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1344,6 +1355,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::LET_UNIT_VALUE), LintId::of(&types::LINKEDLIST), LintId::of(&types::OPTION_OPTION), + LintId::of(&types::PTR_AS_PTR), LintId::of(&unicode::NON_ASCII_LITERAL), LintId::of(&unicode::UNICODE_NOT_NFC), LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), @@ -1365,6 +1377,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(&utils::internal_lints::PRODUCE_ICE), + LintId::of(&utils::internal_lints::UNNECESSARY_SYMBOL_STR), ]); store.register_group(true, "clippy::all", Some("clippy"), vec![ @@ -1386,6 +1399,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), + LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), @@ -1547,6 +1561,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&neg_multiply::NEG_MULTIPLY), @@ -1638,6 +1653,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), + LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), @@ -1654,6 +1670,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), @@ -1804,6 +1821,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(&needless_update::NEEDLESS_UPDATE), LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(&no_effect::NO_EFFECT), @@ -1936,6 +1954,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::BOX_VEC), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), + LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), ]); store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs index 281964ee5e8f..1c5ab2874b04 100644 --- a/src/tools/clippy/clippy_lints/src/loops.rs +++ b/src/tools/clippy/clippy_lints/src/loops.rs @@ -218,7 +218,7 @@ declare_clippy_lint! { /// **Why is this bad?** The `while let` loop is usually shorter and more /// readable. /// - /// **Known problems:** Sometimes the wrong binding is displayed (#383). + /// **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)). /// /// **Example:** /// ```rust,no_run diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 29439e52c48e..89f5b2ff3113 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -9,7 +9,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// **What it does:** It checks for manual implementations of `async` functions. @@ -137,7 +137,7 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t if let Some(args) = segment.args; if args.bindings.len() == 1; let binding = &args.bindings[0]; - if binding.ident.as_str() == "Output"; + if binding.ident.name == sym::Output; if let TypeBindingKind::Equality{ty: output} = binding.kind; then { return Some(output) diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs index 220240acb7aa..1818836d5d5e 100644 --- a/src/tools/clippy/clippy_lints/src/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/map_clone.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { if_chain! { if let hir::ExprKind::MethodCall(ref method, _, ref args, _) = e.kind; if args.len() == 2; - if method.ident.as_str() == "map"; + if method.ident.name == sym::map; let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR); if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind; diff --git a/src/tools/clippy/clippy_lints/src/map_identity.rs b/src/tools/clippy/clippy_lints/src/map_identity.rs index 6b782385a38d..9f9c108a85a0 100644 --- a/src/tools/clippy/clippy_lints/src/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/map_identity.rs @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for MapIdentity { fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> { if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; - if args.len() == 2 && method.ident.as_str() == "map"; + if args.len() == 2 && method.ident.name == sym::map; let caller_ty = cx.typeck_results().expr_ty(&args[0]); if match_trait_method(cx, expr, &paths::ITERATOR) || is_type_diagnostic_item(cx, caller_ty, sym::result_type) diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index f7231bba1750..7adf6bdeac5a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -3096,7 +3096,7 @@ fn lint_flat_map_identity<'tcx>( if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind; if path.segments.len() == 1; - if path.segments[0].ident.as_str() == binding_ident.as_str(); + if path.segments[0].ident.name == binding_ident.name; then { apply_lint("called `flat_map(|x| x)` on an `Iterator`"); diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index 004dd50a31be..8d0c3b8e0fe8 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -89,9 +89,9 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons if let [obj, _] = args; if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD); then { - if path.ident.as_str() == sym!(max).as_str() { + if path.ident.name == sym!(max) { fetch_const(cx, args, MinMax::Max) - } else if path.ident.as_str() == sym!(min).as_str() { + } else if path.ident.name == sym!(min) { fetch_const(cx, args, MinMax::Min) } else { None diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 27f1074a0dd8..0e49eaab4368 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -63,7 +63,7 @@ impl MissingDoc { if let Some(meta) = list.get(0); if let Some(name) = meta.ident(); then { - name.as_str() == "include" + name.name == sym::include } else { false } diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 6b9a37b52520..d795f1264579 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -3,9 +3,7 @@ //! This lint is **warn** by default use crate::utils::sugg::Sugg; -use crate::utils::{ - is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg, -}; +use crate::utils::{is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; diff --git a/src/tools/clippy/clippy_lints/src/needless_borrow.rs b/src/tools/clippy/clippy_lints/src/needless_borrow.rs index bff53eb8ccad..f1c06692e30d 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrow.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrow.rs @@ -2,7 +2,7 @@ //! //! This lint is **warn** by default -use crate::utils::{snippet_opt, span_lint_and_then}; +use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, HirId, Item, Mutability, Pat, PatKind}; @@ -10,7 +10,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::sym; declare_clippy_lint! { /// **What it does:** Checks for address of operations (`&`) that are going to @@ -116,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if item.attrs.iter().any(|a| a.has_name(sym::automatically_derived)) { + if is_automatically_derived(item.attrs) { debug_assert!(self.derived_item.is_none()); self.derived_item = Some(item.hir_id); } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 3b71f1b46e2e..89fe2c19a570 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -8,11 +8,12 @@ use rustc_ast::ast::Attribute; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Impl, Node, PatKind, QPath, TyKind}; +use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Node, PatKind, QPath, TyKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; @@ -151,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { // Ignore `self`s. if idx == 0 { if let PatKind::Binding(.., ident, _) = arg.pat.kind { - if ident.as_str() == "self" { + if ident.name == kw::SelfLower { continue; } } diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs new file mode 100644 index 000000000000..9e9b79ee1cf0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -0,0 +1,232 @@ +use rustc_errors::Applicability; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::DefIdTree; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +use crate::utils; +use if_chain::if_chain; + +declare_clippy_lint! { + /// **What it does:** + /// Suggests alternatives for useless applications of `?` in terminating expressions + /// + /// **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct TO { + /// magic: Option, + /// } + /// + /// fn f(to: TO) -> Option { + /// Some(to.magic?) + /// } + /// + /// struct TR { + /// magic: Result, + /// } + /// + /// fn g(tr: Result) -> Result { + /// tr.and_then(|t| Ok(t.magic?)) + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// struct TO { + /// magic: Option, + /// } + /// + /// fn f(to: TO) -> Option { + /// to.magic + /// } + /// + /// struct TR { + /// magic: Result, + /// } + /// + /// fn g(tr: Result) -> Result { + /// tr.and_then(|t| t.magic) + /// } + /// ``` + pub NEEDLESS_QUESTION_MARK, + complexity, + "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result`." +} + +const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0); +const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0); + +pub struct NeedlessQuestionMark { + msrv: Option, +} + +impl NeedlessQuestionMark { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); + +#[derive(Debug)] +enum SomeOkCall<'a> { + SomeCall(&'a Expr<'a>, &'a Expr<'a>), + OkCall(&'a Expr<'a>, &'a Expr<'a>), +} + +impl LateLintPass<'_> for NeedlessQuestionMark { + /* + * The question mark operator is compatible with both Result and Option, + * from Rust 1.13 and 1.22 respectively. + */ + + /* + * What do we match: + * Expressions that look like this: + * Some(option?), Ok(result?) + * + * Where do we match: + * Last expression of a body + * Return statement + * A body's value (single line closure) + * + * What do we not match: + * Implicit calls to `from(..)` on the error value + */ + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + let e = match &expr.kind { + ExprKind::Ret(Some(e)) => e, + _ => return, + }; + + if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) { + emit_lint(cx, &ok_some_call); + } + } + + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + // Function / Closure block + let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind { + block.expr + } else { + // Single line closure + Some(&body.value) + }; + + if_chain! { + if let Some(expr) = expr_opt; + if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr); + then { + emit_lint(cx, &ok_some_call); + } + }; + } + + extract_msrv_attr!(LateContext); +} + +fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { + let (entire_expr, inner_expr) = match expr { + SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner), + }; + + utils::span_lint_and_sugg( + cx, + NEEDLESS_QUESTION_MARK, + entire_expr.span, + "Question mark operator is useless here", + "try", + format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)), + Applicability::MachineApplicable, + ); +} + +fn is_some_or_ok_call<'a>( + nqml: &NeedlessQuestionMark, + cx: &'a LateContext<'_>, + expr: &'a Expr<'_>, +) -> Option> { + if_chain! { + // Check outer expression matches CALL_IDENT(ARGUMENT) format + if let ExprKind::Call(path, args) = &expr.kind; + if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; + if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + + // Extract inner expression from ARGUMENT + if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; + if let ExprKind::Call(called, args) = &inner_expr_with_q.kind; + if args.len() == 1; + + if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind; + then { + // Extract inner expr type from match argument generated by + // question mark operator + let inner_expr = &args[0]; + + let inner_ty = cx.typeck_results().expr_ty(inner_expr); + let outer_ty = cx.typeck_results().expr_ty(expr); + + // Check if outer and inner type are Option + let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type); + let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type); + + // Check for Option MSRV + let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); + if outer_is_some && inner_is_some && meets_option_msrv { + return Some(SomeOkCall::SomeCall(expr, inner_expr)); + } + + // Check if outer and inner type are Result + let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type); + let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type); + + // Additional check: if the error type of the Result can be converted + // via the From trait, then don't match + let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); + + // Must meet Result MSRV + let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); + if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv { + return Some(SomeOkCall::OkCall(expr, inner_expr)); + } + } + } + + None +} + +fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool { + return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr); +} + +fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == ok_id; + } + } + } + false +} + +fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == some_id; + } + } + } + false +} diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 391f893ef35f..7bdf975ffd44 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -66,7 +66,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// Returns true iff the given expression is the result of calling `Result::ok` fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { - path.ident.name.to_ident_string() == "ok" + path.ident.name.as_str() == "ok" && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type) } else { false @@ -110,7 +110,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { let mut should_wrap = false; - + if let Some(Expr { kind: ExprKind::Match( @@ -124,7 +124,11 @@ fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) = parent.expr { should_wrap = expr.hir_id == arms[1].body.hir_id; - } else if let Some(Expr { kind: ExprKind::If(_, _, Some(else_clause)), .. }) = parent.expr { + } else if let Some(Expr { + kind: ExprKind::If(_, _, Some(else_clause)), + .. + }) = parent.expr + { should_wrap = expr.hir_id == else_clause.hir_id; } diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 04b6e5d58478..ed314937ce8b 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -1,6 +1,6 @@ use crate::utils::{is_automatically_derived, span_lint_hir}; use if_chain::if_chain; -use rustc_hir::{Item, ItemKind, Impl}; +use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 7814065e31a1..d96a9b025f08 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -6,7 +6,7 @@ use rustc_ast::attr; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; -use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node, PatKind, Impl}; +use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -63,7 +63,7 @@ declare_clippy_lint! { /// /// **Why is this bad?** Arguments passed by value might result in an unnecessary /// shallow copy, taking up more space in the stack and requiring a call to - /// `memcpy`, which which can be expensive. + /// `memcpy`, which can be expensive. /// /// **Example:** /// diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index b832add009f8..c6329a1381c9 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -8,8 +8,8 @@ use crate::utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ - BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Impl, - Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, + BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, Impl, ImplItem, ImplItemKind, Item, + ItemKind, Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs index 803ebada54b7..8cd6692ce03a 100644 --- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs +++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// **Why is this bad?** Since `&` is Copy, it's useless to have a /// reference on `Option<&T>`. /// - /// **Known problems:** It may be irrevelent to use this lint on + /// **Known problems:** It may be irrelevant to use this lint on /// public API code as it will make a breaking change to apply it. /// /// **Example:** diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 35827e027aab..63548d8fdb43 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -202,7 +202,7 @@ fn check_final_expr<'tcx>( check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); } }, - | MatchSource::IfLetDesugar { + MatchSource::IfLetDesugar { contains_else_clause: true, } => { if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { @@ -217,6 +217,9 @@ fn check_final_expr<'tcx>( } fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + if ret_span.from_expansion() { + return; + } match inner_span { Some(inner_span) => { if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index ca4fd9f35597..44e739725c82 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -1,5 +1,5 @@ use crate::utils::{get_trait_def_id, paths, span_lint}; -use rustc_hir::{Item, ItemKind, Impl}; +use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index f2f3dfa09a7d..24da056770c9 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -396,5 +396,5 @@ fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool { } fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool { - !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str() + !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 386987eb181e..699fd51ccc19 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -91,7 +91,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind; if rhs2.segments.len() == 1; - if ident.as_str() == rhs2.segments[0].ident.as_str(); + if ident.name == rhs2.segments[0].ident.name; if eq_expr_value(cx, tmp_init, lhs1); if eq_expr_value(cx, rhs1, lhs2); then { diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs index 675eaf4277a4..c53727ba1600 100644 --- a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs +++ b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs @@ -1,7 +1,7 @@ use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint}; use if_chain::if_chain; use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Impl}; +use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs index 2696c5e781ab..3b5a83d2a0be 100644 --- a/src/tools/clippy/clippy_lints/src/types.rs +++ b/src/tools/clippy/clippy_lints/src/types.rs @@ -8,7 +8,6 @@ use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, @@ -19,7 +18,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::TypeFoldable; -use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckResults}; +use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeAndMut, TypeckResults}; +use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::Span; @@ -30,11 +30,13 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; +use crate::utils::sugg::Sugg; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, - last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_hir_ty_cfg_dependant, + is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, + multispan_sugg, numeric_literal::NumericLiteral, qpath_res, reindent_multiline, sext, snippet, snippet_opt, + snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, + span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -73,7 +75,7 @@ declare_clippy_lint! { /// **Why is this bad?** `Vec` already keeps its contents in a separate area on /// the heap. So if you `Box` its contents, you just add another level of indirection. /// - /// **Known problems:** Vec> makes sense if T is a large type (see #3530, + /// **Known problems:** Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), /// 1st comment). /// /// **Example:** @@ -1279,8 +1281,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for casts from a less-strictly-aligned pointer to a - /// more-strictly-aligned pointer + /// **What it does:** Checks for casts, using `as` or `pointer::cast`, + /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer /// /// **Why is this bad?** Dereferencing the resulting pointer may be undefined /// behavior. @@ -1293,6 +1295,9 @@ declare_clippy_lint! { /// ```rust /// let _ = (&1u8 as *const u8) as *const u16; /// let _ = (&mut 1u8 as *mut u8) as *mut u16; + /// + /// (&1u8 as *const u8).cast::(); + /// (&mut 1u8 as *mut u8).cast::(); /// ``` pub CAST_PTR_ALIGNMENT, pedantic, @@ -1634,12 +1639,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } if let ExprKind::Cast(ref ex, cast_to) = expr.kind { - if let TyKind::Path(QPath::Resolved(_, path)) = cast_to.kind { - if let Res::Def(_, def_id) = path.res { - if cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) { - return; - } - } + if is_hir_ty_cfg_dependant(cx, cast_to) { + return; } let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); @@ -1689,6 +1690,19 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { + if_chain! { + if method_path.ident.name == sym!(cast); + if let Some(generic_args) = method_path.args; + if let [GenericArg::Type(cast_to)] = generic_args.args; + // There probably is no obvious reason to do this, just to be consistent with `as` cases. + if !is_hir_ty_cfg_dependant(cx, cast_to); + then { + let (cast_from, cast_to) = + (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } + } } } } @@ -2873,3 +2887,93 @@ impl<'tcx> LateLintPass<'tcx> for RefToMut { } } } + +const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0); + +declare_clippy_lint! { + /// **What it does:** + /// Checks for `as` casts between raw pointers without changing its mutability, + /// namely `*const T` to `*const U` and `*mut T` to `*mut U`. + /// + /// **Why is this bad?** + /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because + /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr as *const i32; + /// let _ = mut_ptr as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr.cast::(); + /// let _ = mut_ptr.cast::(); + /// ``` + pub PTR_AS_PTR, + pedantic, + "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" +} + +pub struct PtrAsPtr { + msrv: Option, +} + +impl PtrAsPtr { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(PtrAsPtr => [PTR_AS_PTR]); + +impl<'tcx> LateLintPass<'tcx> for PtrAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &PTR_AS_PTR_MSRV) { + return; + } + + if expr.span.from_expansion() { + return; + } + + if_chain! { + if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind; + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)); + if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); + if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind(); + if matches!((from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)); + // The `U` in `pointer::cast` have to be `Sized` + // as explained here: https://github.com/rust-lang/rust/issues/60602. + if to_pointee_ty.is_sized(cx.tcx.at(expr.span), cx.param_env); + then { + let mut applicability = Applicability::MachineApplicable; + let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability); + let turbofish = match &cast_to_hir_ty.kind { + TyKind::Infer => Cow::Borrowed(""), + TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), + _ => Cow::Owned(format!("::<{}>", to_pointee_ty)), + }; + span_lint_and_sugg( + cx, + PTR_AS_PTR, + expr.span, + "`as` casting between raw pointers without changing its mutability", + "try `pointer::cast`, a safer alternative", + format!("{}.cast{}()", cast_expr_sugg.maybe_par(), turbofish), + applicability, + ); + } + } + } + + extract_msrv_attr!(LateContext); +} diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs index 0bccfc156788..9b45d38afd42 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs @@ -183,7 +183,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } ] = &closure_body.params; if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr], _) = &closure_body.value.kind; - if method_path.ident.name.to_ident_string() == "cmp"; + if method_path.ident.name == sym::cmp; then { let (closure_body, closure_arg, reverse) = if mirrored_exprs( &cx, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 07cd752184bb..8ac5dd696b76 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -5,7 +5,7 @@ use crate::utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Impl, Node}; +use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs index a61717943110..5349c4f7eb8a 100644 --- a/src/tools/clippy/clippy_lints/src/unused_self.rs +++ b/src/tools/clippy/clippy_lints/src/unused_self.rs @@ -1,7 +1,7 @@ use if_chain::if_chain; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor}; -use rustc_hir::{HirId, ImplItem, ImplItemKind, ItemKind, Impl, Path}; +use rustc_hir::{HirId, Impl, ImplItem, ImplItemKind, ItemKind, Path}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 6a87f5343698..b82909eaea60 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -1,6 +1,5 @@ use crate::utils::{ - differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then, - usage::is_potentially_mutated, + differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated, }; use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index b82ea66190fc..72d1ca739291 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -28,8 +28,8 @@ declare_clippy_lint! { /// feels inconsistent. /// /// **Known problems:** - /// - False positive when using associated types (#2843) - /// - False positives in some situations when using generics (#3410) + /// - False positive when using associated types ([#2843](https://github.com/rust-lang/rust-clippy/issues/2843)) + /// - False positives in some situations when using generics ([#3410](https://github.com/rust-lang/rust-clippy/issues/3410)) /// /// **Example:** /// ```rust diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index efa9c3fab4ab..c53348539860 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -80,10 +80,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { + if match_trait_method(cx, e, &paths::INTO_ITERATOR) && name.ident.name == sym::into_iter { if let Some(parent_expr) = get_parent_expr(cx, e) { if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { - if &*parent_name.ident.as_str() != "into_iter" { + if parent_name.ident.name != sym::into_iter { return; } } diff --git a/src/tools/clippy/clippy_lints/src/utils/attrs.rs b/src/tools/clippy/clippy_lints/src/utils/attrs.rs index 24052a243af8..8d28421d70d7 100644 --- a/src/tools/clippy/clippy_lints/src/utils/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/utils/attrs.rs @@ -1,6 +1,7 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_session::Session; +use rustc_span::sym; use std::str::FromStr; /// Deprecation status of attributes known by Clippy. @@ -64,11 +65,11 @@ pub fn get_attr<'a>( return false; }; let attr_segments = &attr.path.segments; - if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" { + if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy { BUILTIN_ATTRIBUTES .iter() - .find_map(|(builtin_name, deprecation_status)| { - if *builtin_name == attr_segments[1].ident.to_string() { + .find_map(|&(builtin_name, ref deprecation_status)| { + if attr_segments[1].ident.name.as_str() == builtin_name { Some(deprecation_status) } else { None @@ -99,7 +100,7 @@ pub fn get_attr<'a>( }, DeprecationStatus::None => { diag.cancel(); - attr_segments[1].ident.to_string() == name + attr_segments[1].ident.name.as_str() == name }, } }, diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs index 062df68c0a48..10120a8805db 100644 --- a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { - both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) + both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) }, (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) @@ -102,7 +102,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }) }, (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => { - both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str()) + both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) && both(le, re, |l, r| self.eq_expr(l, r)) }, (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), @@ -124,7 +124,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }, (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, (&ExprKind::Loop(ref lb, ref ll, ref lls), &ExprKind::Loop(ref rb, ref rl, ref rls)) => { - lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.as_str() == r.ident.as_str()) + lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) }, (&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => { ls == rs @@ -191,7 +191,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right); - li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp) + li.name == ri.name && self.eq_pat(lp, rp) } /// Checks whether two patterns are the same. @@ -205,7 +205,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, (&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => { - lb == rb && li.name.as_str() == ri.name.as_str() && both(lp, rp, |l, r| self.eq_pat(l, r)) + lb == rb && li.name == ri.name && both(lp, rp, |l, r| self.eq_pat(l, r)) }, (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r), @@ -266,8 +266,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { // The == of idents doesn't work with different contexts, // we have to be explicit about hygiene - left.ident.as_str() == right.ident.as_str() - && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) + left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 407f06f48942..7aa17520ba79 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -10,9 +10,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; +use rustc_hir::{ + BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp, +}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::mir::interpret::ConstValue; @@ -272,6 +275,28 @@ declare_clippy_lint! { "interning a symbol that is pre-interned and defined as a constant" } +declare_clippy_lint! { + /// **What it does:** Checks for unnecessary conversion from Symbol to a string. + /// + /// **Why is this bad?** It's faster use symbols directly intead of strings. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Good: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -868,11 +893,11 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { #[derive(Default)] pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant name. - symbol_map: FxHashMap, + // Maps the symbol value to the constant DefId. + symbol_map: FxHashMap, } -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { @@ -880,16 +905,18 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { return; } - if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) { - for item in cx.tcx.item_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item.ident.to_string()); + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + if let Some(Res::Def(_, def_id)) = path_to_res(cx, module) { + for item in cx.tcx.item_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item_def_id); + } } } } @@ -903,7 +930,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); let value = Symbol::intern(&arg).as_u32(); - if let Some(symbol_const) = self.symbol_map.get(&value); + if let Some(&def_id) = self.symbol_map.get(&value); then { span_lint_and_sugg( cx, @@ -911,10 +938,135 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { is_expn_of(expr.span, "sym").unwrap_or(expr.span), "interning a defined symbol", "try", - format!("rustc_span::symbol::sym::{}", symbol_const), + cx.tcx.def_path_str(def_id), Applicability::MachineApplicable, ); } } + if let ExprKind::Binary(op, left, right) = expr.kind { + if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary string allocation", + "try", + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), + Applicability::MachineApplicable, + ); + } + }, + // nothing found + [(_, None), (_, None)] => {}, + } + } + } + } +} + +impl InterningDefinedSymbol { + fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[ + &paths::SYMBOL_AS_STR, + &paths::SYMBOL_TO_IDENT_STRING, + &paths::TO_STRING_METHOD, + ]; + // SymbolStr might be de-referenced: `&*symbol.as_str()` + let call = if_chain! { + if let ExprKind::AddrOf(_, _, e) = expr.kind; + if let ExprKind::Unary(UnOp::UnDeref, e) = e.kind; + then { e } else { expr } + }; + if_chain! { + // is a method call + if let ExprKind::MethodCall(_, _, [item], _) = call.kind; + if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); + let ty = cx.typeck_results().expr_ty(item); + // ...on either an Ident or a Symbol + if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + Some(false) + } else if match_type(cx, ty, &paths::IDENT) { + Some(true) + } else { + None + }; + // ...which converts it to a string + let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; + if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + then { + let is_to_owned = path.last().unwrap().ends_with("string"); + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); + } + } + // is a string constant + if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + let value = Symbol::intern(&s).as_u32(); + // ...which matches a symbol constant + if let Some(&def_id) = self.symbol_map.get(&value) { + return Some(SymbolStrExpr::Const(def_id)); + } + } + None + } +} + +enum SymbolStrExpr<'tcx> { + /// a string constant with a corresponding symbol constant + Const(DefId), + /// a "symbol to string" expression like `symbol.as_str()` + Expr { + /// part that evaluates to `Symbol` or `Ident` + item: &'tcx Expr<'tcx>, + is_ident: bool, + /// whether an owned `String` is created like `to_ident_string()` + is_to_owned: bool, + }, +} + +impl<'tcx> SymbolStrExpr<'tcx> { + /// Returns a snippet that evaluates to a `Symbol` and is const if possible + fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { + match *self { + Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), + Self::Expr { item, is_ident, .. } => { + let mut snip = snippet(cx, item.span.source_callsite(), ".."); + if is_ident { + // get `Ident.name` + snip.to_mut().push_str(".name"); + } + snip + }, + } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 4c707c4b9044..9b262517a983 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ #[macro_use] -pub mod sym; +pub mod sym_helper; #[allow(clippy::module_name_repetitions)] pub mod ast_utils; @@ -56,8 +56,8 @@ use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; -use rustc_span::sym as rustc_sym; -use rustc_span::symbol::{self, kw, Symbol}; +use rustc_span::sym; +use rustc_span::symbol::{kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -1121,7 +1121,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d /// implementations have. pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(|attr| attr.has_name(rustc_sym::automatically_derived)) + attrs.iter().any(|attr| attr.has_name(sym::automatically_derived)) } /// Remove blocks around an expression. @@ -1434,12 +1434,13 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { let map = cx.tcx.hir(); let parent_id = map.get_parent_node(expr.hir_id); let parent_node = map.get(parent_id); - if let Node::Expr(Expr { kind: ExprKind::If(_, _, _), .. }) = parent_node { - true - } - else { - false - } + matches!( + parent_node, + Node::Expr(Expr { + kind: ExprKind::If(_, _, _), + .. + }) + ) } // Finds the attribute with the given name, if any @@ -1514,7 +1515,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { pub fn is_no_std_crate(krate: &Crate<'_>) -> bool { krate.item.attrs.iter().any(|attr| { if let ast::AttrKind::Normal(ref attr, _) = attr.kind { - attr.path == symbol::sym::no_std + attr.path == sym::no_std } else { false } @@ -1686,6 +1687,18 @@ macro_rules! unwrap_cargo_metadata { }}; } +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { + if_chain! { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; + if let Res::Def(_, def_id) = path.res; + then { + cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) + } else { + false + } + } +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs index 2080a49a11cd..c0b203b5388d 100644 --- a/src/tools/clippy/clippy_lints/src/utils/paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs @@ -54,6 +54,10 @@ pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; +#[cfg(feature = "internal-lints")] +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; +#[cfg(feature = "internal-lints")] +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; @@ -65,6 +69,8 @@ pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; #[cfg(feature = "internal-lints")] +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; +#[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; #[cfg(feature = "internal-lints")] @@ -148,8 +154,12 @@ pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_wit #[cfg(feature = "internal-lints")] pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; +#[cfg(feature = "internal-lints")] pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; +#[cfg(feature = "internal-lints")] pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; #[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/src/tools/clippy/clippy_lints/src/utils/sym.rs b/src/tools/clippy/clippy_lints/src/utils/sym_helper.rs similarity index 68% rename from src/tools/clippy/clippy_lints/src/utils/sym.rs rename to src/tools/clippy/clippy_lints/src/utils/sym_helper.rs index 273288c3d52c..f47dc80ebade 100644 --- a/src/tools/clippy/clippy_lints/src/utils/sym.rs +++ b/src/tools/clippy/clippy_lints/src/utils/sym_helper.rs @@ -1,4 +1,5 @@ #[macro_export] +/// Convenience wrapper around rustc's `Symbol::intern` macro_rules! sym { ($tt:tt) => { rustc_span::symbol::Symbol::intern(stringify!($tt)) diff --git a/src/tools/clippy/clippy_lints/src/utils/visitors.rs b/src/tools/clippy/clippy_lints/src/utils/visitors.rs index b769a18802b6..ebf69df31ca4 100644 --- a/src/tools/clippy/clippy_lints/src/utils/visitors.rs +++ b/src/tools/clippy/clippy_lints/src/utils/visitors.rs @@ -107,7 +107,7 @@ where if let Some(el) = else_opt { self.visit_expr(el); } - } + }, hir::ExprKind::Match(cond, arms, _) => { self.inside_stmt(true).visit_expr(cond); for arm in arms { diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs new file mode 100644 index 000000000000..e632a7e57ee8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -0,0 +1,187 @@ +use crate::utils::{is_type_diagnostic_item, match_def_path, paths, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Local, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span, Symbol}; +use std::convert::TryInto; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `push` immediately after creating a new `Vec`. + /// + /// **Why is this bad?** The `vec![]` macro is both more performant and easier to read than + /// multiple `push` calls. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let mut v = Vec::new(); + /// v.push(0); + /// ``` + /// Use instead: + /// ```rust + /// let v = vec![0]; + /// ``` + pub VEC_INIT_THEN_PUSH, + perf, + "`push` immediately after `Vec` creation" +} + +impl_lint_pass!(VecInitThenPush => [VEC_INIT_THEN_PUSH]); + +#[derive(Default)] +pub struct VecInitThenPush { + searcher: Option, +} + +#[derive(Clone, Copy)] +enum VecInitKind { + New, + WithCapacity(u64), +} +struct VecPushSearcher { + init: VecInitKind, + name: Symbol, + lhs_is_local: bool, + lhs_span: Span, + err_span: Span, + found: u64, +} +impl VecPushSearcher { + fn display_err(&self, cx: &LateContext<'_>) { + match self.init { + _ if self.found == 0 => return, + VecInitKind::WithCapacity(x) if x > self.found => return, + _ => (), + }; + + let mut s = if self.lhs_is_local { + String::from("let ") + } else { + String::new() + }; + s.push_str(&snippet(cx, self.lhs_span, "..")); + s.push_str(" = vec![..];"); + + span_lint_and_sugg( + cx, + VEC_INIT_THEN_PUSH, + self.err_span, + "calls to `push` immediately after creation", + "consider using the `vec![]` macro", + s, + Applicability::HasPlaceholders, + ); + } +} + +impl LateLintPass<'_> for VecInitThenPush { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + self.searcher = None; + if_chain! { + if !in_external_macro(cx.sess(), local.span); + if let Some(init) = local.init; + if let PatKind::Binding(BindingAnnotation::Mutable, _, ident, None) = local.pat.kind; + if let Some(init_kind) = get_vec_init_kind(cx, init); + then { + self.searcher = Some(VecPushSearcher { + init: init_kind, + name: ident.name, + lhs_is_local: true, + lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)), + err_span: local.span, + found: 0, + }); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if self.searcher.is_none() { + if_chain! { + if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::Assign(left, right, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; + if let Some(name) = path.segments.get(0); + if let Some(init_kind) = get_vec_init_kind(cx, right); + then { + self.searcher = Some(VecPushSearcher { + init: init_kind, + name: name.ident.name, + lhs_is_local: false, + lhs_span: left.span, + err_span: expr.span, + found: 0, + }); + } + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let Some(searcher) = self.searcher.take() { + if_chain! { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind; + if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind; + if path.ident.name.as_str() == "push"; + if let ExprKind::Path(QPath::Resolved(_, self_path)) = self_arg.kind; + if let [self_name] = self_path.segments; + if self_name.ident.name == searcher.name; + then { + self.searcher = Some(VecPushSearcher { + found: searcher.found + 1, + err_span: searcher.err_span.to(stmt.span), + .. searcher + }); + } else { + searcher.display_err(cx); + } + } + } + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { + if let Some(searcher) = self.searcher.take() { + searcher.display_err(cx); + } + } +} + +fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + if let ExprKind::Call(func, args) = expr.kind { + match func.kind { + ExprKind::Path(QPath::TypeRelative(ty, name)) + if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type) => + { + if name.ident.name == sym::new { + return Some(VecInitKind::New); + } else if name.ident.name.as_str() == "with_capacity" { + return args.get(0).and_then(|arg| { + if_chain! { + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(num, _) = lit.node; + then { + Some(VecInitKind::WithCapacity(num.try_into().ok()?)) + } else { + None + } + } + }); + } + } + ExprKind::Path(QPath::Resolved(_, path)) + if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type) => + { + return Some(VecInitKind::New); + } + _ => (), + } + } + None +} diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 5683a71efea4..10005a7fc81e 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -7,7 +7,8 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::BytePos; +use rustc_span::symbol::kw; +use rustc_span::{sym, BytePos}; declare_clippy_lint! { /// **What it does:** Checks for `use Enum::*`. @@ -198,12 +199,12 @@ impl WildcardImports { // Allow "...prelude::..::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments.iter().any(|ps| ps.ident.as_str() == "prelude") + segments.iter().any(|ps| ps.ident.name == sym::prelude) } // Allow "super::*" imports in tests. fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { - segments.len() == 1 && segments[0].ident.as_str() == "super" + segments.len() == 1 && segments[0].ident.name == kw::Super } fn is_test_module_or_function(item: &Item<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 337f7a229b90..af324f831dfa 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -10,7 +10,8 @@ use rustc_lexer::unescape::{self, EscapeError}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, BytePos, Span, Symbol}; +use rustc_span::symbol::kw; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -301,7 +302,7 @@ impl EarlyLintPass for Write { } } else if mac.path == sym!(writeln) { if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { - if fmt_str.symbol == Symbol::intern("") { + if fmt_str.symbol == kw::Empty { let mut applicability = Applicability::MachineApplicable; // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed #[allow(clippy::option_if_let_else)] @@ -484,7 +485,7 @@ impl Write { fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if fmt_str.symbol == Symbol::intern("") { + if fmt_str.symbol == kw::Empty { let name = mac.path.segments[0].ident.name; span_lint_and_sugg( cx, diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md index 60dfdb76650a..1a7a30c61be5 100644 --- a/src/tools/clippy/doc/adding_lints.md +++ b/src/tools/clippy/doc/adding_lints.md @@ -147,10 +147,14 @@ add `// edition:2018` at the top of the test file (note that it's space-sensitiv Manually testing against an example file can be useful if you have added some `println!`s and the test suite output becomes unreadable. To try Clippy with -your local modifications, run `env CLIPPY_TESTS=true cargo run --bin -clippy-driver -- -L ./target/debug input.rs` from the working copy root. +your local modifications, run -With tests in place, let's have a look at implementing our lint now. +``` +env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs +``` + +from the working copy root. With tests in place, let's have a look at +implementing our lint now. ## Lint declaration diff --git a/src/tools/clippy/doc/roadmap-2021.md b/src/tools/clippy/doc/roadmap-2021.md new file mode 100644 index 000000000000..fe8b080f56f2 --- /dev/null +++ b/src/tools/clippy/doc/roadmap-2021.md @@ -0,0 +1,235 @@ +# Roadmap 2021 + +# Summary + +This Roadmap lays out the plans for Clippy in 2021: + +- Improving usability and reliability +- Improving experience of contributors and maintainers +- Develop and specify processes + +Members of the Clippy team will be assigned tasks from one or more of these +topics. The team member is then responsible to complete the assigned tasks. This +can either be done by implementing them or by providing mentorship to interested +contributors. + +# Motivation + +With the ongoing growth of the Rust language and with that of the whole +ecosystem, also Clippy gets more and more users and contributors. This is good +for the project, but also brings challenges along. Some of these challenges are: + +- More issues about reliability or usability are popping up +- Traffic is hard to handle for a small team +- Bigger projects don't get completed due to the lack of processes and/or time + of the team members + +Additionally, according to the [Rust Roadmap 2021], clear processes should be +defined by every team and unified across teams. This Roadmap is the first step +towards this. + +[Rust Roadmap 2021]: https://github.com/rust-lang/rfcs/pull/3037 + +# Explanation + +This section will explain the things that should be done in 2021. It is +important to note, that this document focuses on the "What?", not the "How?". +The later will be addressed in follow-up tracking issue, with an assigned team +member. + +The following is split up in two major sections. The first section covers the +user facing plans, the second section the internal plans. + +## User Facing + +Clippy should be as pleasant to use and configure as possible. This section +covers plans that should be implemented to improve the situation of Clippy in +this regard. + +### Usability + +In the following, plans to improve the usability are covered. + +#### No Output After `cargo check` + +Currently when `cargo clippy` is run after `cargo check`, it does not produce +any output. This is especially problematic since `rust-analyzer` is on the rise +and it uses `cargo check` for checking code. A fix is already implemented, but +it still has to be pushed over the finish line. This also includes the +stabilization of the `cargo clippy --fix` command or the support of multi-span +suggestions in `rustfix`. + +- [#4612](https://github.com/rust-lang/rust-clippy/issues/4612) + +#### `lints.toml` Configuration + +This is something that comes up every now and then: a reusable configuration +file, where lint levels can be defined. Discussions about this often lead to +nothing specific or to "we need an RFC for this". And this is exactly what needs +to be done. Get together with the cargo team and write an RFC and implement such +a configuration file somehow and somewhere. + +- [#3164](https://github.com/rust-lang/rust-clippy/issues/3164) +- [cargo#5034](https://github.com/rust-lang/cargo/issues/5034) +- [IRLO](https://internals.rust-lang.org/t/proposal-cargo-lint-configuration/9135/8) + +#### Lint Groups + +There are more and more issues about managing lints in Clippy popping up. Lints +are hard to implement with a guarantee of no/few false positives (FPs). One way +to address this might be to introduce more lint groups to give users the ability +to better manage lints, or improve the process of classifying lints, so that +disabling lints due to FPs becomes rare. It is important to note, that Clippy +lints are less conservative than `rustc` lints, which won't change in the +future. + +- [#5537](https://github.com/rust-lang/rust-clippy/issues/5537) +- [#6366](https://github.com/rust-lang/rust-clippy/issues/6366) + +### Reliability + +In the following, plans to improve the reliability are covered. + +#### False Positive Rate + +In the worst case, new lints are only available in nightly for 2 weeks, before +hitting beta and ultimately stable. This and the fact that fewer people use +nightly Rust nowadays makes it more probable that a lint with many FPs hits +stable. This leads to annoyed users, that will disable these new lints in the +best case and to more annoyed users, that will stop using Clippy in the worst. +A process should be developed and implemented to prevent this from happening. + +- [#6429](https://github.com/rust-lang/rust-clippy/issues/6429) + +## Internal + +(The end of) 2020 has shown, that Clippy has to think about the available +resources, especially regarding management and maintenance of the project. This +section address issues affecting team members and contributors. + +### Management + +In 2020 Clippy achieved over 1000 open issues with regularly between 25-35 open +PRs. This is simultaneously a win and a loss. More issues and PRs means more +people are interested in Clippy and in contributing to it. On the other hand, it +means for team members more work and for contributors longer wait times for +reviews. The following will describe plans how to improve the situation for both +team members and contributors. + +#### Clear Expectations for Team Members + +According to the [Rust Roadmap 2021], a document specifying what it means to be +a member of the team should be produced. This should not put more pressure on +the team members, but rather help them and interested folks to know what the +expectations are. With this it should also be easier to recruit new team members +and may encourage people to get in touch, if they're interested to join. + +#### Scaling up the Team + +More people means less work for each individual. Together with the document +about expectations for team members, a document defining the process of how to +join the team should be produced. This can also increase the stability of the +team, in case of current members dropping out (temporarily). There can also be +different roles in the team, like people triaging vs. people reviewing. + +#### Regular Meetings + +Other teams have regular meetings. Clippy is big enough that it might be worth +to also do them. Especially if more people join the team, this can be important +for sync-ups. Besides the asynchronous communication, that works well for +working on separate lints, a meeting adds a synchronous alternative at a known +time. This is especially helpful if there are bigger things that need to be +discussed (like the projects in this roadmap). For starters bi-weekly meetings +before Rust syncs might make sense. + +#### Triaging + +To get a handle on the influx of open issues, a process for triaging issues and +PRs should be developed. Officially, Clippy follows the Rust triage process, but +currently no one enforces it. This can be improved by sharing triage teams +across projects or by implementing dashboards / tools which simplify triaging. + +### Development + +Improving the developer and contributor experience is something the Clippy team +works on regularly. Though, some things might need special attention and +planing. These topics are listed in the following. + +#### Process for New and Existing Lints + +As already mentioned above, classifying new lints gets quite hard, because the +probability of a buggy lint getting into stable is quite high. A process should +be implemented on how to classify lints. In addition, a test system should be +developed to find out which lints are currently problematic in real world code +to fix or disable them. + +- [#6429 (comment)](https://github.com/rust-lang/rust-clippy/issues/6429#issuecomment-741056379) +- [#6429 (comment)](https://github.com/rust-lang/rust-clippy/issues/6429#issuecomment-741153345) + +#### Processes + +Related to the point before, a process for suggesting and discussing major +changes should be implemented. It's also not clearly defined when a lint should +be enabled or disabled by default. This can also be improved by the test system +mentioned above. + +#### Dev-Tools + +There's already `cargo dev` which makes Clippy development easier and more +pleasant. This can still be expanded, so that it covers more areas of the +development process. + +- [#5394](https://github.com/rust-lang/rust-clippy/issues/5394) + +#### Contributor Guide + +Similar to a Clippy Book, which describes how to use Clippy, a book about how to +contribute to Clippy might be helpful for new and existing contributors. There's +already the `doc` directory in the Clippy repo, this can be turned into a +`mdbook`. + +#### `rustc` integration + +Recently Clippy was integrated with `git subtree` into the `rust-lang/rust` +repository. This made syncing between the two repositories easier. A +`#[non_exhaustive]` list of things that still can be improved is: + +1. Use the same `rustfmt` version and configuration as `rustc`. +2. Make `cargo dev` work in the Rust repo, just as it works in the Clippy repo. + E.g. `cargo dev bless` or `cargo dev update_lints`. And even add more things + to it that might be useful for the Rust repo, e.g. `cargo dev deprecate`. +3. Easier sync process. The `subtree` situation is not ideal. + +## Prioritization + +The most pressing issues for users of Clippy are of course the user facing +issues. So there should be a priority on those issues, but without losing track +of the internal issues listed in this document. + +Getting the FP rate of warn/deny-by-default lints under control should have the +highest priority. Other user facing issues should also get a high priority, but +shouldn't be in the way of addressing internal issues. + +To better manage the upcoming projects, the basic internal processes, like +meetings, tracking issues and documentation, should be established as soon as +possible. They might even be necessary to properly manage the projects, +regarding the user facing issues. + +# Prior Art + +## Rust Roadmap + +Rust's roadmap process was established by [RFC 1728] in 2016. Since then every +year a roadmap was published, that defined the bigger plans for the coming +years. This years roadmap can be found [here][Rust Roadmap 2021]. + +[RFC 1728]: https://rust-lang.github.io/rfcs/1728-north-star.html + +# Drawbacks + +## Big Roadmap + +This roadmap is pretty big and not all items listed in this document might be +addressed during 2021. Because this is the first roadmap for Clippy, having open +tasks at the end of 2021 is fine, but they should be revisited in the 2022 +roadmap. diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index c579beeae89b..72935072f8cd 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-01-02" +channel = "nightly-2021-01-15" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index e490ee54c0be..f5f6c09ed8e9 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -298,7 +298,7 @@ pub fn main() { // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN // - IF `--no-deps` is not set (`!no_deps`) OR // - IF `--no-deps` is set and Clippy is run on the specified primary package - let clippy_tests_set = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true"); + let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true"); let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some(); let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok(); diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index ec3af94b9ca9..ea800336ef55 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -254,7 +254,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { fn prepare_env() { set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); - set_var("CLIPPY_TESTS", "true"); + set_var("__CLIPPY_INTERNAL_TESTS", "true"); //set_var("RUST_BACKTRACE", "0"); } diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed index c6b84d2ef650..9ab845a573ac 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed @@ -14,13 +14,16 @@ macro_rules! sym { fn main() { // Direct use of Symbol::intern - let _ = rustc_span::symbol::sym::f32; + let _ = rustc_span::sym::f32; // Using a sym macro - let _ = rustc_span::symbol::sym::f32; + let _ = rustc_span::sym::f32; // Correct suggestion when symbol isn't stringified constant name - let _ = rustc_span::symbol::sym::proc_dash_macro; + let _ = rustc_span::sym::proc_dash_macro; + + // interning a keyword + let _ = rustc_span::symbol::kw::SelfLower; // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs index 9ec82d4ad0ba..a58e182971d7 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs @@ -22,6 +22,9 @@ fn main() { // Correct suggestion when symbol isn't stringified constant name let _ = Symbol::intern("proc-macro"); + // interning a keyword + let _ = Symbol::intern("self"); + // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); let _ = sym!(xyz123); diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr index 74b906c8a579..50c1c268eb13 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.stderr @@ -2,7 +2,7 @@ error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:17:13 | LL | let _ = Symbol::intern("f32"); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32` | note: the lint level is defined here --> $DIR/interning_defined_symbol.rs:2:9 @@ -15,13 +15,19 @@ error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:20:13 | LL | let _ = sym!(f32); - | ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | ^^^^^^^^^ help: try: `rustc_span::sym::f32` error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:23:13 | LL | let _ = Symbol::intern("proc-macro"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` -error: aborting due to 3 previous errors +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:26:13 + | +LL | let _ = Symbol::intern("self"); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower` + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed new file mode 100644 index 000000000000..2ec0efe4c10a --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![feature(rustc_private)] +#![deny(clippy::internal)] +#![allow(clippy::unnecessary_operation, unused_must_use)] + +extern crate rustc_span; + +use rustc_span::symbol::{Ident, Symbol}; + +fn main() { + Symbol::intern("foo") == rustc_span::sym::clippy; + Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower; + Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper; + Ident::invalid().name == rustc_span::sym::clippy; + rustc_span::sym::clippy == Ident::invalid().name; +} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs new file mode 100644 index 000000000000..87e1b3a2ee76 --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![feature(rustc_private)] +#![deny(clippy::internal)] +#![allow(clippy::unnecessary_operation, unused_must_use)] + +extern crate rustc_span; + +use rustc_span::symbol::{Ident, Symbol}; + +fn main() { + Symbol::intern("foo").as_str() == "clippy"; + Symbol::intern("foo").to_string() == "self"; + Symbol::intern("foo").to_ident_string() != "Self"; + &*Ident::invalid().as_str() == "clippy"; + "clippy" == Ident::invalid().to_string(); +} diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr new file mode 100644 index 000000000000..b1284b7c8ffd --- /dev/null +++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr @@ -0,0 +1,39 @@ +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:11:5 + | +LL | Symbol::intern("foo").as_str() == "clippy"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` + | +note: the lint level is defined here + --> $DIR/unnecessary_symbol_str.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:12:5 + | +LL | Symbol::intern("foo").to_string() == "self"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:13:5 + | +LL | Symbol::intern("foo").to_ident_string() != "Self"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:14:5 + | +LL | &*Ident::invalid().as_str() == "clippy"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::invalid().name == rustc_span::sym::clippy` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:15:5 + | +LL | "clippy" == Ident::invalid().to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::invalid().name` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index 183248234682..d6ecd8568ce7 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -94,3 +94,19 @@ macro_rules! large_enum_variant { } }; } + +#[macro_export] +macro_rules! field_reassign_with_default { + () => { + #[derive(Default)] + struct A { + pub i: i32, + pub j: i64, + } + fn lint() { + let mut a: A = Default::default(); + a.i = 42; + a; + } + }; +} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index 7c4e4a145512..24891682d368 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -4,6 +4,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_quote)] #![allow(incomplete_features)] +#![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] extern crate proc_macro; @@ -23,3 +24,20 @@ pub fn derive(_: TokenStream) -> TokenStream { }; output } + +#[proc_macro_derive(FieldReassignWithDefault)] +pub fn derive_foo(_input: TokenStream) -> TokenStream { + quote! { + #[derive(Default)] + struct A { + pub i: i32, + pub j: i64, + } + #[automatically_derived] + fn lint() { + let mut a: A = Default::default(); + a.i = 42; + a; + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs index 4c08935639f1..d011e84b115a 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.rs +++ b/src/tools/clippy/tests/ui/cast_alignment.rs @@ -12,6 +12,10 @@ fn main() { (&1u8 as *const u8) as *const u16; (&mut 1u8 as *mut u8) as *mut u16; + // cast to more-strictly-aligned type, but with the `pointer::cast` function. + (&1u8 as *const u8).cast::(); + (&mut 1u8 as *mut u8).cast::(); + /* These should be ok */ // not a pointer type diff --git a/src/tools/clippy/tests/ui/cast_alignment.stderr b/src/tools/clippy/tests/ui/cast_alignment.stderr index 79219f86155a..7998b787b91f 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.stderr +++ b/src/tools/clippy/tests/ui/cast_alignment.stderr @@ -12,5 +12,17 @@ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:16:5 + | +LL | (&1u8 as *const u8).cast::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:17:5 + | +LL | (&mut 1u8 as *mut u8).cast::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/clone_on_copy.fixed b/src/tools/clippy/tests/ui/clone_on_copy.fixed index 1f0ca101757e..d924625132eb 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.fixed +++ b/src/tools/clippy/tests/ui/clone_on_copy.fixed @@ -5,7 +5,8 @@ clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::vec_init_then_push )] use std::cell::RefCell; diff --git a/src/tools/clippy/tests/ui/clone_on_copy.rs b/src/tools/clippy/tests/ui/clone_on_copy.rs index ca39a654b4fc..97f494672445 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.rs +++ b/src/tools/clippy/tests/ui/clone_on_copy.rs @@ -5,7 +5,8 @@ clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::vec_init_then_push )] use std::cell::RefCell; diff --git a/src/tools/clippy/tests/ui/clone_on_copy.stderr b/src/tools/clippy/tests/ui/clone_on_copy.stderr index 14a700886a7b..7a706884fb0e 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy.stderr +++ b/src/tools/clippy/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:22:5 + --> $DIR/clone_on_copy.rs:23:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -7,25 +7,25 @@ LL | 42.clone(); = note: `-D clippy::clone-on-copy` implied by `-D warnings` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:26:5 + --> $DIR/clone_on_copy.rs:27:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:29:5 + --> $DIR/clone_on_copy.rs:30:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` error: using `clone` on type `char` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:35:14 + --> $DIR/clone_on_copy.rs:36:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:39:14 + --> $DIR/clone_on_copy.rs:40:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed index ce2a1c28c8a8..fa4bc30e933a 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed @@ -3,6 +3,8 @@ #[rustfmt::skip] #[warn(clippy::collapsible_if)] +#[warn(clippy::collapsible_else_if)] + fn main() { let x = "hello"; let y = "world"; diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs index 99c40b8d38eb..bf6c1d1f894d 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs @@ -3,6 +3,8 @@ #[rustfmt::skip] #[warn(clippy::collapsible_if)] +#[warn(clippy::collapsible_else_if)] + fn main() { let x = "hello"; let y = "world"; diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.stderr b/src/tools/clippy/tests/ui/collapsible_else_if.stderr index 3d1c458879e5..ee3e11ae565d 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_else_if.stderr @@ -1,5 +1,5 @@ error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:12:12 + --> $DIR/collapsible_else_if.rs:14:12 | LL | } else { | ____________^ @@ -9,7 +9,7 @@ LL | | } LL | | } | |_____^ | - = note: `-D clippy::collapsible-if` implied by `-D warnings` + = note: `-D clippy::collapsible-else-if` implied by `-D warnings` help: collapse nested if block | LL | } else if y == "world" { @@ -18,7 +18,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:20:12 + --> $DIR/collapsible_else_if.rs:22:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:28:12 + --> $DIR/collapsible_else_if.rs:30:12 | LL | } else { | ____________^ @@ -59,7 +59,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:39:12 + --> $DIR/collapsible_else_if.rs:41:12 | LL | } else { | ____________^ @@ -82,7 +82,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:50:12 + --> $DIR/collapsible_else_if.rs:52:12 | LL | } else { | ____________^ @@ -105,7 +105,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:61:12 + --> $DIR/collapsible_else_if.rs:63:12 | LL | } else { | ____________^ @@ -128,7 +128,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:72:12 + --> $DIR/collapsible_else_if.rs:74:12 | LL | } else { | ____________^ diff --git a/src/tools/clippy/tests/ui/empty_enum.rs b/src/tools/clippy/tests/ui/empty_enum.rs index 12428f29625c..a2e5c13c4528 100644 --- a/src/tools/clippy/tests/ui/empty_enum.rs +++ b/src/tools/clippy/tests/ui/empty_enum.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] #![warn(clippy::empty_enum)] - +// Enable never type to test empty enum lint +#![feature(never_type)] enum Empty {} fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum.stderr b/src/tools/clippy/tests/ui/empty_enum.stderr index 466dfbe7cee7..7125e5f602b7 100644 --- a/src/tools/clippy/tests/ui/empty_enum.stderr +++ b/src/tools/clippy/tests/ui/empty_enum.stderr @@ -1,5 +1,5 @@ error: enum with no variants - --> $DIR/empty_enum.rs:4:1 + --> $DIR/empty_enum.rs:5:1 | LL | enum Empty {} | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs b/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs new file mode 100644 index 000000000000..386677352e29 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enum_without_never_type.rs @@ -0,0 +1,7 @@ +#![allow(dead_code)] +#![warn(clippy::empty_enum)] + +// `never_type` is not enabled; this test has no stderr file +enum Empty {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/escape_analysis.rs b/src/tools/clippy/tests/ui/escape_analysis.rs index 07004489610d..d26f48fc68f8 100644 --- a/src/tools/clippy/tests/ui/escape_analysis.rs +++ b/src/tools/clippy/tests/ui/escape_analysis.rs @@ -182,3 +182,23 @@ pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} #[rustfmt::skip] // Forces rustfmt to not add ABI pub extern fn do_not_warn_me_no_abi(_c_pointer: Box) -> () {} + +// Issue #4804 - default implementation in trait +mod issue4804 { + trait DefaultTraitImplTest { + // don't warn on `self` + fn default_impl(self: Box) -> u32 { + 5 + } + + // warn on `x: Box` + fn default_impl_x(self: Box, x: Box) -> u32 { + 4 + } + } + + trait WarnTrait { + // warn on `x: Box` + fn foo(x: Box) {} + } +} diff --git a/src/tools/clippy/tests/ui/escape_analysis.stderr b/src/tools/clippy/tests/ui/escape_analysis.stderr index c86a769a3da4..4a82b4419f99 100644 --- a/src/tools/clippy/tests/ui/escape_analysis.stderr +++ b/src/tools/clippy/tests/ui/escape_analysis.stderr @@ -12,5 +12,17 @@ error: local variable doesn't need to be boxed here LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:195:44 + | +LL | fn default_impl_x(self: Box, x: Box) -> u32 { + | ^ + +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:202:16 + | +LL | fn foo(x: Box) {} + | ^ + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs index 3e0921022b41..9fc208f5332a 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs @@ -1,5 +1,18 @@ +// aux-build:proc_macro_derive.rs +// aux-build:macro_rules.rs + #![warn(clippy::field_reassign_with_default)] +#[macro_use] +extern crate proc_macro_derive; +#[macro_use] +extern crate macro_rules; + +// Don't lint on derives that derive `Default` +// See https://github.com/rust-lang/rust-clippy/issues/6545 +#[derive(FieldReassignWithDefault)] +struct DerivedStruct; + #[derive(Default)] struct A { i: i32, @@ -11,6 +24,11 @@ struct B { j: i64, } +#[derive(Default)] +struct C { + i: Vec, + j: i64, +} /// Implements .next() that returns a different number each time. struct SideEffect(i32); @@ -111,6 +129,13 @@ fn main() { // don't lint - some private fields let mut x = m::F::default(); x.a = 1; + + // don't expand macros in the suggestion (#6522) + let mut a: C = C::default(); + a.i = vec![1]; + + // Don't lint in external macros + field_reassign_with_default!(); } mod m { diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr index 9a2bc778c3ff..2f0f28f7bb72 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr @@ -1,75 +1,87 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:30:5 + --> $DIR/field_reassign_with_default.rs:48:5 | LL | a.i = 42; | ^^^^^^^^^ | = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` -note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:29:5 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:47:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:70:5 + --> $DIR/field_reassign_with_default.rs:88:5 | LL | a.j = 43; | ^^^^^^^^^ | -note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:69:5 +note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:87:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:75:5 + --> $DIR/field_reassign_with_default.rs:93:5 | LL | a.i = 42; | ^^^^^^^^^ | -note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:74:5 +note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:92:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:81:5 + --> $DIR/field_reassign_with_default.rs:99:5 | LL | a.i = 42; | ^^^^^^^^^ | -note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:80:5 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:98:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:91:5 + --> $DIR/field_reassign_with_default.rs:109:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:90:5 +note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:108:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:95:5 + --> $DIR/field_reassign_with_default.rs:113:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:94:5 +note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:112:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:135:5 + | +LL | a.i = vec![1]; + | ^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:134:5 + | +LL | let mut a: C = C::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr index 18f56f854329..b101d2704fbd 100644 --- a/src/tools/clippy/tests/ui/from_over_into.stderr +++ b/src/tools/clippy/tests/ui/from_over_into.stderr @@ -1,12 +1,8 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true --> $DIR/from_over_into.rs:6:1 | -LL | / impl Into for String { -LL | | fn into(self) -> StringWrapper { -LL | | StringWrapper(self) -LL | | } -LL | | } - | |_^ +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::from-over-into` implied by `-D warnings` = help: consider to implement `From` instead diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs index 8d54f75b5d19..e83ce47e5630 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else2.rs +++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs @@ -1,6 +1,7 @@ #![warn(clippy::if_same_then_else)] #![allow( clippy::blacklisted_name, + clippy::collapsible_else_if, clippy::collapsible_if, clippy::ifs_same_cond, clippy::needless_return, diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.stderr b/src/tools/clippy/tests/ui/if_same_then_else2.stderr index da2be6c8aa5a..f98e30fa376f 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else2.stderr +++ b/src/tools/clippy/tests/ui/if_same_then_else2.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:20:12 + --> $DIR/if_same_then_else2.rs:21:12 | LL | } else { | ____________^ @@ -13,7 +13,7 @@ LL | | } | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else2.rs:11:13 + --> $DIR/if_same_then_else2.rs:12:13 | LL | if true { | _____________^ @@ -26,7 +26,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:34:12 + --> $DIR/if_same_then_else2.rs:35:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:32:13 + --> $DIR/if_same_then_else2.rs:33:13 | LL | if true { | _____________^ @@ -45,7 +45,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:41:12 + --> $DIR/if_same_then_else2.rs:42:12 | LL | } else { | ____________^ @@ -55,7 +55,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:39:13 + --> $DIR/if_same_then_else2.rs:40:13 | LL | if true { | _____________^ @@ -64,7 +64,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:91:12 + --> $DIR/if_same_then_else2.rs:92:12 | LL | } else { | ____________^ @@ -74,7 +74,7 @@ LL | | }; | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:89:21 + --> $DIR/if_same_then_else2.rs:90:21 | LL | let _ = if true { | _____________________^ @@ -83,7 +83,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:98:12 + --> $DIR/if_same_then_else2.rs:99:12 | LL | } else { | ____________^ @@ -93,7 +93,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:96:13 + --> $DIR/if_same_then_else2.rs:97:13 | LL | if true { | _____________^ @@ -102,7 +102,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:123:12 + --> $DIR/if_same_then_else2.rs:124:12 | LL | } else { | ____________^ @@ -112,7 +112,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:120:20 + --> $DIR/if_same_then_else2.rs:121:20 | LL | } else if true { | ____________________^ diff --git a/src/tools/clippy/tests/ui/needless_question_mark.fixed b/src/tools/clippy/tests/ui/needless_question_mark.fixed new file mode 100644 index 000000000000..70218f3f041d --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_question_mark.fixed @@ -0,0 +1,163 @@ +// run-rustfix + +#![warn(clippy::needless_question_mark)] +#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![feature(custom_inner_attributes)] + +struct TO { + magic: Option, +} + +struct TR { + magic: Result, +} + +fn simple_option_bad1(to: TO) -> Option { + // return as a statement + return to.magic; +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_option_bad2(to: TO) -> Option { + // return as an expression + return to.magic +} + +fn simple_option_bad3(to: TO) -> Option { + // block value "return" + to.magic +} + +fn simple_option_bad4(to: Option) -> Option { + // single line closure + to.and_then(|t| t.magic) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_option_bad5(to: Option) -> Option { + // closure with body + to.and_then(|t| { + t.magic + }) +} + +fn simple_result_bad1(tr: TR) -> Result { + return tr.magic; +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_result_bad2(tr: TR) -> Result { + return tr.magic +} + +fn simple_result_bad3(tr: TR) -> Result { + tr.magic +} + +fn simple_result_bad4(tr: Result) -> Result { + tr.and_then(|t| t.magic) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_result_bad5(tr: Result) -> Result { + tr.and_then(|t| { + t.magic + }) +} + +fn also_bad(tr: Result) -> Result { + if tr.is_ok() { + let t = tr.unwrap(); + return t.magic; + } + Err(false) +} + +fn false_positive_test(x: Result<(), U>) -> Result<(), T> +where + T: From, +{ + Ok(x?) +} + +fn main() {} + +mod question_mark_none { + #![clippy::msrv = "1.12.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should not be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_result { + #![clippy::msrv = "1.21.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + to.magic // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_both { + #![clippy::msrv = "1.22.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + to.magic // should be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + to.magic // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_question_mark.rs b/src/tools/clippy/tests/ui/needless_question_mark.rs new file mode 100644 index 000000000000..60ac2c8d72ea --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_question_mark.rs @@ -0,0 +1,163 @@ +// run-rustfix + +#![warn(clippy::needless_question_mark)] +#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![feature(custom_inner_attributes)] + +struct TO { + magic: Option, +} + +struct TR { + magic: Result, +} + +fn simple_option_bad1(to: TO) -> Option { + // return as a statement + return Some(to.magic?); +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_option_bad2(to: TO) -> Option { + // return as an expression + return Some(to.magic?) +} + +fn simple_option_bad3(to: TO) -> Option { + // block value "return" + Some(to.magic?) +} + +fn simple_option_bad4(to: Option) -> Option { + // single line closure + to.and_then(|t| Some(t.magic?)) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_option_bad5(to: Option) -> Option { + // closure with body + to.and_then(|t| { + Some(t.magic?) + }) +} + +fn simple_result_bad1(tr: TR) -> Result { + return Ok(tr.magic?); +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_result_bad2(tr: TR) -> Result { + return Ok(tr.magic?) +} + +fn simple_result_bad3(tr: TR) -> Result { + Ok(tr.magic?) +} + +fn simple_result_bad4(tr: Result) -> Result { + tr.and_then(|t| Ok(t.magic?)) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_result_bad5(tr: Result) -> Result { + tr.and_then(|t| { + Ok(t.magic?) + }) +} + +fn also_bad(tr: Result) -> Result { + if tr.is_ok() { + let t = tr.unwrap(); + return Ok(t.magic?); + } + Err(false) +} + +fn false_positive_test(x: Result<(), U>) -> Result<(), T> +where + T: From, +{ + Ok(x?) +} + +fn main() {} + +mod question_mark_none { + #![clippy::msrv = "1.12.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should not be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_result { + #![clippy::msrv = "1.21.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_both { + #![clippy::msrv = "1.22.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_question_mark.stderr b/src/tools/clippy/tests/ui/needless_question_mark.stderr new file mode 100644 index 000000000000..b4eb21882ece --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_question_mark.stderr @@ -0,0 +1,88 @@ +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:17:12 + | +LL | return Some(to.magic?); + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + | + = note: `-D clippy::needless-question-mark` implied by `-D warnings` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:25:12 + | +LL | return Some(to.magic?) + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:30:5 + | +LL | Some(to.magic?) + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:35:21 + | +LL | to.and_then(|t| Some(t.magic?)) + | ^^^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:44:9 + | +LL | Some(t.magic?) + | ^^^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:49:12 + | +LL | return Ok(tr.magic?); + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:56:12 + | +LL | return Ok(tr.magic?) + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:60:5 + | +LL | Ok(tr.magic?) + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:64:21 + | +LL | tr.and_then(|t| Ok(t.magic?)) + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:72:9 + | +LL | Ok(t.magic?) + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:79:16 + | +LL | return Ok(t.magic?); + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:132:9 + | +LL | Ok(to.magic?) // should be triggered + | ^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:148:9 + | +LL | Some(to.magic?) // should be triggered + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:156:9 + | +LL | Ok(to.magic?) // should be triggered + | ^^^^^^^^^^^^^ help: try: `to.magic` + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index d849e093da7b..86bfc5b4bb28 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -86,6 +86,21 @@ fn borrows_but_not_last(value: bool) -> String { } } +macro_rules! needed_return { + ($e:expr) => { + if $e > 3 { + return; + } + }; +} + +fn test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 29f2bd1852af..51061370dfe7 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -86,6 +86,21 @@ fn borrows_but_not_last(value: bool) -> String { } } +macro_rules! needed_return { + ($e:expr) => { + if $e > 3 { + return; + } + }; +} + +fn test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed new file mode 100644 index 000000000000..8346a9454f4e --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed @@ -0,0 +1,50 @@ +// run-rustfix + +#![warn(clippy::ptr_as_ptr)] +#![feature(custom_inner_attributes)] + +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast::(); + let _ = mut_ptr.cast::(); + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = (*ptr_ptr).cast::(); + } + + // Changes in mutability. Do not lint this. + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this. + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Ensure the lint doesn't produce unnecessary turbofish for inferred types. + let _: *const i32 = ptr.cast(); + let _: *mut i32 = mut_ptr.cast(); +} + +fn _msrv_1_37() { + #![clippy::msrv = "1.37"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast` was stabilized in 1.38. Do not lint this + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} + +fn _msrv_1_38() { + #![clippy::msrv = "1.38"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast::(); + let _ = mut_ptr.cast::(); +} diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs new file mode 100644 index 000000000000..b68d4bc0aaca --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs @@ -0,0 +1,50 @@ +// run-rustfix + +#![warn(clippy::ptr_as_ptr)] +#![feature(custom_inner_attributes)] + +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = *ptr_ptr as *const i32; + } + + // Changes in mutability. Do not lint this. + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this. + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Ensure the lint doesn't produce unnecessary turbofish for inferred types. + let _: *const i32 = ptr as *const _; + let _: *mut i32 = mut_ptr as _; +} + +fn _msrv_1_37() { + #![clippy::msrv = "1.37"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast` was stabilized in 1.38. Do not lint this + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} + +fn _msrv_1_38() { + #![clippy::msrv = "1.38"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr new file mode 100644 index 000000000000..854906dc111d --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr @@ -0,0 +1,46 @@ +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:10:13 + | +LL | let _ = ptr as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` + | + = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:11:13 + | +LL | let _ = mut_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:16:17 + | +LL | let _ = *ptr_ptr as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:29:25 + | +LL | let _: *const i32 = ptr as *const _; + | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:30:23 + | +LL | let _: *mut i32 = mut_ptr as _; + | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:48:13 + | +LL | let _ = ptr as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:49:13 + | +LL | let _ = mut_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed index 652b611208b7..5b96bb59c5f1 100644 --- a/src/tools/clippy/tests/ui/try_err.fixed +++ b/src/tools/clippy/tests/ui/try_err.fixed @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs index 6bd479657b70..f220d697d2cd 100644 --- a/src/tools/clippy/tests/ui/try_err.rs +++ b/src/tools/clippy/tests/ui/try_err.rs @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs index 9ad16d365094..b6a7bc5a1cc9 100644 --- a/src/tools/clippy/tests/ui/unit_arg.rs +++ b/src/tools/clippy/tests/ui/unit_arg.rs @@ -5,7 +5,8 @@ unused_variables, clippy::unused_unit, clippy::unnecessary_wraps, - clippy::or_fun_call + clippy::or_fun_call, + clippy::needless_question_mark )] use std::fmt::Debug; diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr index c3a839a9bf81..094cff8c9859 100644 --- a/src/tools/clippy/tests/ui/unit_arg.stderr +++ b/src/tools/clippy/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:30:5 + --> $DIR/unit_arg.rs:31:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:5 + --> $DIR/unit_arg.rs:34:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:34:5 + --> $DIR/unit_arg.rs:35:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:39:5 + --> $DIR/unit_arg.rs:40:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:42:5 + --> $DIR/unit_arg.rs:43:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:43:5 + --> $DIR/unit_arg.rs:44:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:47:5 + --> $DIR/unit_arg.rs:48:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL | foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:58:13 + --> $DIR/unit_arg.rs:59:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:61:5 + --> $DIR/unit_arg.rs:62:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:94:5 + --> $DIR/unit_arg.rs:95:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/vec_init_then_push.rs b/src/tools/clippy/tests/ui/vec_init_then_push.rs new file mode 100644 index 000000000000..642ce5040096 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_init_then_push.rs @@ -0,0 +1,21 @@ +#![allow(unused_variables)] +#![warn(clippy::vec_init_then_push)] + +fn main() { + let mut def_err: Vec = Default::default(); + def_err.push(0); + + let mut new_err = Vec::::new(); + new_err.push(1); + + let mut cap_err = Vec::with_capacity(2); + cap_err.push(0); + cap_err.push(1); + cap_err.push(2); + + let mut cap_ok = Vec::with_capacity(10); + cap_ok.push(0); + + new_err = Vec::new(); + new_err.push(0); +} diff --git a/src/tools/clippy/tests/ui/vec_init_then_push.stderr b/src/tools/clippy/tests/ui/vec_init_then_push.stderr new file mode 100644 index 000000000000..819ed47d0991 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_init_then_push.stderr @@ -0,0 +1,34 @@ +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:5:5 + | +LL | / let mut def_err: Vec = Default::default(); +LL | | def_err.push(0); + | |____________________^ help: consider using the `vec![]` macro: `let mut def_err: Vec = vec![..];` + | + = note: `-D clippy::vec-init-then-push` implied by `-D warnings` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:8:5 + | +LL | / let mut new_err = Vec::::new(); +LL | | new_err.push(1); + | |____________________^ help: consider using the `vec![]` macro: `let mut new_err = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:11:5 + | +LL | / let mut cap_err = Vec::with_capacity(2); +LL | | cap_err.push(0); +LL | | cap_err.push(1); +LL | | cap_err.push(2); + | |____________________^ help: consider using the `vec![]` macro: `let mut cap_err = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:19:5 + | +LL | / new_err = Vec::new(); +LL | | new_err.push(0); + | |____________________^ help: consider using the `vec![]` macro: `new_err = vec![..];` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs index 5282eba74fd1..6cfc0fcb4cae 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -94,7 +94,8 @@ mod issue6307 { trait T: Sized { fn as_i32(self) {} fn as_u32(&self) {} - fn into_i32(&self) {} + fn into_i32(self) {} + fn into_i32_ref(&self) {} fn into_u32(self) {} fn is_i32(self) {} fn is_u32(&self) {} @@ -117,7 +118,32 @@ mod issue6307 { trait U { fn as_i32(self); fn as_u32(&self); - fn into_i32(&self); + fn into_i32(self); + fn into_i32_ref(&self); + fn into_u32(self); + fn is_i32(self); + fn is_u32(&self); + fn to_i32(self); + fn to_u32(&self); + fn from_i32(self); + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_self_convention)] + fn from_cake(self); + + // test for false positives + fn as_(self); + fn into_(&self); + fn is_(self); + fn to_(self); + fn from_(self); + fn to_mut(&mut self); + } + + trait C: Copy { + fn as_i32(self); + fn as_u32(&self); + fn into_i32(self); + fn into_i32_ref(&self); fn into_u32(self); fn is_i32(self); fn is_u32(&self); diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr index 86467eb0fc73..32bd9075bd5e 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.stderr +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -79,58 +79,70 @@ LL | fn as_i32(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:97:21 + --> $DIR/wrong_self_convention.rs:98:25 | -LL | fn into_i32(&self) {} - | ^^^^^ +LL | fn into_i32_ref(&self) {} + | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:99:19 + --> $DIR/wrong_self_convention.rs:100:19 | LL | fn is_i32(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:101:19 + --> $DIR/wrong_self_convention.rs:102:19 | LL | fn to_i32(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:103:21 + --> $DIR/wrong_self_convention.rs:104:21 | LL | fn from_i32(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:118:19 + --> $DIR/wrong_self_convention.rs:119:19 | LL | fn as_i32(self); | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:120:21 + --> $DIR/wrong_self_convention.rs:122:25 | -LL | fn into_i32(&self); - | ^^^^^ +LL | fn into_i32_ref(&self); + | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:122:19 + --> $DIR/wrong_self_convention.rs:124:19 | LL | fn is_i32(self); | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:124:19 + --> $DIR/wrong_self_convention.rs:126:19 | LL | fn to_i32(self); | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:126:21 + --> $DIR/wrong_self_convention.rs:128:21 | LL | fn from_i32(self); | ^^^^ -error: aborting due to 22 previous errors +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:146:25 + | +LL | fn into_i32_ref(&self); + | ^^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:152:21 + | +LL | fn from_i32(self); + | ^^^^ + +error: aborting due to 24 previous errors diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index dcfe1bb803fb..1ab3aead966d 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -37,16 +37,7 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ // in intra-doc links (primitive impls are weird) // https://github.com/rust-lang/rust/issues/62834 is necessary to be // able to link to slices - ( - "std/io/struct.IoSlice.html", - &[ - "#method.as_mut_ptr", - "#method.sort_by_key", - "#method.make_ascii_uppercase", - "#method.make_ascii_lowercase", - "#method.get_unchecked_mut", - ], - ), + ("std/io/struct.IoSlice.html", &["#method.as_mut_ptr", "#method.sort_by_key"]), // These try to link to std::collections, but are defined in alloc // https://github.com/rust-lang/rust/issues/74481 ("std/collections/btree_map/struct.BTreeMap.html", &["#insert-and-complex-keys"]),