From 26c0f97579763363310bc32cf6d8ef4cd6f5588f Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 2 Sep 2023 21:55:44 +0200 Subject: [PATCH 01/78] [`len_without_is_empty`]: follow type alias --- clippy_lints/src/len_zero.rs | 8 ++++++++ tests/ui/len_without_is_empty.rs | 23 +++++++++++++++++++++++ tests/ui/len_without_is_empty.stderr | 8 +++++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index deba232bdd23e..c06b35ca0dabf 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -424,6 +424,14 @@ fn check_for_is_empty( item_name: Symbol, item_kind: &str, ) { + // Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to + // find the correct inherent impls. + let impl_ty = if let Some(adt) = cx.tcx.type_of(impl_ty).skip_binder().ty_adt_def() { + adt.did() + } else { + return; + }; + let is_empty = Symbol::intern("is_empty"); let is_empty = cx .tcx diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index ac6c3e06365c6..d623601110e27 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -436,4 +436,27 @@ impl DifferingErrors { } } +// Issue #11165 +pub struct Aliased1; +pub type Alias1 = Aliased1; + +impl Alias1 { + pub fn len(&self) -> usize { + todo!() + } + + pub fn is_empty(&self) -> bool { + todo!() + } +} + +pub struct Aliased2; +pub type Alias2 = Aliased2; +impl Alias2 { + pub fn len(&self) -> usize { + //~^ ERROR: type `Alias2` has a public `len` method, but no `is_empty` method + todo!() + } +} + fn main() {} diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index 1042ea2e1b389..50a8c8086b71f 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -139,5 +139,11 @@ error: struct `AsyncResultLenWithoutIsEmpty` has a public `len` method, but no ` LL | pub async fn len(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: type `Alias2` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:456:5 + | +LL | pub fn len(&self) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors From 1c27ba931b5f5fce58363a59ed7aae94cb4ea336 Mon Sep 17 00:00:00 2001 From: Michael Watzko Date: Wed, 26 Jul 2023 22:30:29 +0200 Subject: [PATCH 02/78] Stabilize the Saturating type (saturating_int_impl, gh-87920) Also stabilizes saturating_int_assign_impl, gh-92354. And also make pub fns const where the underlying saturating_* fns became const in the meantime since the Saturating type was created. --- tests/ui/arithmetic_side_effects.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index def30f5903d74..b454c29aef4db 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -10,7 +10,7 @@ arithmetic_overflow, unconditional_panic )] -#![feature(const_mut_refs, inline_const, saturating_int_impl)] +#![feature(const_mut_refs, inline_const)] #![warn(clippy::arithmetic_side_effects)] extern crate proc_macro_derive; From e34e49f7ff8a4fee6667c3ec68a76e2a55e36ad8 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 6 Aug 2023 23:45:34 +0200 Subject: [PATCH 03/78] `useless_conversion`: don't lint if ty param has multiple bounds --- clippy_lints/src/useless_conversion.rs | 97 +++++++++++++++++++++----- tests/ui/useless_conversion.fixed | 18 +++++ tests/ui/useless_conversion.rs | 18 +++++ tests/ui/useless_conversion.stderr | 20 +++--- 4 files changed, 127 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 5ac4f0aa46c10..05acf034a95df 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,15 +1,19 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths}; +use clippy_utils::{ + get_parent_expr, is_diag_trait_item, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -61,22 +65,83 @@ impl MethodOrFunction { } } -/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did` -fn into_iter_bound(cx: &LateContext<'_>, fn_did: DefId, into_iter_did: DefId, param_index: u32) -> Option { - cx.tcx - .predicates_of(fn_did) - .predicates - .iter() - .find_map(|&(ref pred, span)| { - if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() - && tr.def_id() == into_iter_did - && tr.self_ty().is_param(param_index) +/// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did`, +/// iff the `IntoIterator` bound is the only bound on the type parameter. +/// +/// This last part is important because it might be that the type the user is calling `.into_iter()` +/// on might not satisfy those other bounds and would result in compile errors: +/// ```ignore +/// pub fn foo(i: I) +/// where I: IntoIterator + ExactSizeIterator +/// ^^^^^^^^^^^^^^^^^ this extra bound stops us from suggesting to remove `.into_iter()` ... +/// { +/// assert_eq!(i.len(), 3); +/// } +/// +/// pub fn bar() { +/// foo([1, 2, 3].into_iter()); +/// ^^^^^^^^^^^^ ... here, because `[i32; 3]` is not `ExactSizeIterator` +/// } +/// ``` +fn exclusive_into_iter_bound( + cx: &LateContext<'_>, + fn_did: DefId, + into_iter_did: DefId, + param_index: u32, +) -> Option { + #[derive(Clone)] + struct ExplicitlyUsedTyParam<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + param_index: u32, + } + + impl<'a, 'tcx> TypeVisitor> for ExplicitlyUsedTyParam<'a, 'tcx> { + type BreakTy = (); + fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow { + // Ignore implicit `T: Sized` bound + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr)) = p.kind().skip_binder() + && let Some(sized_trait_did) = self.cx.tcx.lang_items().sized_trait() + && sized_trait_did == tr.def_id() { - Some(span) + return ControlFlow::Continue(()); + } + + // Ignore `::Item` projection, this use of the ty param specifically is fine + // because it's what we're already looking for + if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = p.kind().skip_binder() + && is_diag_trait_item(self.cx,proj.projection_ty.def_id, sym::IntoIterator) + { + return ControlFlow::Continue(()); + } + + p.super_visit_with(self) + } + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + if t.is_param(self.param_index) { + ControlFlow::Break(()) } else { - None + ControlFlow::Continue(()) } - }) + } + } + + let mut into_iter_span = None; + + for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { + if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() + && tr.def_id() == into_iter_did + && tr.self_ty().is_param(param_index) + { + into_iter_span = Some(*span); + } else if pred.visit_with(&mut ExplicitlyUsedTyParam { cx, param_index }).is_break() { + // Found another reference of the type parameter; conservatively assume + // that we can't remove the bound. + return None; + } + } + + into_iter_span } /// Extracts the receiver of a `.into_iter()` method call. @@ -175,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && let Some(arg_pos) = args.iter().position(|x| x.hir_id == e.hir_id) && let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos)) && let ty::Param(param) = into_iter_param.kind() - && let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index) + && let Some(span) = exclusive_into_iter_bound(cx, parent_fn_did, into_iter_did, param.index) && self.expn_depth == 0 { // Get the "innermost" `.into_iter()` call, e.g. given this expression: diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 5259195990592..13463e1782dcb 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -151,6 +151,8 @@ fn main() { let _ = s3; let s4: Foo<'a'> = Foo; let _ = vec![s4, s4, s4].into_iter(); + + issue11300::bar(); } #[allow(dead_code)] @@ -196,6 +198,22 @@ fn explicit_into_iter_fn_arg() { b(macro_generated!()); } +mod issue11300 { + pub fn foo(i: I) + where + I: IntoIterator + ExactSizeIterator, + { + assert_eq!(i.len(), 3); + } + + pub fn bar() { + // This should not trigger the lint: + // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be + // removed and is not useless. + foo([1, 2, 3].into_iter()); + } +} + #[derive(Copy, Clone)] struct Foo; diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index befb2f9a5c32c..06f3160b0f3c7 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -151,6 +151,8 @@ fn main() { let _ = Foo::<'a'>::from(s3); let s4: Foo<'a'> = Foo; let _ = vec![s4, s4, s4].into_iter().into_iter(); + + issue11300::bar(); } #[allow(dead_code)] @@ -196,6 +198,22 @@ fn explicit_into_iter_fn_arg() { b(macro_generated!()); } +mod issue11300 { + pub fn foo(i: I) + where + I: IntoIterator + ExactSizeIterator, + { + assert_eq!(i.len(), 3); + } + + pub fn bar() { + // This should not trigger the lint: + // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be + // removed and is not useless. + foo([1, 2, 3].into_iter()); + } +} + #[derive(Copy, Clone)] struct Foo; diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 28e7bb61098f7..e7466e2fbdbe7 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -119,61 +119,61 @@ LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:183:7 + --> $DIR/useless_conversion.rs:185:7 | LL | b(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:173:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:184:7 + --> $DIR/useless_conversion.rs:186:7 | LL | c(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:174:18 + --> $DIR/useless_conversion.rs:176:18 | LL | fn c(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:185:7 + --> $DIR/useless_conversion.rs:187:7 | LL | d(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:177:12 + --> $DIR/useless_conversion.rs:179:12 | LL | T: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:188:7 + --> $DIR/useless_conversion.rs:190:7 | LL | b(vec![1, 2].into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:173:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> $DIR/useless_conversion.rs:189:7 + --> $DIR/useless_conversion.rs:191:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> $DIR/useless_conversion.rs:173:13 + --> $DIR/useless_conversion.rs:175:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ From cf10690ad4e1c4df0794fcf072f65515cf8bbadd Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 7 Aug 2023 04:04:51 +0200 Subject: [PATCH 04/78] check that the receiver type satisfies bounds --- clippy_lints/src/useless_conversion.rs | 121 ++++++++++++------------- tests/ui/useless_conversion.fixed | 32 +++++++ tests/ui/useless_conversion.rs | 32 +++++++ tests/ui/useless_conversion.stderr | 26 +++++- 4 files changed, 148 insertions(+), 63 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 05acf034a95df..5afb1f76df8c6 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,21 +1,21 @@ -use std::ops::ControlFlow; - use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{ - get_parent_expr, is_diag_trait_item, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths, -}; +use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; declare_clippy_lint! { /// ### What it does @@ -66,10 +66,7 @@ impl MethodOrFunction { } /// Returns the span of the `IntoIterator` trait bound in the function pointed to by `fn_did`, -/// iff the `IntoIterator` bound is the only bound on the type parameter. -/// -/// This last part is important because it might be that the type the user is calling `.into_iter()` -/// on might not satisfy those other bounds and would result in compile errors: +/// iff all of the bounds also hold for the type of the `.into_iter()` receiver. /// ```ignore /// pub fn foo(i: I) /// where I: IntoIterator + ExactSizeIterator @@ -83,61 +80,42 @@ impl MethodOrFunction { /// ^^^^^^^^^^^^ ... here, because `[i32; 3]` is not `ExactSizeIterator` /// } /// ``` -fn exclusive_into_iter_bound( - cx: &LateContext<'_>, +fn into_iter_bound<'tcx>( + cx: &LateContext<'tcx>, fn_did: DefId, into_iter_did: DefId, + into_iter_receiver: Ty<'tcx>, param_index: u32, + node_args: GenericArgsRef<'tcx>, ) -> Option { - #[derive(Clone)] - struct ExplicitlyUsedTyParam<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - param_index: u32, - } - - impl<'a, 'tcx> TypeVisitor> for ExplicitlyUsedTyParam<'a, 'tcx> { - type BreakTy = (); - fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow { - // Ignore implicit `T: Sized` bound - if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr)) = p.kind().skip_binder() - && let Some(sized_trait_did) = self.cx.tcx.lang_items().sized_trait() - && sized_trait_did == tr.def_id() - { - return ControlFlow::Continue(()); - } - - // Ignore `::Item` projection, this use of the ty param specifically is fine - // because it's what we're already looking for - if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = p.kind().skip_binder() - && is_diag_trait_item(self.cx,proj.projection_ty.def_id, sym::IntoIterator) - { - return ControlFlow::Continue(()); - } - - p.super_visit_with(self) - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { - if t.is_param(self.param_index) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - } - } - + let param_env = cx.tcx.param_env(fn_did); let mut into_iter_span = None; for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { - if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() - && tr.def_id() == into_iter_did - && tr.self_ty().is_param(param_index) - { - into_iter_span = Some(*span); - } else if pred.visit_with(&mut ExplicitlyUsedTyParam { cx, param_index }).is_break() { - // Found another reference of the type parameter; conservatively assume - // that we can't remove the bound. - return None; + if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() { + if tr.def_id() == into_iter_did && tr.self_ty().is_param(param_index) { + into_iter_span = Some(*span); + } else { + // Substitute generics in the predicate and replace the IntoIterator type parameter with the + // `.into_iter()` receiver to see if the bound also holds for that type. + let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { + if i == param_index as usize { + GenericArg::from(into_iter_receiver) + } else { + arg + } + })); + let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate); + if !cx + .tcx + .infer_ctxt() + .build() + .predicate_must_hold_modulo_regions(&obligation) + { + return None; + } + } } } @@ -225,22 +203,41 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // `fn_sig` does not ICE. (see #11065) && cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) => { - Some((did, args, MethodOrFunction::Function)) + Some(( + did, + args, + cx.typeck_results().node_args(recv.hir_id), + MethodOrFunction::Function + )) } ExprKind::MethodCall(.., args, _) => { cx.typeck_results().type_dependent_def_id(parent.hir_id) - .map(|did| (did, args, MethodOrFunction::Method)) + .map(|did| { + return ( + did, + args, + cx.typeck_results().node_args(recv.hir_id), + MethodOrFunction::Method + ); + }) } _ => None, }; - if let Some((parent_fn_did, args, kind)) = parent_fn + if let Some((parent_fn_did, args, node_args, kind)) = parent_fn && let Some(into_iter_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator) && let sig = cx.tcx.fn_sig(parent_fn_did).skip_binder().skip_binder() && let Some(arg_pos) = args.iter().position(|x| x.hir_id == e.hir_id) && let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos)) && let ty::Param(param) = into_iter_param.kind() - && let Some(span) = exclusive_into_iter_bound(cx, parent_fn_did, into_iter_did, param.index) + && let Some(span) = into_iter_bound( + cx, + parent_fn_did, + into_iter_did, + cx.typeck_results().expr_ty(into_iter_recv), + param.index, + node_args + ) && self.expn_depth == 0 { // Get the "innermost" `.into_iter()` call, e.g. given this expression: diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 13463e1782dcb..f3504bc62b45e 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -206,11 +206,43 @@ mod issue11300 { assert_eq!(i.len(), 3); } + trait Helper {} + impl Helper for [i32; 3] {} + impl Helper for std::array::IntoIter {} + impl Helper<()> for std::array::IntoIter {} + + fn foo2(_: I) + where + I: IntoIterator + Helper, + { + } + + trait Helper2 {} + impl Helper2> for i32 {} + impl Helper2<[i32; 3]> for i32 {} + fn foo3(_: I) + where + I: IntoIterator, + i32: Helper2, + { + } + pub fn bar() { // This should not trigger the lint: // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be // removed and is not useless. foo([1, 2, 3].into_iter()); + + // This should trigger the lint, receiver type [i32; 3] also implements `Helper` + foo2::([1, 2, 3]); + + // This again should *not* lint, since X = () and I = std::array::IntoIter, + // and `[i32; 3]: Helper<()>` is not true (only `std::array::IntoIter: Helper<()>` is). + foo2::<(), _>([1, 2, 3].into_iter()); + + // This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`, + // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. + foo3([1, 2, 3]); } } diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 06f3160b0f3c7..a4f4722927fea 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -206,11 +206,43 @@ mod issue11300 { assert_eq!(i.len(), 3); } + trait Helper {} + impl Helper for [i32; 3] {} + impl Helper for std::array::IntoIter {} + impl Helper<()> for std::array::IntoIter {} + + fn foo2(_: I) + where + I: IntoIterator + Helper, + { + } + + trait Helper2 {} + impl Helper2> for i32 {} + impl Helper2<[i32; 3]> for i32 {} + fn foo3(_: I) + where + I: IntoIterator, + i32: Helper2, + { + } + pub fn bar() { // This should not trigger the lint: // `[i32, 3]` does not satisfy the `ExactSizeIterator` bound, so the into_iter call cannot be // removed and is not useless. foo([1, 2, 3].into_iter()); + + // This should trigger the lint, receiver type [i32; 3] also implements `Helper` + foo2::([1, 2, 3].into_iter()); + + // This again should *not* lint, since X = () and I = std::array::IntoIter, + // and `[i32; 3]: Helper<()>` is not true (only `std::array::IntoIter: Helper<()>` is). + foo2::<(), _>([1, 2, 3].into_iter()); + + // This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`, + // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. + foo3([1, 2, 3].into_iter()); } } diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index e7466e2fbdbe7..81a0898bdf62a 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -178,5 +178,29 @@ note: this parameter accepts any `IntoIterator`, so you don't need to call `.int LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:237:24 + | +LL | foo2::([1, 2, 3].into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:216:12 + | +LL | I: IntoIterator + Helper, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:245:14 + | +LL | foo3([1, 2, 3].into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:225:12 + | +LL | I: IntoIterator, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 26 previous errors From 18f36897ef0844dc5e5a0d995356247cbee72ff3 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Tue, 5 Sep 2023 19:08:34 +0200 Subject: [PATCH 05/78] use the correct node args for substitution --- clippy_lints/src/useless_conversion.rs | 52 +++++++++++++++----------- tests/ui/useless_conversion.fixed | 41 ++++++++++++++++++++ tests/ui/useless_conversion.rs | 41 ++++++++++++++++++++ tests/ui/useless_conversion.stderr | 26 ++++++++++++- 4 files changed, 137 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 5afb1f76df8c6..f32e7edad6cb6 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -12,7 +12,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; -use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty}; +use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -93,27 +93,35 @@ fn into_iter_bound<'tcx>( for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { if let ty::ClauseKind::Trait(tr) = pred.kind().skip_binder() { - if tr.def_id() == into_iter_did && tr.self_ty().is_param(param_index) { - into_iter_span = Some(*span); - } else { - // Substitute generics in the predicate and replace the IntoIterator type parameter with the - // `.into_iter()` receiver to see if the bound also holds for that type. - let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { - if i == param_index as usize { - GenericArg::from(into_iter_receiver) - } else { - arg + if tr.self_ty().is_param(param_index) { + if tr.def_id() == into_iter_did { + into_iter_span = Some(*span); + } else { + let tr = cx.tcx.erase_regions(tr); + if tr.has_escaping_bound_vars() { + return None; + } + + // Substitute generics in the predicate and replace the IntoIterator type parameter with the + // `.into_iter()` receiver to see if the bound also holds for that type. + let args = cx.tcx.mk_args_from_iter(node_args.iter().enumerate().map(|(i, arg)| { + if i == param_index as usize { + GenericArg::from(into_iter_receiver) + } else { + arg + } + })); + + let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate); + if !cx + .tcx + .infer_ctxt() + .build() + .predicate_must_hold_modulo_regions(&obligation) + { + return None; } - })); - let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate); - if !cx - .tcx - .infer_ctxt() - .build() - .predicate_must_hold_modulo_regions(&obligation) - { - return None; } } } @@ -216,7 +224,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { return ( did, args, - cx.typeck_results().node_args(recv.hir_id), + cx.typeck_results().node_args(parent.hir_id), MethodOrFunction::Method ); }) diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index f3504bc62b45e..ed8387b7eb2c5 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -244,6 +244,47 @@ mod issue11300 { // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. foo3([1, 2, 3]); } + + fn ice() { + struct S1; + impl S1 { + pub fn foo(&self, _: I) {} + } + + S1.foo([1, 2]); + + // ICE that occured in itertools + trait Itertools { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized; + } + impl Itertools for I { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized, + { + } + } + let v0: Vec = vec![0, 2, 4]; + let v1: Vec = vec![1, 3, 5, 7]; + v0.into_iter().interleave_shortest(v1); + + trait TraitWithLifetime<'a> {} + impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {} + + struct Helper; + impl<'a> Helper { + fn with_lt(&self, _: I) + where + I: IntoIterator + TraitWithLifetime<'a>, + { + } + } + Helper.with_lt([&1, &2].into_iter()); + } } #[derive(Copy, Clone)] diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index a4f4722927fea..991a7762fc64a 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -244,6 +244,47 @@ mod issue11300 { // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. foo3([1, 2, 3].into_iter()); } + + fn ice() { + struct S1; + impl S1 { + pub fn foo(&self, _: I) {} + } + + S1.foo([1, 2].into_iter()); + + // ICE that occured in itertools + trait Itertools { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized; + } + impl Itertools for I { + fn interleave_shortest(self, other: J) + where + J: IntoIterator, + Self: Sized, + { + } + } + let v0: Vec = vec![0, 2, 4]; + let v1: Vec = vec![1, 3, 5, 7]; + v0.into_iter().interleave_shortest(v1.into_iter()); + + trait TraitWithLifetime<'a> {} + impl<'a> TraitWithLifetime<'a> for std::array::IntoIter<&'a i32, 2> {} + + struct Helper; + impl<'a> Helper { + fn with_lt(&self, _: I) + where + I: IntoIterator + TraitWithLifetime<'a>, + { + } + } + Helper.with_lt([&1, &2].into_iter()); + } } #[derive(Copy, Clone)] diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 81a0898bdf62a..c1f8b6b4aa966 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -202,5 +202,29 @@ note: this parameter accepts any `IntoIterator`, so you don't need to call `.int LL | I: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:254:16 + | +LL | S1.foo([1, 2].into_iter()); + | ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:251:27 + | +LL | pub fn foo(&self, _: I) {} + | ^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> $DIR/useless_conversion.rs:273:44 + | +LL | v0.into_iter().interleave_shortest(v1.into_iter()); + | ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> $DIR/useless_conversion.rs:260:20 + | +LL | J: IntoIterator, + | ^^^^^^^^^^^^ + +error: aborting due to 28 previous errors From 0f74faf1584b5639a214ef794026bef2d842bfa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 31 Aug 2023 22:12:47 +0200 Subject: [PATCH 06/78] Use `Freeze` for `SourceFile.lines` --- .../src/undocumented_unsafe_blocks.rs | 84 +++++++++---------- clippy_utils/src/source.rs | 2 +- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index a1ea3a495eb6a..da8d8ed4c0f2c 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -507,20 +507,18 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - return unsafe_line.sf.lines(|lines| { - if comment_start_line.line >= unsafe_line.line { - HasSafetyComment::No - } else { - match text_has_safety_comment( - src, - &lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + return if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }); + }; } } HasSafetyComment::Maybe @@ -551,20 +549,18 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - return unsafe_line.sf.lines(|lines| { - if comment_start_line.line >= unsafe_line.line { - HasSafetyComment::No - } else { - match text_has_safety_comment( - src, - &lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + return if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }); + }; } } HasSafetyComment::Maybe @@ -614,20 +610,18 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - unsafe_line.sf.lines(|lines| { - if macro_line.line < unsafe_line.line { - match text_has_safety_comment( - src, - &lines[macro_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } - } else { - HasSafetyComment::No + if macro_line.line < unsafe_line.line { + match text_has_safety_comment( + src, + &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }) + } else { + HasSafetyComment::No + } } else { // Problem getting source text. Pretend a comment was found. HasSafetyComment::Maybe @@ -671,13 +665,11 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // Get the text from the start of function body to the unsafe block. // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } // ^-------------^ - unsafe_line.sf.lines(|lines| { - body_line.line < unsafe_line.line && text_has_safety_comment( - src, - &lines[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos, - ).is_some() - }) + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines()[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos, + ).is_some() } else { // Problem getting source text. Pretend a comment was found. true diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 03416d35ba452..600cd084c19f5 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -118,7 +118,7 @@ fn first_char_in_first_line(cx: &T, span: Span) -> Option(cx: &T, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap(); - let line_start = sf.lines(|lines| lines[line]); + let line_start = sf.lines()[line]; let line_start = sf.absolute_position(line_start); span.with_lo(line_start) } From cc8c0e00991ccf692b9b12bf887cefac99fffa04 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 8 Sep 2023 12:40:21 +0000 Subject: [PATCH 07/78] Ignore span's parents in `collect_ast_format_args`/`find_format_args` --- clippy_utils/src/macros.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 173f9841d4469..98724fcbe96a3 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -389,7 +389,9 @@ thread_local! { /// `FormatArgsCollector` pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) { AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow_mut().insert(span, format_args.clone()); + ast_format_args + .borrow_mut() + .insert(span.with_parent(None), format_args.clone()); }); } @@ -414,7 +416,7 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, if let Some(expr) = format_args_expr { AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow().get(&expr.span).map(callback); + ast_format_args.borrow().get(&expr.span.with_parent(None)).map(callback); }); } } From e88a556124189e3ee23841238252b3831b3af966 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 8 Sep 2023 22:39:20 +0000 Subject: [PATCH 08/78] Reuse rustdoc's doc comment handling in Clippy --- clippy_lints/Cargo.toml | 1 - clippy_lints/src/doc.rs | 185 +++++++++------------------ clippy_lints/src/lib.rs | 2 + tests/compile-test.rs | 4 +- tests/ui/doc/doc-fixable.fixed | 10 ++ tests/ui/doc/doc-fixable.rs | 10 ++ tests/ui/doc/doc-fixable.stderr | 13 +- tests/ui/doc/unbalanced_ticks.rs | 2 +- tests/ui/doc/unbalanced_ticks.stderr | 21 +-- tests/ui/doc_errors.rs | 16 +++ tests/ui/doc_errors.stderr | 2 +- tests/ui/needless_doc_main.rs | 9 +- tests/ui/needless_doc_main.stderr | 42 ++++-- 13 files changed, 160 insertions(+), 157 deletions(-) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index c0a9f466e7b0e..dcd9a4adcbd46 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -15,7 +15,6 @@ clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } if_chain = "1.0" itertools = "0.10.1" -pulldown-cmark = { version = "0.9", default-features = false } quine-mc_cluskey = "0.2" regex-syntax = "0.7" serde = { version = "1.0", features = ["derive"] } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index e29ab634c9790..d8f2fbcea0027 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,18 +1,16 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use clippy_utils::source::{first_line_of_span, snippet_with_applicability}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; use if_chain::if_chain; -use itertools::Itertools; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; -use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind}; -use rustc_ast::token::CommentKind; +use rustc_ast::ast::{Async, Attribute, Fn, FnRetTy, ItemKind}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; @@ -26,6 +24,9 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; use rustc_parse::parser::ForceCollect; +use rustc_resolve::rustdoc::{ + add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, DocFragment, +}; use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::edition::Edition; @@ -450,53 +451,16 @@ fn lint_for_missing_headers( } } -/// Cleanup documentation decoration. -/// -/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or -/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we -/// need to keep track of -/// the spans but this function is inspired from the later. -#[expect(clippy::cast_possible_truncation)] -#[must_use] -pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) { - // one-line comments lose their prefix - if comment_kind == CommentKind::Line { - let mut doc = doc.to_owned(); - doc.push('\n'); - let len = doc.len(); - // +3 skips the opening delimiter - return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]); - } +#[derive(Copy, Clone)] +struct Fragments<'a> { + doc: &'a str, + fragments: &'a [DocFragment], +} - let mut sizes = vec![]; - let mut contains_initial_stars = false; - for line in doc.lines() { - let offset = line.as_ptr() as usize - doc.as_ptr() as usize; - debug_assert_eq!(offset as u32 as usize, offset); - contains_initial_stars |= line.trim_start().starts_with('*'); - // +1 adds the newline, +3 skips the opening delimiter - sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32)))); - } - if !contains_initial_stars { - return (doc.to_string(), sizes); - } - // remove the initial '*'s if any - let mut no_stars = String::with_capacity(doc.len()); - for line in doc.lines() { - let mut chars = line.chars(); - for c in &mut chars { - if c.is_whitespace() { - no_stars.push(c); - } else { - no_stars.push(if c == '*' { ' ' } else { c }); - break; - } - } - no_stars.push_str(chars.as_str()); - no_stars.push('\n'); +impl Fragments<'_> { + fn span(self, cx: &LateContext<'_>, range: Range) -> Option { + source_span_for_markdown_range(cx.tcx, &self.doc, &range, &self.fragments) } - - (no_stars, sizes) } #[derive(Copy, Clone, Default)] @@ -515,27 +479,12 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ Some(("fake".into(), "fake".into())) } + let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let mut doc = String::new(); - let mut spans = vec![]; - - for attr in attrs { - if let AttrKind::DocComment(comment_kind, comment) = attr.kind { - let (comment, current_spans) = strip_doc_comment_decoration(comment.as_str(), comment_kind, attr.span); - spans.extend_from_slice(¤t_spans); - doc.push_str(&comment); - } else if attr.has_name(sym::doc) { - // ignore mix of sugared and non-sugared doc - // don't trigger the safety or errors check - return None; - } - } - - let mut current = 0; - for &mut (ref mut offset, _) in &mut spans { - let offset_copy = *offset; - *offset = current; - current += offset_copy; + for fragment in &fragments { + add_doc_fragment(&mut doc, fragment); } + doc.pop(); if doc.is_empty() { return Some(DocHeaders::default()); @@ -543,23 +492,19 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ let mut cb = fake_broken_link_callback; - let parser = - pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter(); - // Iterate over all `Events` and combine consecutive events into one - let events = parser.coalesce(|previous, current| { - let previous_range = previous.1; - let current_range = current.1; - - match (previous.0, current.0) { - (Text(previous), Text(current)) => { - let mut previous = previous.to_string(); - previous.push_str(¤t); - Ok((Text(previous.into()), previous_range)) - }, - (previous, current) => Err(((previous, previous_range), (current, current_range))), - } - }); - Some(check_doc(cx, valid_idents, events, &spans)) + // disable smart punctuation to pick up ['link'] more easily + let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION; + let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb)); + + Some(check_doc( + cx, + valid_idents, + parser.into_offset_iter(), + Fragments { + fragments: &fragments, + doc: &doc, + }, + )) } const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; @@ -568,7 +513,7 @@ fn check_doc<'a, Events: Iterator, Range, valid_idents: &FxHashSet, events: Events, - spans: &[(usize, Span)], + fragments: Fragments<'_>, ) -> DocHeaders { // true if a safety header was found let mut headers = DocHeaders::default(); @@ -579,8 +524,8 @@ fn check_doc<'a, Events: Iterator, Range, Span)> = Vec::new(); - let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1; + let mut text_to_check: Vec<(CowStr<'_>, Range)> = Vec::new(); + let mut paragraph_range = 0..0; for (event, range) in events { match event { Start(CodeBlock(ref kind)) => { @@ -613,25 +558,28 @@ fn check_doc<'a, Events: Iterator, Range { if let End(Heading(_, _, _)) = event { in_heading = false; } - if ticks_unbalanced { + if ticks_unbalanced + && let Some(span) = fragments.span(cx, paragraph_range.clone()) + { span_lint_and_help( cx, DOC_MARKDOWN, - paragraph_span, + span, "backticks are unbalanced", None, "a backtick may be missing a pair", ); } else { - for (text, span) in text_to_check { - check_text(cx, valid_idents, &text, span); + for (text, range) in text_to_check { + if let Some(span) = fragments.span(cx, range) { + check_text(cx, valid_idents, &text, span); + } } } text_to_check = Vec::new(); @@ -640,8 +588,7 @@ fn check_doc<'a, Events: Iterator, Range (), // HTML is weird, just ignore it SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { - let (begin, span) = get_current_span(spans, range.start); - paragraph_span = paragraph_span.with_hi(span.hi()); + paragraph_range.end = range.end; ticks_unbalanced |= text.contains('`') && !in_code; if Some(&text) == in_link.as_ref() || ticks_unbalanced { // Probably a link of the form `` @@ -658,19 +605,19 @@ fn check_doc<'a, Events: Iterator, Range, Range, - in_link: bool, - trimmed_text: &str, - span: Span, - range: &Range, - begin: usize, - text_len: usize, -) { - if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') { - // fix the span to only point at the text within the link - let lo = span.lo() + BytePos::from_usize(range.start - begin); +fn check_link_quotes(cx: &LateContext<'_>, trimmed_text: &str, range: Range, fragments: Fragments<'_>) { + if trimmed_text.starts_with('\'') + && trimmed_text.ends_with('\'') + && let Some(span) = fragments.span(cx, range) + { span_lint( cx, DOC_LINK_WITH_QUOTES, - span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)), + span, "possible intra-doc link using quotes instead of backticks", ); } } -fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) { - let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) { - Ok(o) => o, - Err(e) => e - 1, - }; - spans[index] -} - -fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range, fragments: Fragments<'_>) { fn has_needless_main(code: String, edition: Edition) -> bool { rustc_driver::catch_fatal_errors(|| { rustc_span::create_session_globals_then(edition, || { @@ -774,12 +706,13 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { .unwrap_or_default() } + let trailing_whitespace = text.len() - text.trim_end().len(); + // Because of the global session, we need to create a new session in a different thread with // the edition we need. let text = text.to_owned(); - if thread::spawn(move || has_needless_main(text, edition)) - .join() - .expect("thread::spawn failed") + if thread::spawn(move || has_needless_main(text, edition)).join().expect("thread::spawn failed") + && let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ab71bfbdc587e..20fdf26dc521b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -21,6 +21,7 @@ // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) +extern crate pulldown_cmark; extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; @@ -37,6 +38,7 @@ extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; extern crate rustc_parse; +extern crate rustc_resolve; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index e329a94ff6a1d..c1c3d4a868f92 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -130,7 +130,9 @@ fn base_config(test_dir: &str) -> (Config, Args) { }; let mut config = Config { - mode: Mode::Yolo { rustfix: RustfixMode::Everything }, + mode: Mode::Yolo { + rustfix: RustfixMode::Everything, + }, stderr_filters: vec![(Match::PathBackslash, b"/")], stdout_filters: vec![], output_conflict_handling: if bless { diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index f7c2f14a4824a..47b56960a00fe 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -198,6 +198,16 @@ fn pulldown_cmark_crash() {} /// [plain text][path::to::item] fn intra_doc_link() {} +/// Ignore escaped\_underscores +/// +/// \\[ +/// \\prod\_{x\\in X} p\_x +/// \\] +fn issue_2581() {} + +/// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` +fn lint_after_escaped_chars() {} + // issue #7033 - generic_const_exprs ICE struct S where [(); N.checked_next_power_of_two().unwrap()]: { diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 51961e75b84c2..4d9a4eafa5fcb 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -198,6 +198,16 @@ fn pulldown_cmark_crash() {} /// [plain text][path::to::item] fn intra_doc_link() {} +/// Ignore escaped\_underscores +/// +/// \\[ +/// \\prod\_{x\\in X} p\_x +/// \\] +fn issue_2581() {} + +/// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint +fn lint_after_escaped_chars() {} + // issue #7033 - generic_const_exprs ICE struct S where [(); N.checked_next_power_of_two().unwrap()]: { diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr index a30ded81385f1..4c9ff41d91835 100644 --- a/tests/ui/doc/doc-fixable.stderr +++ b/tests/ui/doc/doc-fixable.stderr @@ -308,5 +308,16 @@ help: try LL | /// An iterator over `mycrate::Collection`'s values. | ~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 28 previous errors +error: item in documentation is missing backticks + --> $DIR/doc-fixable.rs:208:34 + | +LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` + | ~~~~~~~~~~~~~~~~~ + +error: aborting due to 29 previous errors diff --git a/tests/ui/doc/unbalanced_ticks.rs b/tests/ui/doc/unbalanced_ticks.rs index 4a1711f79a020..6f7bab72040c8 100644 --- a/tests/ui/doc/unbalanced_ticks.rs +++ b/tests/ui/doc/unbalanced_ticks.rs @@ -48,4 +48,4 @@ fn other_markdown() {} /// /// `lol` /// pub struct Struct; /// ``` -fn iss_7421() {} +fn issue_7421() {} diff --git a/tests/ui/doc/unbalanced_ticks.stderr b/tests/ui/doc/unbalanced_ticks.stderr index 92b6f8536c88b..89ad8db3916c2 100644 --- a/tests/ui/doc/unbalanced_ticks.stderr +++ b/tests/ui/doc/unbalanced_ticks.stderr @@ -1,7 +1,8 @@ error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:7:1 + --> $DIR/unbalanced_ticks.rs:7:5 | -LL | / /// This is a doc comment with `unbalanced_tick marks and several words that +LL | /// This is a doc comment with `unbalanced_tick marks and several words that + | _____^ LL | | LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`. LL | | /// Because of the initial `unbalanced_tick` pair, the error message is @@ -13,10 +14,10 @@ LL | | /// very `confusing_and_misleading`. = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]` error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:14:1 + --> $DIR/unbalanced_ticks.rs:14:5 | LL | /// This paragraph has `unbalanced_tick marks and should stop_linting. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair @@ -32,10 +33,10 @@ LL | /// This paragraph is fine and `should_be` linted normally. | ~~~~~~~~~~~ error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:20:1 + --> $DIR/unbalanced_ticks.rs:20:5 | LL | /// Double unbalanced backtick from ``here to here` should lint. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair @@ -51,18 +52,18 @@ LL | /// ## `not_fine` | ~~~~~~~~~~ error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:37:1 + --> $DIR/unbalanced_ticks.rs:37:5 | LL | /// ### `unbalanced - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair error: backticks are unbalanced - --> $DIR/unbalanced_ticks.rs:40:1 + --> $DIR/unbalanced_ticks.rs:40:5 | LL | /// - This `item has unbalanced tick marks - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: a backtick may be missing a pair diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 86721f61d199c..d661231c59ea8 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -85,6 +85,22 @@ impl Struct1 { async fn async_priv_method_missing_errors_header() -> Result<(), ()> { unimplemented!(); } + + /** + # Errors + A description of the errors goes here. + */ + fn block_comment() -> Result<(), ()> { + unimplemented!(); + } + + /** + * # Errors + * A description of the errors goes here. + */ + fn block_comment_leading_asterisks() -> Result<(), ()> { + unimplemented!(); + } } pub trait Trait1 { diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index 9cea864f1d867..28b2db2440b7a 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -38,7 +38,7 @@ LL | pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:92:5 + --> $DIR/doc_errors.rs:108:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index d31b9047eaaeb..fee05926ce4fc 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -10,7 +10,7 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// With an explicit return type it should lint too /// ```edition2015 /// fn main() -> () { @@ -18,7 +18,7 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// This should, too. /// ```rust /// fn main() { @@ -26,11 +26,12 @@ /// unimplemented!(); /// } /// ``` -/// +/// /// This one too. /// ```no_run -/// fn main() { +/// // the fn is not always the first line //~^ ERROR: needless `fn main` in doctest +/// fn main() { /// unimplemented!(); /// } /// ``` diff --git a/tests/ui/needless_doc_main.stderr b/tests/ui/needless_doc_main.stderr index 8dd93bb05dd50..84256548671d1 100644 --- a/tests/ui/needless_doc_main.stderr +++ b/tests/ui/needless_doc_main.stderr @@ -1,29 +1,47 @@ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:7:4 + --> $DIR/needless_doc_main.rs:7:5 | -LL | /// fn main() { - | ^^^^^^^^^^^^ +LL | /// fn main() { + | _____^ +LL | | +LL | | +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ | = note: `-D clippy::needless-doctest-main` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]` error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:16:4 + --> $DIR/needless_doc_main.rs:16:5 | -LL | /// fn main() -> () { - | ^^^^^^^^^^^^^^^^^^ +LL | /// fn main() -> () { + | _____^ +LL | | +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:24:4 + --> $DIR/needless_doc_main.rs:24:5 | -LL | /// fn main() { - | ^^^^^^^^^^^^ +LL | /// fn main() { + | _____^ +LL | | +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:32:4 + --> $DIR/needless_doc_main.rs:32:5 | -LL | /// fn main() { - | ^^^^^^^^^^^^ +LL | /// // the fn is not always the first line + | _____^ +LL | | +LL | | /// fn main() { +LL | | /// unimplemented!(); +LL | | /// } + | |_____^ error: aborting due to 4 previous errors From 54f3ddb84a2bbdbb0b60f1d38ea3deae03286b30 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 10 Sep 2023 18:17:47 +0000 Subject: [PATCH 09/78] Call `LateLintPass::check_attribute` from `with_lint_attrs` --- tests/ui/allow_attributes.fixed | 7 +++++++ tests/ui/allow_attributes.rs | 7 +++++++ tests/ui/blanket_clippy_restriction_lints.stderr | 14 +++++++------- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/tests/ui/allow_attributes.fixed b/tests/ui/allow_attributes.fixed index 945ba83611c0d..b506a9890f5b9 100644 --- a/tests/ui/allow_attributes.fixed +++ b/tests/ui/allow_attributes.fixed @@ -22,6 +22,13 @@ struct T4; #[cfg_attr(panic = "unwind", expect(dead_code))] struct CfgT; +#[allow(clippy::allow_attributes, unused)] +struct Allowed; + +#[expect(clippy::allow_attributes)] +#[allow(unused)] +struct Expected; + fn ignore_external() { external! { #[allow(clippy::needless_borrow)] // Should not lint diff --git a/tests/ui/allow_attributes.rs b/tests/ui/allow_attributes.rs index 8afa61c7002c5..c7daa7abd9d41 100644 --- a/tests/ui/allow_attributes.rs +++ b/tests/ui/allow_attributes.rs @@ -22,6 +22,13 @@ struct T4; #[cfg_attr(panic = "unwind", allow(dead_code))] struct CfgT; +#[allow(clippy::allow_attributes, unused)] +struct Allowed; + +#[expect(clippy::allow_attributes)] +#[allow(unused)] +struct Expected; + fn ignore_external() { external! { #[allow(clippy::needless_borrow)] // Should not lint diff --git a/tests/ui/blanket_clippy_restriction_lints.stderr b/tests/ui/blanket_clippy_restriction_lints.stderr index d04ea7151c2a5..afb634f34b41a 100644 --- a/tests/ui/blanket_clippy_restriction_lints.stderr +++ b/tests/ui/blanket_clippy_restriction_lints.stderr @@ -1,10 +1,3 @@ -error: `clippy::restriction` is not meant to be enabled as a group - | - = note: because of the command line `--warn clippy::restriction` - = help: enable the restriction lints you need individually - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` - error: `clippy::restriction` is not meant to be enabled as a group --> $DIR/blanket_clippy_restriction_lints.rs:6:9 | @@ -12,6 +5,8 @@ LL | #![warn(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | = help: enable the restriction lints you need individually + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]` error: `clippy::restriction` is not meant to be enabled as a group --> $DIR/blanket_clippy_restriction_lints.rs:8:9 @@ -29,5 +24,10 @@ LL | #![forbid(clippy::restriction)] | = help: enable the restriction lints you need individually +error: `clippy::restriction` is not meant to be enabled as a group + | + = note: because of the command line `--warn clippy::restriction` + = help: enable the restriction lints you need individually + error: aborting due to 4 previous errors From ab08a3d7b2143154f16b82c388bb355aff6c4b4b Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 8 Sep 2023 16:14:47 +0000 Subject: [PATCH 10/78] Update tools and fulldeps tests --- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 2 +- clippy_utils/src/ast_utils.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 23d6e2a845fd6..d10f10ef87e81 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -586,7 +586,7 @@ fn ident_difference_expr_with_base_location( | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) | (While(_, _, _), While(_, _, _)) | (If(_, _, _), If(_, _, _)) - | (Let(_, _, _), Let(_, _, _)) + | (Let(_, _, _, _), Let(_, _, _, _)) | (Type(_, _), Type(_, _)) | (Cast(_, _), Cast(_, _)) | (Lit(_), Lit(_)) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 9cf5957722985..766a548145160 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -68,7 +68,7 @@ impl EarlyLintPass for UnnestedOrPatterns { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { if self.msrv.meets(msrvs::OR_PATTERNS) { - if let ast::ExprKind::Let(pat, _, _) = &e.kind { + if let ast::ExprKind::Let(pat, _, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } } diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 140cfa2194fcb..a78ff02021f25 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -166,7 +166,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), (Lit(l), Lit(r)) => l == r, (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), - (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re), + (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re), (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { From f598bb75d47c45b69b3fe2952d53fcccf870e8b3 Mon Sep 17 00:00:00 2001 From: Michael Krasnitski Date: Mon, 11 Sep 2023 21:43:27 -0400 Subject: [PATCH 11/78] Walk the left side of where bounds if the bounded type is not a generic parameter --- clippy_lints/src/extra_unused_type_parameters.rs | 7 ++++++- tests/ui/extra_unused_type_parameters.fixed | 15 +++++++++++++++ tests/ui/extra_unused_type_parameters.rs | 15 +++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index c18006a71c26e..0a885984abbf2 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -246,8 +246,13 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { { self.ty_params.remove(&def_id); } + } else { + // If the bounded type isn't a generic param, but is instead a concrete generic + // type, any params we find nested inside of it are being used as concrete types, + // and can therefore can be considered used. So, we're fine to walk the left-hand + // side of the where bound. + walk_ty(self, predicate.bounded_ty); } - // Only walk the right-hand side of where bounds for bound in predicate.bounds { walk_param_bound(self, bound); } diff --git a/tests/ui/extra_unused_type_parameters.fixed b/tests/ui/extra_unused_type_parameters.fixed index a4943344a11ef..c9bebabdf17ae 100644 --- a/tests/ui/extra_unused_type_parameters.fixed +++ b/tests/ui/extra_unused_type_parameters.fixed @@ -113,4 +113,19 @@ with_span!( } ); +mod issue11302 { + use std::fmt::Debug; + use std::marker::PhantomData; + + #[derive(Debug)] + struct Wrapper(PhantomData); + + fn store(v: &mut Vec>) + where + Wrapper: Debug, + { + v.push(Box::new(Wrapper(PhantomData))); + } +} + fn main() {} diff --git a/tests/ui/extra_unused_type_parameters.rs b/tests/ui/extra_unused_type_parameters.rs index 6d85b1ce9d4f3..1bc0047adf01f 100644 --- a/tests/ui/extra_unused_type_parameters.rs +++ b/tests/ui/extra_unused_type_parameters.rs @@ -113,4 +113,19 @@ with_span!( } ); +mod issue11302 { + use std::fmt::Debug; + use std::marker::PhantomData; + + #[derive(Debug)] + struct Wrapper(PhantomData); + + fn store(v: &mut Vec>) + where + Wrapper: Debug, + { + v.push(Box::new(Wrapper(PhantomData))); + } +} + fn main() {} From f136e1634a8d834d32deb89273717227eedaf153 Mon Sep 17 00:00:00 2001 From: jonboh Date: Sat, 26 Aug 2023 16:26:09 +0200 Subject: [PATCH 12/78] new unnecessary_map_on_constructor lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/unnecessary_map_on_constructor.rs | 93 +++++++++++++++++++ tests/ui/eta.fixed | 3 +- tests/ui/eta.rs | 3 +- tests/ui/eta.stderr | 54 +++++------ tests/ui/manual_map_option.fixed | 1 + tests/ui/manual_map_option.rs | 1 + tests/ui/manual_map_option.stderr | 42 ++++----- tests/ui/option_filter_map.fixed | 2 +- tests/ui/option_filter_map.rs | 2 +- tests/ui/result_map_unit_fn_unfixable.rs | 2 +- tests/ui/unnecessary_map_on_constructor.fixed | 56 +++++++++++ tests/ui/unnecessary_map_on_constructor.rs | 56 +++++++++++ .../ui/unnecessary_map_on_constructor.stderr | 53 +++++++++++ 16 files changed, 319 insertions(+), 53 deletions(-) create mode 100644 clippy_lints/src/unnecessary_map_on_constructor.rs create mode 100644 tests/ui/unnecessary_map_on_constructor.fixed create mode 100644 tests/ui/unnecessary_map_on_constructor.rs create mode 100644 tests/ui/unnecessary_map_on_constructor.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ae4a0ee2c09..9aca13a6cf642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5437,6 +5437,7 @@ Released 2018-09-13 [`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap +[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a4d40df52e7bc..ecf44e05920ae 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -671,6 +671,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO, crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO, crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO, + crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 440940a44780a..f5035dcfeeb6a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -329,6 +329,7 @@ mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; mod unnecessary_box_returns; +mod unnecessary_map_on_constructor; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_struct_initialization; @@ -1102,6 +1103,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)); store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)); + store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unnecessary_map_on_constructor.rs b/clippy_lints/src/unnecessary_map_on_constructor.rs new file mode 100644 index 0000000000000..5aa057580e9d9 --- /dev/null +++ b/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -0,0 +1,93 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::get_type_diagnostic_name; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Suggest removing the use of a may (or map_err) method when an Option or Result is being construted. + /// + /// ### Why is this bad? + /// It introduces unnecessary complexity. In this case the function can be used directly and + /// construct the Option or Result from the output. + /// + /// ### Example + /// ```rust + /// Some(4).map(i32::swap_bytes); + /// ``` + /// Use instead: + /// ```rust + /// Some(i32::swap_bytes(4)); + /// ``` + #[clippy::version = "1.73.0"] + pub UNNECESSARY_MAP_ON_CONSTRUCTOR, + complexity, + "using `map`/`map_err` on `Option` or `Result` constructors" +} +declare_lint_pass!(UnnecessaryMapOnConstructor => [UNNECESSARY_MAP_ON_CONSTRUCTOR]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if expr.span.from_expansion() { + return; + } + if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind + && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)){ + let (constructor_path, constructor_item) = + if let hir::ExprKind::Call(constructor, constructor_args) = recv.kind + && let hir::ExprKind::Path(constructor_path) = constructor.kind + && let Some(arg) = constructor_args.get(0) + { + if constructor.span.from_expansion() || arg.span.from_expansion() { + return; + } + (constructor_path, arg) + } else { + return; + }; + let constructor_symbol = match constructor_path { + hir::QPath::Resolved(_, path) => { + if let Some(path_segment) = path.segments.last() { + path_segment.ident.name + } else { + return; + } + }, + hir::QPath::TypeRelative(_, path) => path.ident.name, + hir::QPath::LangItem(_, _, _) => return, + }; + match constructor_symbol { + sym::Some | sym::Ok if path.ident.name == rustc_span::sym::map => (), + sym::Err if path.ident.name == sym!(map_err) => (), + _ => return, + } + + if let Some(map_arg) = args.get(0) + && let hir::ExprKind::Path(fun) = map_arg.kind + { + if map_arg.span.from_expansion() { + return; + } + let mut applicability = Applicability::MachineApplicable; + let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut applicability); + let constructor_snippet = + snippet_with_applicability(cx, constructor_path.span(), "_", &mut applicability); + let constructor_arg_snippet = + snippet_with_applicability(cx, constructor_item.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + UNNECESSARY_MAP_ON_CONSTRUCTOR, + expr.span, + &format!("unnecessary {} on constructor {constructor_snippet}(_)", path.ident.name), + "try", + format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"), + applicability, + ); + } + } + } +} diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index f1cac8c5fbc63..32c7499bf73f6 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -7,7 +7,8 @@ clippy::option_map_unit_fn, clippy::redundant_closure_call, clippy::uninlined_format_args, - clippy::useless_vec + clippy::useless_vec, + clippy::unnecessary_map_on_constructor )] use std::path::{Path, PathBuf}; diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index c7a470b5be6df..25b7431ba8cd7 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -7,7 +7,8 @@ clippy::option_map_unit_fn, clippy::redundant_closure_call, clippy::uninlined_format_args, - clippy::useless_vec + clippy::useless_vec, + clippy::unnecessary_map_on_constructor )] use std::path::{Path, PathBuf}; diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 7c7f179746205..951e4ac749c25 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure - --> $DIR/eta.rs:28:27 + --> $DIR/eta.rs:29:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` @@ -8,31 +8,31 @@ LL | let a = Some(1u8).map(|a| foo(a)); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` error: redundant closure - --> $DIR/eta.rs:32:40 + --> $DIR/eta.rs:33:40 | LL | let _: Option> = true.then(|| vec![]); // special case vec! | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: redundant closure - --> $DIR/eta.rs:33:35 + --> $DIR/eta.rs:34:35 | LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` error: redundant closure - --> $DIR/eta.rs:34:26 + --> $DIR/eta.rs:35:26 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` error: redundant closure - --> $DIR/eta.rs:41:27 + --> $DIR/eta.rs:42:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> $DIR/eta.rs:93:51 + --> $DIR/eta.rs:94:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,127 +41,127 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]` error: redundant closure - --> $DIR/eta.rs:94:51 + --> $DIR/eta.rs:95:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> $DIR/eta.rs:96:42 + --> $DIR/eta.rs:97:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> $DIR/eta.rs:100:29 + --> $DIR/eta.rs:101:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> $DIR/eta.rs:101:27 + --> $DIR/eta.rs:102:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> $DIR/eta.rs:103:65 + --> $DIR/eta.rs:104:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> $DIR/eta.rs:166:22 + --> $DIR/eta.rs:167:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> $DIR/eta.rs:173:27 + --> $DIR/eta.rs:174:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> $DIR/eta.rs:178:27 + --> $DIR/eta.rs:179:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> $DIR/eta.rs:210:28 + --> $DIR/eta.rs:211:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:211:28 + --> $DIR/eta.rs:212:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:212:28 + --> $DIR/eta.rs:213:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> $DIR/eta.rs:219:21 + --> $DIR/eta.rs:220:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> $DIR/eta.rs:223:21 + --> $DIR/eta.rs:224:21 | LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` error: redundant closure - --> $DIR/eta.rs:316:18 + --> $DIR/eta.rs:317:18 | LL | takes_fn_mut(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> $DIR/eta.rs:319:19 + --> $DIR/eta.rs:320:19 | LL | takes_fn_once(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> $DIR/eta.rs:323:26 + --> $DIR/eta.rs:324:26 | LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` error: redundant closure - --> $DIR/eta.rs:335:19 + --> $DIR/eta.rs:336:19 | LL | array_opt.map(|a| a.as_slice()); | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` error: redundant closure - --> $DIR/eta.rs:338:19 + --> $DIR/eta.rs:339:19 | LL | slice_opt.map(|s| s.len()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` error: redundant closure - --> $DIR/eta.rs:341:17 + --> $DIR/eta.rs:342:17 | LL | ptr_opt.map(|p| p.is_null()); | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` error: redundant closure - --> $DIR/eta.rs:345:17 + --> $DIR/eta.rs:346:17 | LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` error: redundant closure - --> $DIR/eta.rs:388:19 + --> $DIR/eta.rs:389:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index f6a964da418a2..16cee3fd38236 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -5,6 +5,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::unnecessary_map_on_constructor, for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index df9dc256d3038..4655acf1406c7 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -5,6 +5,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::unnecessary_map_on_constructor, for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index ff6ed974d4a12..3754a982cb9bc 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:13:5 + --> $DIR/manual_map_option.rs:14:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -11,7 +11,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::manual_map)]` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:18:5 + --> $DIR/manual_map_option.rs:19:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -20,7 +20,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:23:5 + --> $DIR/manual_map_option.rs:24:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -29,7 +29,7 @@ LL | | }; | |_____^ help: try: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:28:5 + --> $DIR/manual_map_option.rs:29:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -39,7 +39,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:35:5 + --> $DIR/manual_map_option.rs:36:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -48,7 +48,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:40:5 + --> $DIR/manual_map_option.rs:41:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -57,7 +57,7 @@ LL | | }; | |_____^ help: try: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:50:5 + --> $DIR/manual_map_option.rs:51:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -66,7 +66,7 @@ LL | | }; | |_____^ help: try: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:55:5 + --> $DIR/manual_map_option.rs:56:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -75,7 +75,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:60:5 + --> $DIR/manual_map_option.rs:61:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -84,7 +84,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:65:5 + --> $DIR/manual_map_option.rs:66:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -93,7 +93,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:70:5 + --> $DIR/manual_map_option.rs:71:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -102,7 +102,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:83:9 + --> $DIR/manual_map_option.rs:84:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -111,7 +111,7 @@ LL | | }; | |_________^ help: try: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:89:5 + --> $DIR/manual_map_option.rs:90:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:94:5 + --> $DIR/manual_map_option.rs:95:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -129,7 +129,7 @@ LL | | }; | |_____^ help: try: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:99:5 + --> $DIR/manual_map_option.rs:100:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -138,7 +138,7 @@ LL | | }; | |_____^ help: try: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:104:5 + --> $DIR/manual_map_option.rs:105:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -147,7 +147,7 @@ LL | | }; | |_____^ help: try: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:109:5 + --> $DIR/manual_map_option.rs:110:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), @@ -156,7 +156,7 @@ LL | | }; | |_____^ help: try: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:167:5 + --> $DIR/manual_map_option.rs:168:5 | LL | / match Some(0) { LL | | Some(x) => Some(vec![x]), @@ -165,7 +165,7 @@ LL | | }; | |_____^ help: try: `Some(0).map(|x| vec![x])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:172:5 + --> $DIR/manual_map_option.rs:173:5 | LL | / match option_env!("") { LL | | Some(x) => Some(String::from(x)), @@ -174,7 +174,7 @@ LL | | }; | |_____^ help: try: `option_env!("").map(String::from)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:192:12 + --> $DIR/manual_map_option.rs:193:12 | LL | } else if let Some(x) = Some(0) { | ____________^ @@ -185,7 +185,7 @@ LL | | }; | |_____^ help: try: `{ Some(0).map(|x| x + 1) }` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:200:12 + --> $DIR/manual_map_option.rs:201:12 | LL | } else if let Some(x) = Some(0) { | ____________^ diff --git a/tests/ui/option_filter_map.fixed b/tests/ui/option_filter_map.fixed index d4c04ff907b2a..ee004c0e194bb 100644 --- a/tests/ui/option_filter_map.fixed +++ b/tests/ui/option_filter_map.fixed @@ -1,5 +1,5 @@ #![warn(clippy::option_filter_map)] -#![allow(clippy::map_flatten)] +#![allow(clippy::map_flatten, clippy::unnecessary_map_on_constructor)] fn main() { let _ = Some(Some(1)).flatten(); diff --git a/tests/ui/option_filter_map.rs b/tests/ui/option_filter_map.rs index 99fb4723cab7a..eae2fa176a890 100644 --- a/tests/ui/option_filter_map.rs +++ b/tests/ui/option_filter_map.rs @@ -1,5 +1,5 @@ #![warn(clippy::option_filter_map)] -#![allow(clippy::map_flatten)] +#![allow(clippy::map_flatten, clippy::unnecessary_map_on_constructor)] fn main() { let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap); diff --git a/tests/ui/result_map_unit_fn_unfixable.rs b/tests/ui/result_map_unit_fn_unfixable.rs index a4dfc8f293d3f..62798b6d3d6f3 100644 --- a/tests/ui/result_map_unit_fn_unfixable.rs +++ b/tests/ui/result_map_unit_fn_unfixable.rs @@ -1,6 +1,6 @@ #![warn(clippy::result_map_unit_fn)] #![feature(never_type)] -#![allow(unused)] +#![allow(unused, clippy::unnecessary_map_on_constructor)] //@no-rustfix struct HasResult { field: Result, diff --git a/tests/ui/unnecessary_map_on_constructor.fixed b/tests/ui/unnecessary_map_on_constructor.fixed new file mode 100644 index 0000000000000..d0ba7ed749e4b --- /dev/null +++ b/tests/ui/unnecessary_map_on_constructor.fixed @@ -0,0 +1,56 @@ +#![allow(unused)] +#![warn(clippy::unnecessary_map_on_constructor)] + +use std::ffi::OsStr; + +fn fun(t: i32) -> i32 { + t +} + +fn notfun(e: SimpleError) -> SimpleError { + e +} +macro_rules! expands_to_fun { + () => { + fun + }; +} + +#[derive(Copy, Clone)] +struct SimpleError {} + +type SimpleResult = std::result::Result; + +fn main() { + let x: i32 = 4; + + let err = SimpleError {}; + let a = Some(x); + let b: SimpleResult = Ok(x); + let c: SimpleResult = Err(err); + + let a = Some(fun(x)); + let b: SimpleResult = Ok(fun(x)); + let c: SimpleResult = Err(notfun(err)); + + let a = Option::Some(fun(x)); + let b: SimpleResult = SimpleResult::Ok(fun(x)); + let c: SimpleResult = SimpleResult::Err(notfun(err)); + let b: std::result::Result = Ok(fun(x)); + let c: std::result::Result = Err(notfun(err)); + + let a = Some(fun(x)); + let b: SimpleResult = Ok(fun(x)); + let c: SimpleResult = Err(notfun(err)); + + // Should not trigger warning + a.map(fun); + b.map(fun); + c.map_err(notfun); + + b.map_err(notfun); // Ok(_).map_err + c.map(fun); // Err(_).map() + + option_env!("PATH").map(OsStr::new); + Some(x).map(expands_to_fun!()); +} diff --git a/tests/ui/unnecessary_map_on_constructor.rs b/tests/ui/unnecessary_map_on_constructor.rs new file mode 100644 index 0000000000000..e89e7aad4c40e --- /dev/null +++ b/tests/ui/unnecessary_map_on_constructor.rs @@ -0,0 +1,56 @@ +#![allow(unused)] +#![warn(clippy::unnecessary_map_on_constructor)] + +use std::ffi::OsStr; + +fn fun(t: i32) -> i32 { + t +} + +fn notfun(e: SimpleError) -> SimpleError { + e +} +macro_rules! expands_to_fun { + () => { + fun + }; +} + +#[derive(Copy, Clone)] +struct SimpleError {} + +type SimpleResult = std::result::Result; + +fn main() { + let x: i32 = 4; + + let err = SimpleError {}; + let a = Some(x); + let b: SimpleResult = Ok(x); + let c: SimpleResult = Err(err); + + let a = Some(x).map(fun); + let b: SimpleResult = Ok(x).map(fun); + let c: SimpleResult = Err(err).map_err(notfun); + + let a = Option::Some(x).map(fun); + let b: SimpleResult = SimpleResult::Ok(x).map(fun); + let c: SimpleResult = SimpleResult::Err(err).map_err(notfun); + let b: std::result::Result = Ok(x).map(fun); + let c: std::result::Result = Err(err).map_err(notfun); + + let a = Some(fun(x)); + let b: SimpleResult = Ok(fun(x)); + let c: SimpleResult = Err(notfun(err)); + + // Should not trigger warning + a.map(fun); + b.map(fun); + c.map_err(notfun); + + b.map_err(notfun); // Ok(_).map_err + c.map(fun); // Err(_).map() + + option_env!("PATH").map(OsStr::new); + Some(x).map(expands_to_fun!()); +} diff --git a/tests/ui/unnecessary_map_on_constructor.stderr b/tests/ui/unnecessary_map_on_constructor.stderr new file mode 100644 index 0000000000000..d522b68d8726a --- /dev/null +++ b/tests/ui/unnecessary_map_on_constructor.stderr @@ -0,0 +1,53 @@ +error: unnecessary map on constructor Some(_) + --> $DIR/unnecessary_map_on_constructor.rs:32:13 + | +LL | let a = Some(x).map(fun); + | ^^^^^^^^^^^^^^^^ help: try: `Some(fun(x))` + | + = note: `-D clippy::unnecessary-map-on-constructor` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_on_constructor)]` + +error: unnecessary map on constructor Ok(_) + --> $DIR/unnecessary_map_on_constructor.rs:33:27 + | +LL | let b: SimpleResult = Ok(x).map(fun); + | ^^^^^^^^^^^^^^ help: try: `Ok(fun(x))` + +error: unnecessary map_err on constructor Err(_) + --> $DIR/unnecessary_map_on_constructor.rs:34:27 + | +LL | let c: SimpleResult = Err(err).map_err(notfun); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Err(notfun(err))` + +error: unnecessary map on constructor Option::Some(_) + --> $DIR/unnecessary_map_on_constructor.rs:36:13 + | +LL | let a = Option::Some(x).map(fun); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option::Some(fun(x))` + +error: unnecessary map on constructor SimpleResult::Ok(_) + --> $DIR/unnecessary_map_on_constructor.rs:37:27 + | +LL | let b: SimpleResult = SimpleResult::Ok(x).map(fun); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `SimpleResult::Ok(fun(x))` + +error: unnecessary map_err on constructor SimpleResult::Err(_) + --> $DIR/unnecessary_map_on_constructor.rs:38:27 + | +LL | let c: SimpleResult = SimpleResult::Err(err).map_err(notfun); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `SimpleResult::Err(notfun(err))` + +error: unnecessary map on constructor Ok(_) + --> $DIR/unnecessary_map_on_constructor.rs:39:52 + | +LL | let b: std::result::Result = Ok(x).map(fun); + | ^^^^^^^^^^^^^^ help: try: `Ok(fun(x))` + +error: unnecessary map_err on constructor Err(_) + --> $DIR/unnecessary_map_on_constructor.rs:40:52 + | +LL | let c: std::result::Result = Err(err).map_err(notfun); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Err(notfun(err))` + +error: aborting due to 8 previous errors + From 471469d30facd25ed3072524694e369aeb72082c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 12 Sep 2023 18:13:53 +0200 Subject: [PATCH 13/78] Merge commit '98363cbf6a7c3f8b571a7d92a3c645bb4376e4a6' into clippyup --- .github/driver.sh | 2 +- .github/workflows/clippy_bors.yml | 33 +- CHANGELOG.md | 5 + Cargo.toml | 2 +- book/src/SUMMARY.md | 3 + book/src/development/emitting_lints.md | 217 ++++++++ book/src/development/trait_checking.md | 105 ++++ book/src/development/writing_tests.md | 218 ++++++++ book/src/lint_configuration.md | 24 + clippy_lints/src/declared_lints.rs | 6 +- .../src/default_union_representation.rs | 3 + clippy_lints/src/dereference.rs | 31 +- clippy_lints/src/derivable_impls.rs | 3 +- clippy_lints/src/disallowed_macros.rs | 11 +- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/if_then_some_else_none.rs | 18 +- clippy_lints/src/ignored_unit_patterns.rs | 13 +- clippy_lints/src/implied_bounds_in_impls.rs | 242 ++++++--- clippy_lints/src/lib.rs | 9 +- clippy_lints/src/loops/explicit_iter_loop.rs | 17 +- clippy_lints/src/loops/mod.rs | 10 +- clippy_lints/src/loops/never_loop.rs | 268 +++++----- clippy_lints/src/manual_range_patterns.rs | 129 +++-- .../src/methods/iter_out_of_bounds.rs | 106 ++++ .../src/methods/iter_overeager_cloned.rs | 2 +- clippy_lints/src/methods/mod.rs | 58 ++- .../src/missing_asserts_for_indexing.rs | 391 +++++++++++++++ ...orrect_impls.rs => non_canonical_impls.rs} | 45 +- .../src/operators/arithmetic_side_effects.rs | 16 +- clippy_lints/src/operators/float_cmp.rs | 2 +- clippy_lints/src/raw_strings.rs | 89 +++- clippy_lints/src/renamed_lints.rs | 4 +- .../src/slow_vector_initialization.rs | 12 +- clippy_lints/src/std_instead_of_core.rs | 26 +- .../src/undocumented_unsafe_blocks.rs | 107 ++-- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/unwrap.rs | 111 ++++- clippy_lints/src/utils/conf.rs | 20 + clippy_utils/src/lib.rs | 4 +- clippy_utils/src/paths.rs | 2 + clippy_utils/src/source.rs | 2 +- clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/ty.rs | 7 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- clippy_utils/src/usage.rs | 13 +- rust-toolchain | 2 +- tests/compile-test.rs | 45 +- .../cargo_common_metadata/fail/Cargo.stderr | 1 + .../fail_publish/Cargo.stderr | 1 + .../fail_publish_true/Cargo.stderr | 1 + .../ui-cargo/duplicate_mod/fail/Cargo.stderr | 1 + tests/ui-cargo/feature_name/fail/Cargo.stderr | 2 + .../module_style/fail_mod/Cargo.stderr | 1 + .../module_style/fail_mod_remap/Cargo.stderr | 1 + .../module_style/fail_no_mod/Cargo.stderr | 1 + .../multiple_crate_versions/fail/Cargo.stderr | 1 + .../wildcard_dependencies/fail/Cargo.stderr | 1 + tests/ui-internal/check_formulation.stderr | 1 + tests/ui-internal/if_chain_style.stderr | 1 + tests/ui-internal/invalid_paths.stderr | 1 + ...unnecessary_def_path_hardcoded_path.stderr | 1 + .../disallowed_macros/auxiliary/macros.rs | 15 + tests/ui-toml/disallowed_macros/clippy.toml | 2 + .../disallowed_macros/disallowed_macros.rs | 5 +- .../disallowed_macros.stderr | 22 +- .../auxiliary/proc_macros.rs | 469 ------------------ .../excessive_nesting/excessive_nesting.rs | 4 +- .../toml_unknown_key/conf_unknown_key.stderr | 2 + .../auxiliary/proc_macro_unsafe.rs | 13 - .../undocumented_unsafe_blocks.rs | 16 +- tests/ui/bool_comparison.fixed | 2 +- tests/ui/bool_comparison.rs | 2 +- ...ze_32bit.stderr => cast_size.32bit.stderr} | 56 ++- ...ast_size.stderr => cast_size.64bit.stderr} | 34 +- tests/ui/cast_size.rs | 31 +- tests/ui/cast_size_32bit.rs | 56 --- .../ui/checked_unwrap/simple_conditionals.rs | 51 ++ .../checked_unwrap/simple_conditionals.stderr | 70 ++- tests/ui/clone_on_copy_impl.rs | 2 +- tests/ui/crashes/ice-11337.rs | 9 + tests/ui/crashes/ice-11422.fixed | 25 + tests/ui/crashes/ice-11422.rs | 25 + tests/ui/crashes/ice-11422.stderr | 16 + tests/ui/crashes/ice-360.rs | 3 +- tests/ui/crashes/ice-360.stderr | 20 +- tests/ui/derivable_impls.fixed | 13 + tests/ui/derivable_impls.rs | 13 + tests/ui/derive.rs | 6 +- tests/ui/derive.stderr | 20 +- tests/ui/derive_ord_xor_partial_ord.rs | 2 +- tests/ui/empty_loop.rs | 3 + tests/ui/empty_loop.stderr | 4 +- tests/ui/enum_clike_unportable_variant.rs | 2 +- tests/ui/enum_clike_unportable_variant.stderr | 17 +- tests/ui/explicit_auto_deref.fixed | 30 +- tests/ui/explicit_auto_deref.rs | 28 ++ tests/ui/explicit_auto_deref.stderr | 12 +- tests/ui/explicit_iter_loop.fixed | 7 +- tests/ui/explicit_iter_loop.rs | 1 + tests/ui/explicit_iter_loop.stderr | 65 +-- tests/ui/float_cmp.rs | 13 + tests/ui/float_cmp.stderr | 12 +- ...stderr => fn_to_numeric_cast.32bit.stderr} | 62 +-- ...stderr => fn_to_numeric_cast.64bit.stderr} | 44 +- tests/ui/fn_to_numeric_cast.rs | 27 +- tests/ui/fn_to_numeric_cast_32bit.rs | 80 --- tests/ui/if_then_some_else_none.rs | 12 + tests/ui/ignored_unit_patterns.fixed | 16 +- tests/ui/ignored_unit_patterns.rs | 16 +- tests/ui/ignored_unit_patterns.stderr | 10 +- tests/ui/implied_bounds_in_impls.fixed | 57 +++ tests/ui/implied_bounds_in_impls.rs | 57 +++ tests/ui/implied_bounds_in_impls.stderr | 74 ++- tests/ui/iter_out_of_bounds.rs | 71 +++ tests/ui/iter_out_of_bounds.stderr | 119 +++++ tests/ui/iter_overeager_cloned.fixed | 6 +- tests/ui/iter_overeager_cloned.rs | 2 - tests/ui/iter_overeager_cloned.stderr | 18 +- tests/ui/iter_skip_next.fixed | 1 + tests/ui/iter_skip_next.rs | 1 + tests/ui/iter_skip_next.stderr | 14 +- tests/ui/iter_skip_next_unfixable.rs | 2 +- tests/ui/iter_skip_zero.fixed | 2 +- tests/ui/iter_skip_zero.rs | 2 +- tests/ui/large_enum_variant.32bit.stderr | 280 +++++++++++ ...stderr => large_enum_variant.64bit.stderr} | 44 +- tests/ui/large_enum_variant.rs | 1 + tests/ui/manual_range_patterns.fixed | 17 +- tests/ui/manual_range_patterns.rs | 11 + tests/ui/manual_range_patterns.stderr | 66 ++- tests/ui/missing_asserts_for_indexing.fixed | 121 +++++ tests/ui/missing_asserts_for_indexing.rs | 121 +++++ tests/ui/missing_asserts_for_indexing.stderr | 253 ++++++++++ .../missing_asserts_for_indexing_unfixable.rs | 49 ++ ...sing_asserts_for_indexing_unfixable.stderr | 164 ++++++ tests/ui/needless_borrow.fixed | 13 + tests/ui/needless_borrow.rs | 13 + tests/ui/needless_raw_string.fixed | 13 +- tests/ui/needless_raw_string.rs | 13 +- tests/ui/needless_raw_string.stderr | 48 +- tests/ui/needless_raw_string_hashes.fixed | 19 +- tests/ui/needless_raw_string_hashes.rs | 19 +- tests/ui/needless_raw_string_hashes.stderr | 144 +++++- tests/ui/never_loop.rs | 55 +- tests/ui/never_loop.stderr | 27 +- ...e.fixed => non_canonical_clone_impl.fixed} | 0 ...py_type.rs => non_canonical_clone_impl.rs} | 0 ...stderr => non_canonical_clone_impl.stderr} | 19 +- ...d => non_canonical_partial_ord_impl.fixed} | 0 ...e.rs => non_canonical_partial_ord_impl.rs} | 0 ... => non_canonical_partial_ord_impl.stderr} | 11 +- ..._canonical_partial_ord_impl_fully_qual.rs} | 3 - ...onical_partial_ord_impl_fully_qual.stderr} | 14 +- tests/ui/rename.fixed | 8 +- tests/ui/rename.rs | 8 +- tests/ui/rename.stderr | 132 ++--- tests/ui/result_large_err.rs | 2 + tests/ui/result_large_err.stderr | 24 +- tests/ui/similar_names.rs | 1 + tests/ui/similar_names.stderr | 28 +- tests/ui/single_call_fn.rs | 1 + tests/ui/single_call_fn.stderr | 16 +- tests/ui/slow_vector_initialization.rs | 16 +- tests/ui/slow_vector_initialization.stderr | 12 +- tests/ui/std_instead_of_core.fixed | 62 +++ tests/ui/std_instead_of_core.rs | 1 - tests/ui/std_instead_of_core.stderr | 71 +-- tests/ui/transmute_32bit.stderr | 30 +- .../unnecessary_struct_initialization.fixed | 2 +- tests/ui/unnecessary_struct_initialization.rs | 2 +- tests/ui/vec.fixed | 1 + tests/ui/vec.rs | 1 + tests/ui/vec.stderr | 10 +- tests/ui/write_literal_2.stderr | 4 +- 174 files changed, 4682 insertions(+), 1635 deletions(-) create mode 100644 book/src/development/emitting_lints.md create mode 100644 book/src/development/trait_checking.md create mode 100644 book/src/development/writing_tests.md create mode 100644 clippy_lints/src/methods/iter_out_of_bounds.rs create mode 100644 clippy_lints/src/missing_asserts_for_indexing.rs rename clippy_lints/src/{incorrect_impls.rs => non_canonical_impls.rs} (88%) delete mode 100644 tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs delete mode 100644 tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs rename tests/ui/{cast_size_32bit.stderr => cast_size.32bit.stderr} (78%) rename tests/ui/{cast_size.stderr => cast_size.64bit.stderr} (94%) delete mode 100644 tests/ui/cast_size_32bit.rs create mode 100644 tests/ui/crashes/ice-11337.rs create mode 100644 tests/ui/crashes/ice-11422.fixed create mode 100644 tests/ui/crashes/ice-11422.rs create mode 100644 tests/ui/crashes/ice-11422.stderr rename tests/ui/{fn_to_numeric_cast_32bit.stderr => fn_to_numeric_cast.32bit.stderr} (70%) rename tests/ui/{fn_to_numeric_cast.stderr => fn_to_numeric_cast.64bit.stderr} (85%) delete mode 100644 tests/ui/fn_to_numeric_cast_32bit.rs create mode 100644 tests/ui/iter_out_of_bounds.rs create mode 100644 tests/ui/iter_out_of_bounds.stderr create mode 100644 tests/ui/large_enum_variant.32bit.stderr rename tests/ui/{large_enum_variant.stderr => large_enum_variant.64bit.stderr} (91%) create mode 100644 tests/ui/missing_asserts_for_indexing.fixed create mode 100644 tests/ui/missing_asserts_for_indexing.rs create mode 100644 tests/ui/missing_asserts_for_indexing.stderr create mode 100644 tests/ui/missing_asserts_for_indexing_unfixable.rs create mode 100644 tests/ui/missing_asserts_for_indexing_unfixable.stderr rename tests/ui/{incorrect_clone_impl_on_copy_type.fixed => non_canonical_clone_impl.fixed} (100%) rename tests/ui/{incorrect_clone_impl_on_copy_type.rs => non_canonical_clone_impl.rs} (100%) rename tests/ui/{incorrect_clone_impl_on_copy_type.stderr => non_canonical_clone_impl.stderr} (54%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type.fixed => non_canonical_partial_ord_impl.fixed} (100%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type.rs => non_canonical_partial_ord_impl.rs} (100%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type.stderr => non_canonical_partial_ord_impl.stderr} (63%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type_fully_qual.rs => non_canonical_partial_ord_impl_fully_qual.rs} (82%) rename tests/ui/{incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr => non_canonical_partial_ord_impl_fully_qual.stderr} (67%) create mode 100644 tests/ui/std_instead_of_core.fixed diff --git a/.github/driver.sh b/.github/driver.sh index 798782340ee7d..c05c6ecc1151c 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -30,7 +30,7 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? ./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1 -sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr >normalized.stderr +sed -e "s,tests/ui,\$DIR," -e "/= help: for/d" double_neg.stderr > normalized.stderr diff -u normalized.stderr tests/ui/double_neg.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 5c69714bc1e99..9b96f8dc25338 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -52,24 +52,14 @@ jobs: needs: changelog strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc] - exclude: + include: - os: ubuntu-latest - host: x86_64-apple-darwin - - os: ubuntu-latest - host: x86_64-pc-windows-msvc - - os: macos-latest - host: x86_64-unknown-linux-gnu - - os: macos-latest - host: i686-unknown-linux-gnu - - os: macos-latest - host: x86_64-pc-windows-msvc - - os: windows-latest host: x86_64-unknown-linux-gnu - - os: windows-latest + - os: ubuntu-latest host: i686-unknown-linux-gnu - os: windows-latest + host: x86_64-pc-windows-msvc + - os: macos-latest host: x86_64-apple-darwin runs-on: ${{ matrix.os }} @@ -84,8 +74,17 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Install i686 dependencies + if: matrix.host == 'i686-unknown-linux-gnu' + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install gcc-multilib zlib1g-dev:i386 + - name: Install toolchain - run: rustup show active-toolchain + run: | + rustup set default-host ${{ matrix.host }} + rustup show active-toolchain # Run - name: Set LD_LIBRARY_PATH (Linux) @@ -109,11 +108,11 @@ jobs: run: cargo build --tests --features deny-warnings,internal - name: Test - if: runner.os == 'Linux' + if: matrix.host == 'x86_64-unknown-linux-gnu' run: cargo test --features deny-warnings,internal - name: Test - if: runner.os != 'Linux' + if: matrix.host != 'x86_64-unknown-linux-gnu' run: cargo test --features deny-warnings,internal -- --skip dogfood - name: Test clippy_lints diff --git a/CHANGELOG.md b/CHANGELOG.md index 92cc19edb0505..a7ae4a0ee2c09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5031,6 +5031,7 @@ Released 2018-09-13 [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero [`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections [`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items +[`iter_out_of_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_out_of_bounds [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next [`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero @@ -5131,6 +5132,7 @@ Released 2018-09-13 [`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message +[`missing_asserts_for_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_asserts_for_indexing [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames @@ -5204,6 +5206,8 @@ Released 2018-09-13 [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal +[`non_canonical_clone_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_canonical_clone_impl +[`non_canonical_partial_ord_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_canonical_partial_ord_impl [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty @@ -5570,4 +5574,5 @@ Released 2018-09-13 [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings [`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments [`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates +[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow diff --git a/Cargo.toml b/Cargo.toml index 7c78beb5ddef9..2d8b590dbe311 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ tempfile = { version = "3.2", optional = true } termize = "0.1" [dev-dependencies] -ui_test = "0.18.1" +ui_test = "0.20" tester = "0.9" regex = "1.5" toml = "0.7.3" diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index daaefd06a97f3..b02457307d743 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -14,8 +14,11 @@ - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) - [Defining Lints](development/defining_lints.md) + - [Writing tests](development/writing_tests.md) - [Lint Passes](development/lint_passes.md) + - [Emitting lints](development/emitting_lints.md) - [Type Checking](development/type_checking.md) + - [Trait Checking](development/trait_checking.md) - [Method Checking](development/method_checking.md) - [Macro Expansions](development/macro_expansions.md) - [Common Tools](development/common_tools_writing_lints.md) diff --git a/book/src/development/emitting_lints.md b/book/src/development/emitting_lints.md new file mode 100644 index 0000000000000..a12f6aa91b307 --- /dev/null +++ b/book/src/development/emitting_lints.md @@ -0,0 +1,217 @@ +# Emitting a lint + +Once we have [defined a lint](defining_lints.md), written [UI +tests](writing_tests.md) and chosen [the lint pass](lint_passes.md) for the lint, +we can begin the implementation of the lint logic so that we can emit it and +gradually work towards a lint that behaves as expected. + +Note that we will not go into concrete implementation of a lint logic in this +chapter. We will go into details in later chapters as well as in two examples of +real Clippy lints. + +To emit a lint, we must implement a pass (see [Lint Passes](lint_passes.md)) for +the lint that we have declared. In this example we'll implement a "late" lint, +so take a look at the [LateLintPass][late_lint_pass] documentation, which +provides an abundance of methods that we can implement for our lint. + +```rust +pub trait LateLintPass<'tcx>: LintPass { + // Trait methods +} +``` + +By far the most common method used for Clippy lints is [`check_expr` +method][late_check_expr], this is because Rust is an expression language and, +more often than not, the lint we want to work on must examine expressions. + +> _Note:_ If you don't fully understand what expressions are in Rust, take a +> look at the official documentation on [expressions][rust_expressions] + +Other common ones include the [`check_fn` method][late_check_fn] and the +[`check_item` method][late_check_item]. + +### Emitting a lint + +Inside the trait method that we implement, we can write down the lint logic and +emit the lint with suggestions. + +Clippy's [diagnostics] provides quite a few diagnostic functions that we can use +to emit lints. Take a look at the documentation to pick one that suits your +lint's needs the best. Some common ones you will encounter in the Clippy +repository includes: + +- [`span_lint`]: Emits a lint without providing any other information +- [`span_lint_and_note`]: Emits a lint and adds a note +- [`span_lint_and_help`]: Emits a lint and provides a helpful message +- [`span_lint_and_sugg`]: Emits a lint and provides a suggestion to fix the code +- [`span_lint_and_then`]: Like `span_lint`, but allows for a lot of output + customization. + +```rust +impl<'tcx> LateLintPass<'tcx> for LintName { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint + if some_lint_expr_logic(expr) { + span_lint_and_help( + cx, // < The context + LINT_NAME, // < The name of the lint in ALL CAPS + expr.span, // < The span to lint + "message on why the lint is emitted", + None, // < An optional help span (to highlight something in the lint) + "message that provides a helpful suggestion", + ); + } + } +} +``` + +> Note: The message should be matter of fact and avoid capitalization and +> punctuation. If multiple sentences are needed, the messages should probably be +> split up into an error + a help / note / suggestion message. + +## Suggestions: Automatic fixes + +Some lints know what to change in order to fix the code. For example, the lint +[`range_plus_one`][range_plus_one] warns for ranges where the user wrote `x..y + +1` instead of using an [inclusive range][inclusive_range] (`x..=y`). The fix to +this code would be changing the `x..y + 1` expression to `x..=y`. **This is +where suggestions come in**. + +A suggestion is a change that the lint provides to fix the issue it is linting. +The output looks something like this (from the example earlier): + +```text +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:37:14 + | +LL | for _ in 1..1 + 1 {} + | ^^^^^^^^ help: use: `1..=1` +``` + +**Not all suggestions are always right**, some of them require human +supervision, that's why we have [Applicability][applicability]. + +Applicability indicates confidence in the correctness of the suggestion, some +are always right (`Applicability::MachineApplicable`), but we use +`Applicability::MaybeIncorrect` and others when talking about a suggestion that +may be incorrect. + +### Example + +The same lint `LINT_NAME` but that emits a suggestion would look something like this: + +```rust +impl<'tcx> LateLintPass<'tcx> for LintName { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint + if some_lint_expr_logic(expr) { + span_lint_and_sugg( // < Note this change + cx, + LINT_NAME, + span, + "message on why the lint is emitted", + "use", + format!("foo + {} * bar", snippet(cx, expr.span, "")), // < Suggestion + Applicability::MachineApplicable, + ); + } + } +} +``` + +Suggestions generally use the [`format!`][format_macro] macro to interpolate the +old values with the new ones. To get code snippets, use one of the `snippet*` +functions from `clippy_utils::source`. + +## How to choose between notes, help messages and suggestions + +Notes are presented separately from the main lint message, they provide useful +information that the user needs to understand why the lint was activated. They +are the most helpful when attached to a span. + +Examples: + +### Notes + +```text +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/drop_forget_ref.rs:10:5 + | +10 | forget(&SomeStruct); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::forget-ref` implied by `-D warnings` +note: argument has type &SomeStruct + --> $DIR/drop_forget_ref.rs:10:12 + | +10 | forget(&SomeStruct); + | ^^^^^^^^^^^ +``` + +### Help Messages + +Help messages are specifically to help the user. These are used in situation +where you can't provide a specific machine applicable suggestion. They can also +be attached to a span. + +Example: + +```text +error: constant division of 0.0 with 0.0 will always result in NaN + --> $DIR/zero_div_zero.rs:6:25 + | +6 | let other_f64_nan = 0.0f64 / 0.0; + | ^^^^^^^^^^^^ + | + = help: consider using `f64::NAN` if you would like a constant representing NaN +``` + +### Suggestions + +Suggestions are the most helpful, they are changes to the source code to fix the +error. The magic in suggestions is that tools like `rustfix` can detect them and +automatically fix your code. + +Example: + +```text +error: This `.fold` can be more succinctly expressed as `.any` +--> $DIR/methods.rs:390:13 + | +390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)` + | +``` + +### Snippets + +Snippets are pieces of the source code (as a string), they are extracted +generally using the [`snippet`][snippet_fn] function. + +For example, if you want to know how an item looks (and you know the item's +span), you could use `snippet(cx, span, "..")`. + +## Final: Run UI Tests to Emit the Lint + +Now, if we run our [UI test](writing_tests.md), we should see that Clippy now +produces output that contains the lint message we designed. + +The next step is to implement the logic properly, which is a detail that we will +cover in the next chapters. + +[diagnostics]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/index.html +[late_check_expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_expr +[late_check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_fn +[late_check_item]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_item +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html +[rust_expressions]: https://doc.rust-lang.org/reference/expressions.html +[`span_lint`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint.html +[`span_lint_and_note`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_note.html +[`span_lint_and_help`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_help.html +[`span_lint_and_sugg`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html +[`span_lint_and_then`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_then.html +[range_plus_one]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one +[inclusive_range]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html +[applicability]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_errors/enum.Applicability.html +[snippet_fn]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/source/fn.snippet.html +[format_macro]: https://doc.rust-lang.org/std/macro.format.html diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md new file mode 100644 index 0000000000000..fb263922cf88e --- /dev/null +++ b/book/src/development/trait_checking.md @@ -0,0 +1,105 @@ +# Trait Checking + +Besides [type checking](type_checking.md), we might want to examine if +a specific type `Ty` implements certain trait when implementing a lint. +There are three approaches to achieve this, depending on if the target trait +that we want to examine has a [diagnostic item][diagnostic_items], +[lang item][lang_items], or neither. + +## Using Diagnostic Items + +As explained in the [Rust Compiler Development Guide][rustc_dev_guide], diagnostic items +are introduced for identifying types via [Symbols][symbol]. + +For instance, if we want to examine whether an expression implements +the `Iterator` trait, we could simply write the following code, +providing the `LateContext` (`cx`), our expression at hand, and +the symbol of the trait in question: + +```rust +use clippy_utils::is_trait_method; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_span::symbol::sym; + +impl LateLintPass<'_> for CheckIteratorTraitLint { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]) + }); + if implements_iterator { + // [...] + } + + } +} +``` + +> **Note**: Refer to [this index][symbol_index] for all the defined `Symbol`s. + +## Using Lang Items + +Besides diagnostic items, we can also use [`lang_items`][lang_items]. +Take a look at the documentation to find that `LanguageItems` contains +all language items defined in the compiler. + +Using one of its `*_trait` method, we could obtain the [DefId] of any +specific item, such as `Clone`, `Copy`, `Drop`, `Eq`, which are familiar +to many Rustaceans. + +For instance, if we want to examine whether an expression `expr` implements +`Drop` trait, we could access `LanguageItems` via our `LateContext`'s +[TyCtxt], which provides a `lang_items` method that will return the id of +`Drop` trait to us. Then, by calling Clippy utils function `implements_trait` +we can check that the `Ty` of the `expr` implements the trait: + +```rust +use clippy_utils::implements_trait; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +impl LateLintPass<'_> for CheckDropTraitLint { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let ty = cx.typeck_results().expr_ty(expr); + if cx.tcx.lang_items() + .drop_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) { + println!("`expr` implements `Drop` trait!"); + } + } +} +``` + +## Using Type Path + +If neither diagnostic item nor a language item is available, we can use +[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait +implementation. + +> **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item. + +Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` : + +```rust +use clippy_utils::{match_trait_method, paths}; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) { + println!("`expr` implements `CORE_ITER_CLONED` trait!"); + } + } +} +``` + +[DefId]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html +[diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html +[lang_items]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/lang_items/struct.LanguageItems.html +[paths]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/paths.rs +[rustc_dev_guide]: https://rustc-dev-guide.rust-lang.org/ +[symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html +[symbol_index]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/sym/index.html +[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html +[rust]: https://github.com/rust-lang/rust diff --git a/book/src/development/writing_tests.md b/book/src/development/writing_tests.md new file mode 100644 index 0000000000000..8937e0d8e9469 --- /dev/null +++ b/book/src/development/writing_tests.md @@ -0,0 +1,218 @@ +# Testing + +Developing lints for Clippy is a Test-Driven Development (TDD) process because +our first task before implementing any logic for a new lint is to write some test cases. + +## Develop Lints with Tests + +When we develop Clippy, we enter a complex and chaotic realm full of +programmatic issues, stylistic errors, illogical code and non-adherence to convention. +Tests are the first layer of order we can leverage to define when and where +we want a new lint to trigger or not. + +Moreover, writing tests first help Clippy developers to find a balance for +the first iteration of and further enhancements for a lint. +With test cases on our side, we will not have to worry about over-engineering +a lint on its first version nor missing out some obvious edge cases of the lint. +This approach empowers us to iteratively enhance each lint. + +## Clippy UI Tests + +We use **UI tests** for testing in Clippy. These UI tests check that the output +of Clippy is exactly as we expect it to be. Each test is just a plain Rust file +that contains the code we want to check. + +The output of Clippy is compared against a `.stderr` file. Note that you don't +have to create this file yourself. We'll get to generating the `.stderr` files +with the command [`cargo bless`](#cargo-bless) (seen later on). + +### Write Test Cases + +Let us now think about some tests for our imaginary `foo_functions` lint. We +start by opening the test file `tests/ui/foo_functions.rs` that was created by +`cargo dev new_lint`. + +Update the file with some examples to get started: + +```rust +#![warn(clippy::foo_functions)] // < Add this, so the lint is guaranteed to be enabled in this file + +// Impl methods +struct A; +impl A { + pub fn fo(&self) {} + pub fn foo(&self) {} //~ ERROR: function called "foo" + pub fn food(&self) {} +} + +// Default trait methods +trait B { + fn fo(&self) {} + fn foo(&self) {} //~ ERROR: function called "foo" + fn food(&self) {} +} + +// Plain functions +fn fo() {} +fn foo() {} //~ ERROR: function called "foo" +fn food() {} + +fn main() { + // We also don't want to lint method calls + foo(); + let a = A; + a.foo(); +} +``` + +Without actual lint logic to emit the lint when we see a `foo` function name, +this test will just pass, because no lint will be emitted. However, we can now +run the test with the following command: + +```sh +$ TESTNAME=foo_functions cargo uitest +``` + +Clippy will compile and it will conclude with an `ok` for the tests: + +``` +...Clippy warnings and test outputs... +test compile_test ... ok +test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s +``` + +This is normal. After all, we wrote a bunch of Rust code but we haven't really +implemented any logic for Clippy to detect `foo` functions and emit a lint. + +As we gradually implement our lint logic, we will keep running this UI test command. +Clippy will begin outputting information that allows us to check if the output is +turning into what we want it to be. + +### Example output + +As our `foo_functions` lint is tested, the output would look something like this: + +``` +failures: +---- compile_test stdout ---- +normalized stderr: +error: function called "foo" + --> $DIR/foo_functions.rs:6:12 + | +LL | pub fn foo(&self) {} + | ^^^ + | + = note: `-D clippy::foo-functions` implied by `-D warnings` +error: function called "foo" + --> $DIR/foo_functions.rs:13:8 + | +LL | fn foo(&self) {} + | ^^^ +error: function called "foo" + --> $DIR/foo_functions.rs:19:4 + | +LL | fn foo() {} + | ^^^ +error: aborting due to 3 previous errors +``` + +Note the *failures* label at the top of the fragment, we'll get rid of it +(saving this output) in the next section. + +> _Note:_ You can run multiple test files by specifying a comma separated list: +> `TESTNAME=foo_functions,bar_methods,baz_structs`. + +### `cargo bless` + +Once we are satisfied with the output, we need to run this command to +generate or update the `.stderr` file for our lint: + +```sh +$ TESTNAME=foo_functions cargo uibless +``` + +This writes the emitted lint suggestions and fixes to the `.stderr` file, with +the reason for the lint, suggested fixes, and line numbers, etc. + +Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit +our lint, we need to commit the generated `.stderr` files, too. + +In general, you should only commit files changed by `cargo bless` for the +specific lint you are creating/editing. + +> _Note:_ If the generated `.stderr`, and `.fixed` files are empty, +> they should be removed. + +## `toml` Tests + +Some lints can be configured through a `clippy.toml` file. Those configuration +values are tested in `tests/ui-toml`. + +To add a new test there, create a new directory and add the files: + +- `clippy.toml`: Put here the configuration value you want to test. +- `lint_name.rs`: A test file where you put the testing code, that should see a + different lint behavior according to the configuration set in the + `clippy.toml` file. + +The potential `.stderr` and `.fixed` files can again be generated with `cargo +bless`. + +## Cargo Lints + +The process of testing is different for Cargo lints in that now we are +interested in the `Cargo.toml` manifest file. In this case, we also need a +minimal crate associated with that manifest. Those tests are generated in +`tests/ui-cargo`. + +Imagine we have a new example lint that is named `foo_categories`, we can run: + +```sh +$ cargo dev new_lint --name=foo_categories --pass=late --category=cargo +``` + +After running `cargo dev new_lint` we will find by default two new crates, +each with its manifest file: + +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the + new lint to raise an error. +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger + the lint. + +If you need more cases, you can copy one of those crates (under +`foo_categories`) and rename it. + +The process of generating the `.stderr` file is the same as for other lints +and prepending the `TESTNAME` variable to `cargo uitest` works for Cargo lints too. + +## Rustfix Tests + +If the lint you are working on is making use of structured suggestions, +[`rustfix`] will apply the suggestions from the lint to the test file code and +compare that to the contents of a `.fixed` file. + +Structured suggestions tell a user how to fix or re-write certain code that has +been linted with [`span_lint_and_sugg`]. + +Should `span_lint_and_sugg` be used to generate a suggestion, but not all +suggestions lead to valid code, you can use the `//@no-rustfix` comment on top +of the test file, to not run `rustfix` on that file. + +We'll talk about suggestions more in depth in a [later chapter](emitting_lints.md). + +Use `cargo bless` to automatically generate the `.fixed` file after running +the tests. + +[`rustfix`]: https://github.com/rust-lang/rustfix +[`span_lint_and_sugg`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html + +## Testing Manually + +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 from the working copy root. + +```sh +$ cargo dev lint input.rs +``` diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index caaad6d11736f..52c795e04fe95 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -751,3 +751,27 @@ Which crates to allow absolute paths from * [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) +## `enforce-iter-loop-reborrow` +#### Example +``` +let mut vec = vec![1, 2, 3]; +let rmvec = &mut vec; +for _ in rmvec.iter() {} +for _ in rmvec.iter_mut() {} +``` + +Use instead: +``` +let mut vec = vec![1, 2, 3]; +let rmvec = &mut vec; +for _ in &*rmvec {} +for _ in &mut *rmvec {} +``` + +**Default Value:** `false` (`bool`) + +--- +**Affected lints:** +* [`explicit_iter_loop`](https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop) + + diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index f73f1ed18f8b6..a4d40df52e7bc 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -211,8 +211,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO, crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, - crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO, - crate::incorrect_impls::INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE_INFO, crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO, crate::indexing_slicing::INDEXING_SLICING_INFO, crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO, @@ -365,6 +363,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::ITER_NTH_ZERO_INFO, crate::methods::ITER_ON_EMPTY_COLLECTIONS_INFO, crate::methods::ITER_ON_SINGLE_ITEMS_INFO, + crate::methods::ITER_OUT_OF_BOUNDS_INFO, crate::methods::ITER_OVEREAGER_CLONED_INFO, crate::methods::ITER_SKIP_NEXT_INFO, crate::methods::ITER_SKIP_ZERO_INFO, @@ -456,6 +455,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::misc_early::ZERO_PREFIXED_LITERAL_INFO, crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO, crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO, + crate::missing_asserts_for_indexing::MISSING_ASSERTS_FOR_INDEXING_INFO, crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, @@ -496,6 +496,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO, crate::no_effect::UNNECESSARY_OPERATION_INFO, crate::no_mangle_with_rust_abi::NO_MANGLE_WITH_RUST_ABI_INFO, + crate::non_canonical_impls::NON_CANONICAL_CLONE_IMPL_INFO, + crate::non_canonical_impls::NON_CANONICAL_PARTIAL_ORD_IMPL_INFO, crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO, crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO, crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO, diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 03b5a2d6d0829..bbce6e1dd8f2b 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -69,6 +69,9 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { } /// Returns true if the given item is a union with at least two non-ZST fields. +/// (ZST fields having an arbitrary offset is completely inconsequential, and +/// if there is only one field left after ignoring ZST fields then the offset +/// of that field does not matter either.) fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if let ItemKind::Union(data, _) = &item.kind { data.fields().iter().filter(|f| !is_zst(cx, f.ty)).count() >= 2 diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 58c2785500013..8090f821d1ff2 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -3,7 +3,7 @@ use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exact use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, }; @@ -33,7 +33,6 @@ use rustc_middle::ty::{ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::collections::VecDeque; @@ -452,13 +451,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // Trait methods taking `self` arg_ty } && impl_ty.is_ref() - && cx.tcx.infer_ctxt().build() - .type_implements_trait( - trait_id, - [impl_ty.into()].into_iter().chain(args.iter().copied()), - cx.param_env, - ) - .must_apply_modulo_regions() + && implements_trait( + cx, + impl_ty, + trait_id, + &args[..cx.tcx.generics_of(trait_id).params.len() - 1], + ) { false } else { @@ -609,12 +607,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { adjusted_ty, }, )); - } else if stability.is_deref_stable() { + } else if stability.is_deref_stable() + && let Some(parent) = get_parent_expr(cx, expr) + { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { - span: expr.span, - hir_id: expr.hir_id, + span: parent.span, + hir_id: parent.hir_id, adjusted_ty, }, )); @@ -1399,6 +1399,13 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data return; } + if let ExprKind::Field(parent_expr, _) = expr.kind + && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(parent_expr).kind() + && adt.is_union() + { + // Auto deref does not apply on union field + return; + } span_lint_hir_and_then( cx, EXPLICIT_AUTO_DEREF, diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 9a85cc4ce2dc0..d2bfc4f8e273d 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -217,8 +217,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind(); if let attrs = cx.tcx.hir().attrs(item.hir_id()); if !attrs.iter().any(|attr| attr.doc_str().is_some()); - if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); - if !child_attrs.iter().any(|attr| attr.doc_str().is_some()); + if cx.tcx.hir().attrs(impl_item_hir).is_empty(); then { if adt_def.is_struct() { diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 1971cab64ef38..7469f813ef87f 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::macro_backtrace; +use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefIdMap; -use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; +use rustc_hir::{Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{ExpnId, Span}; @@ -111,6 +112,10 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { self.check(cx, expr.span); + // `$t + $t` can have the context of $t, check also the span of the binary operator + if let ExprKind::Binary(op, ..) = expr.kind { + self.check(cx, op.span); + } } fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { @@ -147,4 +152,8 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) { self.check(cx, path.span); } + + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { + self.check(cx, attr.span); + } } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index d8f2fbcea0027..4498e9ccdec25 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -472,7 +472,7 @@ struct DocHeaders { fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> Option { /// We don't want the parser to choke on intra doc links. Since we don't - /// actually care about rendering them, just pretend that all broken links are + /// actually care about rendering them, just pretend that all broken links /// point to a fake address. #[expect(clippy::unnecessary_wraps)] // we're following a type signature fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> { diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index ab6ad3f3b3ab0..e2d19e2455700 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -6,7 +6,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && then_expr.span.ctxt() == ctxt && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) - && !stmts_contains_early_return(then_block.stmts) + && !contains_return(then_block.stmts) { let mut app = Applicability::Unspecified; let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string(); @@ -116,17 +116,3 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { extract_msrv_attr!(LateContext); } - -fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool { - stmts.iter().any(|stmt| { - let Stmt { - kind: StmtKind::Semi(e), - .. - } = stmt - else { - return false; - }; - - contains_return(e) - }) -} diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs index c635120b88245..d8ead1c9d9f68 100644 --- a/clippy_lints/src/ignored_unit_patterns.rs +++ b/clippy_lints/src/ignored_unit_patterns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use hir::PatKind; +use hir::{Node, PatKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -37,6 +37,17 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { + match cx.tcx.hir().get_parent(pat.hir_id) { + Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => { + // Ignore function parameters + return; + }, + Node::Local(local) if local.ty.is_some() => { + // Ignore let bindings with explicit type + return; + }, + _ => {}, + } if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).is_unit() { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index c6d1acad3a0f1..ec9044bba5cc3 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem, @@ -9,7 +9,7 @@ use rustc_hir::{ }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, ClauseKind, TyCtxt}; +use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -45,52 +45,169 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub IMPLIED_BOUNDS_IN_IMPLS, - complexity, + nursery, "specifying bounds that are implied by other bounds in `impl Trait` type" } declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]); -/// This function tries to, for all type parameters in a supertype predicate `GenericTrait`, -/// check if the substituted type in the implied-by bound matches with what's subtituted in the -/// implied type. +#[allow(clippy::too_many_arguments)] +fn emit_lint( + cx: &LateContext<'_>, + poly_trait: &rustc_hir::PolyTraitRef<'_>, + opaque_ty: &rustc_hir::OpaqueTy<'_>, + index: usize, + // The bindings that were implied + implied_bindings: &[rustc_hir::TypeBinding<'_>], + // The original bindings that `implied_bindings` are implied from + implied_by_bindings: &[rustc_hir::TypeBinding<'_>], + implied_by_args: &[GenericArg<'_>], + implied_by_span: Span, +) { + let implied_by = snippet(cx, implied_by_span, ".."); + + span_lint_and_then( + cx, + IMPLIED_BOUNDS_IN_IMPLS, + poly_trait.span, + &format!("this bound is already specified as the supertrait of `{implied_by}`"), + |diag| { + // If we suggest removing a bound, we may also need to extend the span + // to include the `+` token that is ahead or behind, + // so we don't end up with something like `impl + B` or `impl A + ` + + let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) { + poly_trait.span.to(next_bound.span().shrink_to_lo()) + } else if index > 0 + && let Some(prev_bound) = opaque_ty.bounds.get(index - 1) + { + prev_bound.span().shrink_to_hi().to(poly_trait.span.shrink_to_hi()) + } else { + poly_trait.span + }; + + let mut sugg = vec![(implied_span_extended, String::new())]; + + // We also might need to include associated type binding that were specified in the implied bound, + // but omitted in the implied-by bound: + // `fn f() -> impl Deref + DerefMut` + // If we're going to suggest removing `Deref<..>`, we'll need to put `` on `DerefMut` + let omitted_assoc_tys: Vec<_> = implied_bindings + .iter() + .filter(|binding| !implied_by_bindings.iter().any(|b| b.ident == binding.ident)) + .collect(); + + if !omitted_assoc_tys.is_empty() { + // `<>` needs to be added if there aren't yet any generic arguments or bindings + let needs_angle_brackets = implied_by_args.is_empty() && implied_by_bindings.is_empty(); + let insert_span = match (implied_by_args, implied_by_bindings) { + ([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(), + ([.., arg], []) => arg.span().shrink_to_hi(), + ([], [.., binding]) => binding.span.shrink_to_hi(), + ([], []) => implied_by_span.shrink_to_hi(), + }; + + let mut associated_tys_sugg = if needs_angle_brackets { + "<".to_owned() + } else { + // If angle brackets aren't needed (i.e., there are already generic arguments or bindings), + // we need to add a comma: + // `impl A` + // ^ if we insert `Assoc=i32` without a comma here, that'd be invalid syntax: + // `impl A` + ", ".to_owned() + }; + + for (index, binding) in omitted_assoc_tys.into_iter().enumerate() { + if index > 0 { + associated_tys_sugg += ", "; + } + associated_tys_sugg += &snippet(cx, binding.span, ".."); + } + if needs_angle_brackets { + associated_tys_sugg += ">"; + } + sugg.push((insert_span, associated_tys_sugg)); + } + + diag.multipart_suggestion_with_style( + "try removing this bound", + sugg, + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + }, + ); +} + +/// Tries to "resolve" a type. +/// The index passed to this function must start with `Self=0`, i.e. it must be a valid +/// type parameter index. +/// If the index is out of bounds, it means that the generic parameter has a default type. +fn try_resolve_type<'tcx>( + tcx: TyCtxt<'tcx>, + args: &'tcx [GenericArg<'tcx>], + generics: &'tcx Generics, + index: usize, +) -> Option> { + match args.get(index - 1) { + Some(GenericArg::Type(ty)) => Some(hir_ty_to_ty(tcx, ty)), + Some(_) => None, + None => Some(tcx.type_of(generics.params[index].def_id).skip_binder()), + } +} + +/// This function tries to, for all generic type parameters in a supertrait predicate `trait ...: +/// GenericTrait`, check if the substituted type in the implied-by bound matches with what's +/// subtituted in the implied bound. /// /// Consider this example. /// ```rust,ignore /// trait GenericTrait {} /// trait GenericSubTrait: GenericTrait {} -/// ^ trait_predicate_args: [Self#0, U#2] +/// ^^^^^^^^^^^^^^^ trait_predicate_args: [Self#0, U#2] +/// (the Self#0 is implicit: `>`) /// impl GenericTrait for () {} /// impl GenericSubTrait<(), i32, ()> for () {} -/// impl GenericSubTrait<(), [u8; 8], ()> for () {} +/// impl GenericSubTrait<(), i64, ()> for () {} /// -/// fn f() -> impl GenericTrait + GenericSubTrait<(), [u8; 8], ()> { -/// ^^^ implied_args ^^^^^^^^^^^^^^^ implied_by_args -/// (we are interested in `[u8; 8]` specifically, as that -/// is what `U` in `GenericTrait` is substituted with) -/// () +/// fn f() -> impl GenericTrait + GenericSubTrait<(), i64, ()> { +/// ^^^ implied_args ^^^^^^^^^^^ implied_by_args +/// (we are interested in `i64` specifically, as that +/// is what `U` in `GenericTrait` is substituted with) /// } /// ``` -/// Here i32 != [u8; 8], so this will return false. -fn is_same_generics( - tcx: TyCtxt<'_>, - trait_predicate_args: &[ty::GenericArg<'_>], - implied_by_args: &[GenericArg<'_>], - implied_args: &[GenericArg<'_>], +/// Here i32 != i64, so this will return false. +fn is_same_generics<'tcx>( + tcx: TyCtxt<'tcx>, + trait_predicate_args: &'tcx [ty::GenericArg<'tcx>], + implied_by_args: &'tcx [GenericArg<'tcx>], + implied_args: &'tcx [GenericArg<'tcx>], + implied_by_def_id: DefId, + implied_def_id: DefId, ) -> bool { + // Get the generics of the two traits to be able to get default generic parameter. + let implied_by_generics = tcx.generics_of(implied_by_def_id); + let implied_generics = tcx.generics_of(implied_def_id); + trait_predicate_args .iter() .enumerate() .skip(1) // skip `Self` implicit arg .all(|(arg_index, arg)| { - if let Some(ty) = arg.as_type() - && let &ty::Param(ty::ParamTy{ index, .. }) = ty.kind() - // Since `trait_predicate_args` and type params in traits start with `Self=0` - // and generic argument lists `GenericTrait` don't have `Self`, - // we need to subtract 1 from the index. - && let GenericArg::Type(ty_a) = implied_by_args[index as usize - 1] - && let GenericArg::Type(ty_b) = implied_args[arg_index - 1] - { - hir_ty_to_ty(tcx, ty_a) == hir_ty_to_ty(tcx, ty_b) + if let Some(ty) = arg.as_type() { + if let &ty::Param(ty::ParamTy { index, .. }) = ty.kind() + // `index == 0` means that it's referring to `Self`, + // in which case we don't try to substitute it + && index != 0 + && let Some(ty_a) = try_resolve_type(tcx, implied_by_args, implied_by_generics, index as usize) + && let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index) + { + ty_a == ty_b + } else if let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index) { + ty == ty_b + } else { + false + } } else { false } @@ -121,7 +238,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates && !predicates.is_empty() // If the trait has no supertrait, there is nothing to add. { - Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates)) + Some((bound.span(), path, predicates, trait_def_id)) } else { None } @@ -134,43 +251,42 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound && let [.., path] = poly_trait.trait_ref.path.segments && let implied_args = path.args.map_or([].as_slice(), |a| a.args) + && let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings) && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id() - && let Some(implied_by_span) = implied_bounds.iter().find_map(|&(span, implied_by_args, preds)| { - preds.iter().find_map(|(clause, _)| { - if let ClauseKind::Trait(tr) = clause.kind().skip_binder() - && tr.def_id() == def_id - && is_same_generics(cx.tcx, tr.trait_ref.args, implied_by_args, implied_args) - { - Some(span) - } else { - None - } + && let Some((implied_by_span, implied_by_args, implied_by_bindings)) = implied_bounds + .iter() + .find_map(|&(span, implied_by_path, preds, implied_by_def_id)| { + let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args); + let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings); + + preds.iter().find_map(|(clause, _)| { + if let ClauseKind::Trait(tr) = clause.kind().skip_binder() + && tr.def_id() == def_id + && is_same_generics( + cx.tcx, + tr.trait_ref.args, + implied_by_args, + implied_args, + implied_by_def_id, + def_id, + ) + { + Some((span, implied_by_args, implied_by_bindings)) + } else { + None + } + }) }) - }) { - let implied_by = snippet(cx, implied_by_span, ".."); - span_lint_and_then( - cx, IMPLIED_BOUNDS_IN_IMPLS, - poly_trait.span, - &format!("this bound is already specified as the supertrait of `{implied_by}`"), - |diag| { - // If we suggest removing a bound, we may also need extend the span - // to include the `+` token, so we don't end up with something like `impl + B` - - let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) { - poly_trait.span.to(next_bound.span().shrink_to_lo()) - } else { - poly_trait.span - }; - - diag.span_suggestion_with_style( - implied_span_extended, - "try removing this bound", - "", - Applicability::MachineApplicable, - SuggestionStyle::ShowAlways - ); - } + emit_lint( + cx, + poly_trait, + opaque_ty, + index, + implied_bindings, + implied_by_bindings, + implied_by_args, + implied_by_span ); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 20fdf26dc521b..f52614b62088f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -156,7 +156,6 @@ mod implicit_saturating_add; mod implicit_saturating_sub; mod implied_bounds_in_impls; mod inconsistent_struct_constructor; -mod incorrect_impls; mod index_refutable_slice; mod indexing_slicing; mod infinite_iter; @@ -212,6 +211,7 @@ mod misc; mod misc_early; mod mismatching_type_param_order; mod missing_assert_message; +mod missing_asserts_for_indexing; mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; @@ -245,6 +245,7 @@ mod neg_multiply; mod new_without_default; mod no_effect; mod no_mangle_with_rust_abi; +mod non_canonical_impls; mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; @@ -697,7 +698,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); - store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv()))); + let enforce_iter_loop_reborrow = conf.enforce_iter_loop_reborrow; + store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv(), enforce_iter_loop_reborrow))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(entry::HashMapPass)); @@ -1070,7 +1072,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: avoid_breaking_exported_api, )) }); - store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls)); + store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls)); store.register_late_pass(move |_| { Box::new(single_call_fn::SingleCallFn { avoid_breaking_exported_api, @@ -1101,6 +1103,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)); + store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 7b8c88235a97d..6ab256ef0010b 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -13,8 +13,14 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMut use rustc_middle::ty::{self, EarlyBinder, Ty, TypeAndMut}; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, msrv: &Msrv) { - let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr) else { +pub(super) fn check( + cx: &LateContext<'_>, + self_arg: &Expr<'_>, + call_expr: &Expr<'_>, + msrv: &Msrv, + enforce_iter_loop_reborrow: bool, +) { + let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow) else { return; }; if let ty::Array(_, count) = *ty.peel_refs().kind() { @@ -102,6 +108,7 @@ fn is_ref_iterable<'tcx>( cx: &LateContext<'tcx>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, + enforce_iter_loop_reborrow: bool, ) -> Option<(AdjustKind, Ty<'tcx>)> { let typeck = cx.typeck_results(); if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) @@ -142,7 +149,8 @@ fn is_ref_iterable<'tcx>( { return Some((AdjustKind::None, self_ty)); } - } else if let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind() + } else if enforce_iter_loop_reborrow + && let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind() && let Some(mutbl) = mutbl { // Attempt to reborrow the mutable reference @@ -186,7 +194,8 @@ fn is_ref_iterable<'tcx>( }, .. ] => { - if target != self_ty + if enforce_iter_loop_reborrow + && target != self_ty && implements_trait(cx, target, trait_id, &[]) && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target]) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index ffd29ab763027..1fb16adad7a19 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -609,10 +609,14 @@ declare_clippy_lint! { pub struct Loops { msrv: Msrv, + enforce_iter_loop_reborrow: bool, } impl Loops { - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(msrv: Msrv, enforce_iter_loop_reborrow: bool) -> Self { + Self { + msrv, + enforce_iter_loop_reborrow, + } } } impl_lint_pass!(Loops => [ @@ -719,7 +723,7 @@ impl Loops { if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { match method.ident.as_str() { "iter" | "iter_mut" => { - explicit_iter_loop::check(cx, self_arg, arg, &self.msrv); + explicit_iter_loop::check(cx, self_arg, arg, &self.msrv, self.enforce_iter_loop_reborrow); }, "into_iter" => { explicit_into_iter_loop::check(cx, self_arg, arg); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index cc19ac55e5e44..3d8a4cd948afc 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -1,13 +1,13 @@ use super::utils::make_iterator_snippet; use super::NEVER_LOOP; -use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{sym, Span}; use std::iter::{once, Iterator}; pub(super) fn check<'tcx>( @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( for_loop: Option<&ForLoop<'_>>, ) { match never_loop_block(cx, block, &mut Vec::new(), loop_id) { - NeverLoopResult::AlwaysBreak => { + NeverLoopResult::Diverging => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { arg: iterator, @@ -39,67 +39,76 @@ pub(super) fn check<'tcx>( } }); }, - NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (), - NeverLoopResult::IgnoreUntilEnd(_) => unreachable!(), + NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Normal => (), } } +/// The `never_loop` analysis keeps track of three things: +/// +/// * Has any (reachable) code path hit a `continue` of the main loop? +/// * Is the current code path diverging (that is, the next expression is not reachable) +/// * For each block label `'a` inside the main loop, has any (reachable) code path encountered a +/// `break 'a`? +/// +/// The first two bits of information are in this enum, and the last part is in the +/// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by +/// scope. #[derive(Copy, Clone)] enum NeverLoopResult { - // A break/return always get triggered but not necessarily for the main loop. - AlwaysBreak, - // A continue may occur for the main loop. + /// A continue may occur for the main loop. MayContinueMainLoop, - // Ignore everything until the end of the block with this id - IgnoreUntilEnd(HirId), - Otherwise, + /// We have not encountered any main loop continue, + /// but we are diverging (subsequent control flow is not reachable) + Diverging, + /// We have not encountered any main loop continue, + /// and subsequent control flow is (possibly) reachable + Normal, } #[must_use] fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult { match arg { - NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, + NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal, NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, - NeverLoopResult::IgnoreUntilEnd(id) => NeverLoopResult::IgnoreUntilEnd(id), } } // Combine two results for parts that are called in order. #[must_use] -fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult { +fn combine_seq(first: NeverLoopResult, second: impl FnOnce() -> NeverLoopResult) -> NeverLoopResult { match first { - NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop | NeverLoopResult::IgnoreUntilEnd(_) => { - first - }, - NeverLoopResult::Otherwise => second, + NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop => first, + NeverLoopResult::Normal => second(), + } +} + +// Combine an iterator of results for parts that are called in order. +#[must_use] +fn combine_seq_many(iter: impl IntoIterator) -> NeverLoopResult { + for e in iter { + if let NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop = e { + return e; + } } + NeverLoopResult::Normal } // Combine two results where only one of the part may have been executed. #[must_use] -fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirId]) -> NeverLoopResult { +fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult { match (b1, b2) { - (NeverLoopResult::IgnoreUntilEnd(a), NeverLoopResult::IgnoreUntilEnd(b)) => { - if ignore_ids.iter().find(|&e| e == &a || e == &b).unwrap() == &a { - NeverLoopResult::IgnoreUntilEnd(b) - } else { - NeverLoopResult::IgnoreUntilEnd(a) - } - }, - (i @ NeverLoopResult::IgnoreUntilEnd(_), NeverLoopResult::AlwaysBreak) - | (NeverLoopResult::AlwaysBreak, i @ NeverLoopResult::IgnoreUntilEnd(_)) => i, - (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { NeverLoopResult::MayContinueMainLoop }, - (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise, + (NeverLoopResult::Normal, _) | (_, NeverLoopResult::Normal) => NeverLoopResult::Normal, + (NeverLoopResult::Diverging, NeverLoopResult::Diverging) => NeverLoopResult::Diverging, } } fn never_loop_block<'tcx>( cx: &LateContext<'tcx>, block: &Block<'tcx>, - ignore_ids: &mut Vec, + local_labels: &mut Vec<(HirId, bool)>, main_loop_id: HirId, ) -> NeverLoopResult { let iter = block @@ -107,15 +116,21 @@ fn never_loop_block<'tcx>( .iter() .filter_map(stmt_to_expr) .chain(block.expr.map(|expr| (expr, None))); - - iter.map(|(e, els)| { - let e = never_loop_expr(cx, e, ignore_ids, main_loop_id); + combine_seq_many(iter.map(|(e, els)| { + let e = never_loop_expr(cx, e, local_labels, main_loop_id); // els is an else block in a let...else binding els.map_or(e, |els| { - combine_branches(e, never_loop_block(cx, els, ignore_ids, main_loop_id), ignore_ids) + combine_seq(e, || match never_loop_block(cx, els, local_labels, main_loop_id) { + // Returning MayContinueMainLoop here means that + // we will not evaluate the rest of the body + NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, + // An else block always diverges, so the Normal case should not happen, + // but the analysis is approximate so it might return Normal anyway. + // Returning Normal here says that nothing more happens on the main path + NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal, + }) }) - }) - .fold(NeverLoopResult::Otherwise, combine_seq) + })) } fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { @@ -131,76 +146,69 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t fn never_loop_expr<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, - ignore_ids: &mut Vec, + local_labels: &mut Vec<(HirId, bool)>, main_loop_id: HirId, ) -> NeverLoopResult { - match expr.kind { + let result = match expr.kind { ExprKind::Unary(_, e) | ExprKind::Cast(e, _) | ExprKind::Type(e, _) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Repeat(e, _) - | ExprKind::DropTemps(e) => never_loop_expr(cx, e, ignore_ids, main_loop_id), - ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, ignore_ids, main_loop_id), - ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, &mut es.iter(), ignore_ids, main_loop_id), + | ExprKind::DropTemps(e) => never_loop_expr(cx, e, local_labels, main_loop_id), + ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, local_labels, main_loop_id), + ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, es.iter(), local_labels, main_loop_id), ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all( cx, - &mut std::iter::once(receiver).chain(es.iter()), - ignore_ids, + std::iter::once(receiver).chain(es.iter()), + local_labels, main_loop_id, ), ExprKind::Struct(_, fields, base) => { - let fields = never_loop_expr_all(cx, &mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id); + let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id); if let Some(base) = base { - combine_seq(fields, never_loop_expr(cx, base, ignore_ids, main_loop_id)) + combine_seq(fields, || never_loop_expr(cx, base, local_labels, main_loop_id)) } else { fields } }, - ExprKind::Call(e, es) => never_loop_expr_all(cx, &mut once(e).chain(es.iter()), ignore_ids, main_loop_id), + ExprKind::Call(e, es) => never_loop_expr_all(cx, once(e).chain(es.iter()), local_labels, main_loop_id), ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2, _) => { - never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id) - }, + | ExprKind::Index(e1, e2, _) => never_loop_expr_all(cx, [e1, e2].iter().copied(), local_labels, main_loop_id), ExprKind::Loop(b, _, _, _) => { - // Break can come from the inner loop so remove them. - absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id)) + // We don't attempt to track reachability after a loop, + // just assume there may have been a break somewhere + absorb_break(never_loop_block(cx, b, local_labels, main_loop_id)) }, ExprKind::If(e, e2, e3) => { - let e1 = never_loop_expr(cx, e, ignore_ids, main_loop_id); - let e2 = never_loop_expr(cx, e2, ignore_ids, main_loop_id); - // If we know the `if` condition evaluates to `true`, don't check everything past it; it - // should just return whatever's evaluated for `e1` and `e2` since `e3` is unreachable - if let Some(Constant::Bool(true)) = constant(cx, cx.typeck_results(), e) { - return combine_seq(e1, e2); - } - let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| { - never_loop_expr(cx, e, ignore_ids, main_loop_id) - }); - combine_seq(e1, combine_branches(e2, e3, ignore_ids)) + let e1 = never_loop_expr(cx, e, local_labels, main_loop_id); + combine_seq(e1, || { + let e2 = never_loop_expr(cx, e2, local_labels, main_loop_id); + let e3 = e3.as_ref().map_or(NeverLoopResult::Normal, |e| { + never_loop_expr(cx, e, local_labels, main_loop_id) + }); + combine_branches(e2, e3) + }) }, ExprKind::Match(e, arms, _) => { - let e = never_loop_expr(cx, e, ignore_ids, main_loop_id); - if arms.is_empty() { - e - } else { - let arms = never_loop_expr_branch(cx, &mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id); - combine_seq(e, arms) - } + let e = never_loop_expr(cx, e, local_labels, main_loop_id); + combine_seq(e, || { + arms.iter().fold(NeverLoopResult::Diverging, |a, b| { + combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)) + }) + }) }, ExprKind::Block(b, l) => { if l.is_some() { - ignore_ids.push(b.hir_id); - } - let ret = never_loop_block(cx, b, ignore_ids, main_loop_id); - if l.is_some() { - ignore_ids.pop(); + local_labels.push((b.hir_id, false)); } + let ret = never_loop_block(cx, b, local_labels, main_loop_id); + let jumped_to = l.is_some() && local_labels.pop().unwrap().1; match ret { - NeverLoopResult::IgnoreUntilEnd(a) if a == b.hir_id => NeverLoopResult::Otherwise, + NeverLoopResult::Diverging if jumped_to => NeverLoopResult::Normal, _ => ret, } }, @@ -211,74 +219,78 @@ fn never_loop_expr<'tcx>( if id == main_loop_id { NeverLoopResult::MayContinueMainLoop } else { - NeverLoopResult::AlwaysBreak + NeverLoopResult::Diverging } }, - // checks if break targets a block instead of a loop - ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e - .map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| { - never_loop_expr(cx, e, ignore_ids, main_loop_id) - }), - ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { - combine_seq( - never_loop_expr(cx, e, ignore_ids, main_loop_id), - NeverLoopResult::AlwaysBreak, - ) - }), - ExprKind::Become(e) => combine_seq( - never_loop_expr(cx, e, ignore_ids, main_loop_id), - NeverLoopResult::AlwaysBreak, - ), - ExprKind::InlineAsm(asm) => asm - .operands - .iter() - .map(|(o, _)| match o { - InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { - never_loop_expr(cx, expr, ignore_ids, main_loop_id) - }, - InlineAsmOperand::Out { expr, .. } => { - never_loop_expr_all(cx, &mut expr.iter().copied(), ignore_ids, main_loop_id) - }, - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all( - cx, - &mut once(*in_expr).chain(out_expr.iter().copied()), - ignore_ids, - main_loop_id, - ), - InlineAsmOperand::Const { .. } - | InlineAsmOperand::SymFn { .. } - | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, + ExprKind::Break(_, e) | ExprKind::Ret(e) => { + let first = e.as_ref().map_or(NeverLoopResult::Normal, |e| { + never_loop_expr(cx, e, local_labels, main_loop_id) + }); + combine_seq(first, || { + // checks if break targets a block instead of a loop + if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind { + if let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) { + *reachable = true; + } + } + NeverLoopResult::Diverging }) - .fold(NeverLoopResult::Otherwise, combine_seq), + }, + ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || { + NeverLoopResult::Diverging + }), + ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o { + InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { + never_loop_expr(cx, expr, local_labels, main_loop_id) + }, + InlineAsmOperand::Out { expr, .. } => { + never_loop_expr_all(cx, expr.iter().copied(), local_labels, main_loop_id) + }, + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all( + cx, + once(*in_expr).chain(out_expr.iter().copied()), + local_labels, + main_loop_id, + ), + InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { + NeverLoopResult::Normal + }, + })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) | ExprKind::Closure { .. } | ExprKind::Path(_) | ExprKind::ConstBlock(_) | ExprKind::Lit(_) - | ExprKind::Err(_) => NeverLoopResult::Otherwise, + | ExprKind::Err(_) => NeverLoopResult::Normal, + }; + let result = combine_seq(result, || { + if cx.typeck_results().expr_ty(expr).is_never() { + NeverLoopResult::Diverging + } else { + NeverLoopResult::Normal + } + }); + if let NeverLoopResult::Diverging = result && + let Some(macro_call) = root_macro_call_first_node(cx, expr) && + let Some(sym::todo_macro) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + // We return MayContinueMainLoop here because we treat `todo!()` + // as potentially containing any code, including a continue of the main loop. + // This effectively silences the lint whenever a loop contains this macro anywhere. + NeverLoopResult::MayContinueMainLoop + } else { + result } } fn never_loop_expr_all<'tcx, T: Iterator>>( cx: &LateContext<'tcx>, - es: &mut T, - ignore_ids: &mut Vec, - main_loop_id: HirId, -) -> NeverLoopResult { - es.map(|e| never_loop_expr(cx, e, ignore_ids, main_loop_id)) - .fold(NeverLoopResult::Otherwise, combine_seq) -} - -fn never_loop_expr_branch<'tcx, T: Iterator>>( - cx: &LateContext<'tcx>, - e: &mut T, - ignore_ids: &mut Vec, + es: T, + local_labels: &mut Vec<(HirId, bool)>, main_loop_id: HirId, ) -> NeverLoopResult { - e.fold(NeverLoopResult::AlwaysBreak, |a, b| { - combine_branches(a, never_loop_expr(cx, b, ignore_ids, main_loop_id), ignore_ids) - }) + combine_seq_many(es.map(|e| never_loop_expr(cx, e, local_labels, main_loop_id))) } fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String { diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs index 39d8b20d38d02..90557b5556085 100644 --- a/clippy_lints/src/manual_range_patterns.rs +++ b/clippy_lints/src/manual_range_patterns.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -6,6 +7,7 @@ use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{Span, DUMMY_SP}; declare_clippy_lint! { /// ### What it does @@ -49,6 +51,29 @@ fn expr_as_i128(expr: &Expr<'_>) -> Option { } } +#[derive(Copy, Clone)] +struct Num { + val: i128, + span: Span, +} + +impl Num { + fn new(expr: &Expr<'_>) -> Option { + Some(Self { + val: expr_as_i128(expr)?, + span: expr.span, + }) + } + + fn dummy(val: i128) -> Self { + Self { val, span: DUMMY_SP } + } + + fn min(self, other: Self) -> Self { + if self.val < other.val { self } else { other } + } +} + impl LateLintPass<'_> for ManualRangePatterns { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) { if in_external_macro(cx.sess(), pat.span) { @@ -56,71 +81,83 @@ impl LateLintPass<'_> for ManualRangePatterns { } // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives + // or at least one range if let PatKind::Or(pats) = pat.kind - && pats.len() >= 3 + && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))) { - let mut min = i128::MAX; - let mut max = i128::MIN; + let mut min = Num::dummy(i128::MAX); + let mut max = Num::dummy(i128::MIN); + let mut range_kind = RangeEnd::Included; let mut numbers_found = FxHashSet::default(); let mut ranges_found = Vec::new(); for pat in pats { if let PatKind::Lit(lit) = pat.kind - && let Some(num) = expr_as_i128(lit) + && let Some(num) = Num::new(lit) { - numbers_found.insert(num); + numbers_found.insert(num.val); min = min.min(num); - max = max.max(num); + if num.val >= max.val { + max = num; + range_kind = RangeEnd::Included; + } } else if let PatKind::Range(Some(left), Some(right), end) = pat.kind - && let Some(left) = expr_as_i128(left) - && let Some(right) = expr_as_i128(right) - && right >= left + && let Some(left) = Num::new(left) + && let Some(mut right) = Num::new(right) { + if let RangeEnd::Excluded = end { + right.val -= 1; + } + min = min.min(left); - max = max.max(right); - ranges_found.push(left..=match end { - RangeEnd::Included => right, - RangeEnd::Excluded => right - 1, - }); + if right.val > max.val { + max = right; + range_kind = end; + } + ranges_found.push(left.val..=right.val); } else { return; } } - let contains_whole_range = 'contains: { - let mut num = min; - while num <= max { - if numbers_found.contains(&num) { - num += 1; - } - // Given a list of (potentially overlapping) ranges like: - // 1..=5, 3..=7, 6..=10 - // We want to find the range with the highest end that still contains the current number - else if let Some(range) = ranges_found - .iter() - .filter(|range| range.contains(&num)) - .max_by_key(|range| range.end()) - { - num = range.end() + 1; - } else { - break 'contains false; - } + let mut num = min.val; + while num <= max.val { + if numbers_found.contains(&num) { + num += 1; + } + // Given a list of (potentially overlapping) ranges like: + // 1..=5, 3..=7, 6..=10 + // We want to find the range with the highest end that still contains the current number + else if let Some(range) = ranges_found + .iter() + .filter(|range| range.contains(&num)) + .max_by_key(|range| range.end()) + { + num = range.end() + 1; + } else { + return; } - break 'contains true; - }; - - if contains_whole_range { - span_lint_and_sugg( - cx, - MANUAL_RANGE_PATTERNS, - pat.span, - "this OR pattern can be rewritten using a range", - "try", - format!("{min}..={max}"), - Applicability::MachineApplicable, - ); } + + span_lint_and_then( + cx, + MANUAL_RANGE_PATTERNS, + pat.span, + "this OR pattern can be rewritten using a range", + |diag| { + if let Some(min) = snippet_opt(cx, min.span) + && let Some(max) = snippet_opt(cx, max.span) + { + diag.span_suggestion( + pat.span, + "try", + format!("{min}{range_kind}{max}"), + Applicability::MachineApplicable, + ); + } + }, + ); } } } diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs new file mode 100644 index 0000000000000..79c6d63254b28 --- /dev/null +++ b/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -0,0 +1,106 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::higher::VecArgs; +use clippy_utils::{expr_or_init, is_trait_method, match_def_path, paths}; +use rustc_ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self}; +use rustc_span::sym; + +use super::ITER_OUT_OF_BOUNDS; + +fn expr_as_u128(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { + if let ExprKind::Lit(lit) = expr_or_init(cx, e).kind + && let LitKind::Int(n, _) = lit.node + { + Some(n) + } else { + None + } +} + +/// Attempts to extract the length out of an iterator expression. +fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> Option { + let ty::Adt(adt, substs) = cx.typeck_results().expr_ty(iter).kind() else { + return None; + }; + let did = adt.did(); + + if match_def_path(cx, did, &paths::ARRAY_INTO_ITER) { + // For array::IntoIter, the length is the second generic + // parameter. + substs + .const_at(1) + .try_eval_target_usize(cx.tcx, cx.param_env) + .map(u128::from) + } else if match_def_path(cx, did, &paths::SLICE_ITER) + && let ExprKind::MethodCall(_, recv, ..) = iter.kind + { + if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() { + // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..) + len.try_eval_target_usize(cx.tcx, cx.param_env).map(u128::from) + } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) { + match args { + VecArgs::Vec(vec) => vec.len().try_into().ok(), + VecArgs::Repeat(_, len) => expr_as_u128(cx, len), + } + } else { + None + } + } else if match_def_path(cx, did, &paths::ITER_EMPTY) { + Some(0) + } else if match_def_path(cx, did, &paths::ITER_ONCE) { + Some(1) + } else { + None + } +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, + message: &'static str, + note: &'static str, +) { + if is_trait_method(cx, expr, sym::Iterator) + && let Some(len) = get_iterator_length(cx, recv) + && let Some(skipped) = expr_as_u128(cx, arg) + && skipped > len + { + span_lint_and_note(cx, ITER_OUT_OF_BOUNDS, expr.span, message, None, note); + } +} + +pub(super) fn check_skip<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, +) { + check( + cx, + expr, + recv, + arg, + "this `.skip()` call skips more items than the iterator will produce", + "this operation is useless and will create an empty iterator", + ); +} + +pub(super) fn check_take<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, +) { + check( + cx, + expr, + recv, + arg, + "this `.take()` call takes more items than the iterator will produce", + "this operation is useless and the returned iterator will simply yield the same items", + ); +} diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index ee405a3e30c3c..a49dd98db8713 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -21,7 +21,7 @@ pub(super) enum Op<'a> { RmCloned, // rm `.cloned()` - // e.g. `map` `for_each` + // e.g. `map` `for_each` `all` `any` NeedlessMove(&'a str, &'a Expr<'a>), // later `.cloned()` diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5075137caa0cb..81223fa8d954f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -43,6 +43,7 @@ mod iter_next_slice; mod iter_nth; mod iter_nth_zero; mod iter_on_single_or_empty_collections; +mod iter_out_of_bounds; mod iter_overeager_cloned; mod iter_skip_next; mod iter_skip_zero; @@ -3054,12 +3055,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// vec!(1, 2, 3, 4, 5).resize(0, 5) + /// vec![1, 2, 3, 4, 5].resize(0, 5) /// ``` /// /// Use instead: /// ```rust - /// vec!(1, 2, 3, 4, 5).clear() + /// vec![1, 2, 3, 4, 5].clear() /// ``` #[clippy::version = "1.46.0"] pub VEC_RESIZE_TO_ZERO, @@ -3538,6 +3539,30 @@ declare_clippy_lint! { "acquiring a write lock when a read lock would work" } +declare_clippy_lint! { + /// ### What it does + /// Looks for iterator combinator calls such as `.take(x)` or `.skip(x)` + /// where `x` is greater than the amount of items that an iterator will produce. + /// + /// ### Why is this bad? + /// Taking or skipping more items than there are in an iterator either creates an iterator + /// with all items from the original iterator or an iterator with no items at all. + /// This is most likely not what the user intended to do. + /// + /// ### Example + /// ```rust + /// for _ in [1, 2, 3].iter().take(4) {} + /// ``` + /// Use instead: + /// ```rust + /// for _ in [1, 2, 3].iter() {} + /// ``` + #[clippy::version = "1.74.0"] + pub ITER_OUT_OF_BOUNDS, + suspicious, + "calls to `.take()` or `.skip()` that are out of bounds" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3676,7 +3701,8 @@ impl_lint_pass!(Methods => [ STRING_LIT_CHARS_ANY, ITER_SKIP_ZERO, FILTER_MAP_BOOL_THEN, - READONLY_WRITE_LOCK + READONLY_WRITE_LOCK, + ITER_OUT_OF_BOUNDS, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3873,6 +3899,12 @@ impl Methods { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); }, + ("all", [arg]) => { + if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { + iter_overeager_cloned::check(cx, expr, recv, recv2, + iter_overeager_cloned::Op::NeedlessMove(name, arg), false); + } + } ("and_then", [arg]) => { let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); @@ -3880,12 +3912,16 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, - ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind - && let body = cx.tcx.hir().body(arg.body) - && let [param] = body.params - && let Some(("chars", recv, _, _, _)) = method_call(recv) => - { - string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + ("any", [arg]) => { + match method_call(recv) { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false), + Some(("chars", recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind + && let body = cx.tcx.hir().body(arg.body) + && let [param] = body.params => { + string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + } + _ => {} + } } ("arg", [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); @@ -4136,6 +4172,7 @@ impl Methods { }, ("skip", [arg]) => { iter_skip_zero::check(cx, expr, arg); + iter_out_of_bounds::check_skip(cx, expr, recv, arg); if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check(cx, expr, recv, recv2, @@ -4163,7 +4200,8 @@ impl Methods { } }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("take", [_arg]) => { + ("take", [arg]) => { + iter_out_of_bounds::check_take(cx, expr, recv, arg); if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned, false); diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs new file mode 100644 index 0000000000000..08fec2b8ec820 --- /dev/null +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -0,0 +1,391 @@ +use std::mem; +use std::ops::ControlFlow; + +use clippy_utils::comparisons::{normalize_comparison, Rel}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{eq_expr_value, hash_expr, higher}; +use rustc_ast::{LitKind, RangeLimits}; +use rustc_data_structures::unhash::UnhashMap; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir::{BinOp, Block, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for repeated slice indexing without asserting beforehand that the length + /// is greater than the largest index used to index into the slice. + /// + /// ### Why is this bad? + /// In the general case where the compiler does not have a lot of information + /// about the length of a slice, indexing it repeatedly will generate a bounds check + /// for every single index. + /// + /// Asserting that the length of the slice is at least as large as the largest value + /// to index beforehand gives the compiler enough information to elide the bounds checks, + /// effectively reducing the number of bounds checks from however many times + /// the slice was indexed to just one (the assert). + /// + /// ### Drawbacks + /// False positives. It is, in general, very difficult to predict how well + /// the optimizer will be able to elide bounds checks and it very much depends on + /// the surrounding code. For example, indexing into the slice yielded by the + /// [`slice::chunks_exact`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.chunks_exact) + /// iterator will likely have all of the bounds checks elided even without an assert + /// if the `chunk_size` is a constant. + /// + /// Asserts are not tracked across function calls. Asserting the length of a slice + /// in a different function likely gives the optimizer enough information + /// about the length of a slice, but this lint will not detect that. + /// + /// ### Example + /// ```rust + /// fn sum(v: &[u8]) -> u8 { + /// // 4 bounds checks + /// v[0] + v[1] + v[2] + v[3] + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn sum(v: &[u8]) -> u8 { + /// assert!(v.len() > 4); + /// // no bounds checks + /// v[0] + v[1] + v[2] + v[3] + /// } + /// ``` + #[clippy::version = "1.70.0"] + pub MISSING_ASSERTS_FOR_INDEXING, + restriction, + "indexing into a slice multiple times without an `assert`" +} +declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]); + +fn report_lint(cx: &LateContext<'_>, full_span: Span, msg: &str, indexes: &[Span], f: F) +where + F: FnOnce(&mut Diagnostic), +{ + span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, full_span, msg, |diag| { + f(diag); + for span in indexes { + diag.span_note(*span, "slice indexed here"); + } + diag.note("asserting the length before indexing will elide bounds checks"); + }); +} + +#[derive(Copy, Clone, Debug)] +enum LengthComparison { + /// `v.len() < 5` + LengthLessThanInt, + /// `5 < v.len()` + IntLessThanLength, + /// `v.len() <= 5` + LengthLessThanOrEqualInt, + /// `5 <= v.len()` + IntLessThanOrEqualLength, +} + +/// Extracts parts out of a length comparison expression. +/// +/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, `v.len()`))` +fn len_comparison<'hir>( + bin_op: BinOp, + left: &'hir Expr<'hir>, + right: &'hir Expr<'hir>, +) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { + macro_rules! int_lit_pat { + ($id:ident) => { + ExprKind::Lit(Spanned { + node: LitKind::Int($id, _), + .. + }) + }; + } + + // normalize comparison, `v.len() > 4` becomes `4 < v.len()` + // this simplifies the logic a bit + let (op, left, right) = normalize_comparison(bin_op.node, left, right)?; + match (op, &left.kind, &right.kind) { + (Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, *left as usize, right)), + (Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, *right as usize, left)), + (Rel::Le, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanOrEqualLength, *left as usize, right)), + (Rel::Le, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanOrEqualInt, *right as usize, left)), + _ => None, + } +} + +/// Attempts to extract parts out of an `assert!`-like expression +/// in the form `assert!(some_slice.len() > 5)`. +/// +/// `assert!` has expanded to an if expression at the HIR, so this +/// actually works not just with `assert!` specifically, but anything +/// that has a never type expression in the `then` block (e.g. `panic!`). +fn assert_len_expr<'hir>( + cx: &LateContext<'_>, + expr: &'hir Expr<'hir>, +) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { + if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind + && let ExprKind::Binary(bin_op, left, right) = &condition.kind + + && let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right) + && let ExprKind::MethodCall(method, recv, ..) = &slice_len.kind + && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() + && method.ident.name == sym::len + + // check if `then` block has a never type expression + && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind + && cx.typeck_results().expr_ty(then_expr).is_never() + { + Some((cmp, asserted_len, recv)) + } else { + None + } +} + +#[derive(Debug)] +enum IndexEntry<'hir> { + /// `assert!` without any indexing (so far) + StrayAssert { + asserted_len: usize, + comparison: LengthComparison, + assert_span: Span, + slice: &'hir Expr<'hir>, + }, + /// `assert!` with indexing + /// + /// We also store the highest index to be able to check + /// if the `assert!` asserts the right length. + AssertWithIndex { + highest_index: usize, + asserted_len: usize, + assert_span: Span, + slice: &'hir Expr<'hir>, + indexes: Vec, + comparison: LengthComparison, + }, + /// Indexing without an `assert!` + IndexWithoutAssert { + highest_index: usize, + indexes: Vec, + slice: &'hir Expr<'hir>, + }, +} + +impl<'hir> IndexEntry<'hir> { + pub fn slice(&self) -> &'hir Expr<'hir> { + match self { + IndexEntry::StrayAssert { slice, .. } + | IndexEntry::AssertWithIndex { slice, .. } + | IndexEntry::IndexWithoutAssert { slice, .. } => slice, + } + } + + pub fn index_spans(&self) -> Option<&[Span]> { + match self { + IndexEntry::StrayAssert { .. } => None, + IndexEntry::AssertWithIndex { indexes, .. } | IndexEntry::IndexWithoutAssert { indexes, .. } => { + Some(indexes) + }, + } + } +} + +/// Extracts the upper index of a slice indexing expression. +/// +/// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`, +/// for `..=5` this returns `Some(5)` +fn upper_index_expr(expr: &Expr<'_>) -> Option { + if let ExprKind::Lit(lit) = &expr.kind && let LitKind::Int(index, _) = lit.node { + Some(index as usize) + } else if let Some(higher::Range { end: Some(end), limits, .. }) = higher::Range::hir(expr) + && let ExprKind::Lit(lit) = &end.kind + && let LitKind::Int(index @ 1.., _) = lit.node + { + match limits { + RangeLimits::HalfOpen => Some(index as usize - 1), + RangeLimits::Closed => Some(index as usize), + } + } else { + None + } +} + +/// Checks if the expression is an index into a slice and adds it to `indexes` +fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap>>) { + if let ExprKind::Index(slice, index_lit, _) = expr.kind + && cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice() + && let Some(index) = upper_index_expr(index_lit) + { + let hash = hash_expr(cx, slice); + + let indexes = map.entry(hash).or_default(); + let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice)); + + if let Some(entry) = entry { + match entry { + IndexEntry::StrayAssert { asserted_len, comparison, assert_span, slice } => { + *entry = IndexEntry::AssertWithIndex { + highest_index: index, + asserted_len: *asserted_len, + assert_span: *assert_span, + slice, + indexes: vec![expr.span], + comparison: *comparison, + }; + }, + IndexEntry::IndexWithoutAssert { highest_index, indexes, .. } + | IndexEntry::AssertWithIndex { highest_index, indexes, .. } => { + indexes.push(expr.span); + *highest_index = (*highest_index).max(index); + }, + } + } else { + indexes.push(IndexEntry::IndexWithoutAssert { + highest_index: index, + indexes: vec![expr.span], + slice, + }); + } + } +} + +/// Checks if the expression is an `assert!` expression and adds it to `asserts` +fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap>>) { + if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) { + let hash = hash_expr(cx, slice); + let indexes = map.entry(hash).or_default(); + + let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice)); + + if let Some(entry) = entry { + if let IndexEntry::IndexWithoutAssert { + highest_index, + indexes, + slice, + } = entry + { + *entry = IndexEntry::AssertWithIndex { + highest_index: *highest_index, + indexes: mem::take(indexes), + slice, + assert_span: expr.span, + comparison, + asserted_len, + }; + } + } else { + indexes.push(IndexEntry::StrayAssert { + asserted_len, + comparison, + assert_span: expr.span, + slice, + }); + } + } +} + +/// Inspects indexes and reports lints. +/// +/// Called at the end of this lint after all indexing and `assert!` expressions have been collected. +fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap>>) { + for bucket in map.values() { + for entry in bucket { + let Some(full_span) = entry + .index_spans() + .and_then(|spans| spans.first().zip(spans.last())) + .map(|(low, &high)| low.to(high)) + else { + continue; + }; + + match entry { + IndexEntry::AssertWithIndex { + highest_index, + asserted_len, + indexes, + comparison, + assert_span, + slice, + } if indexes.len() > 1 => { + // if we have found an `assert!`, let's also check that it's actually right + // and if it convers the highest index and if not, suggest the correct length + let sugg = match comparison { + // `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks. + // The user probably meant `v.len() > 5` + LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => Some( + format!("assert!({}.len() > {highest_index})", snippet(cx, slice.span, "..")), + ), + // `5 < v.len()` == `v.len() > 5` + LengthComparison::IntLessThanLength if asserted_len < highest_index => Some(format!( + "assert!({}.len() > {highest_index})", + snippet(cx, slice.span, "..") + )), + // `5 <= v.len() == `v.len() >= 5` + LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => Some(format!( + "assert!({}.len() > {highest_index})", + snippet(cx, slice.span, "..") + )), + _ => None, + }; + + if let Some(sugg) = sugg { + report_lint( + cx, + full_span, + "indexing into a slice multiple times with an `assert` that does not cover the highest index", + indexes, + |diag| { + diag.span_suggestion( + *assert_span, + "provide the highest index that is indexed with", + sugg, + Applicability::MachineApplicable, + ); + }, + ); + } + }, + IndexEntry::IndexWithoutAssert { + indexes, + highest_index, + slice, + } if indexes.len() > 1 => { + // if there was no `assert!` but more than one index, suggest + // adding an `assert!` that covers the highest index + report_lint( + cx, + full_span, + "indexing into a slice multiple times without an `assert`", + indexes, + |diag| { + diag.help(format!( + "consider asserting the length before indexing: `assert!({}.len() > {highest_index});`", + snippet(cx, slice.span, "..") + )); + }, + ); + }, + _ => {}, + } + } + } +} + +impl LateLintPass<'_> for MissingAssertsForIndexing { + fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { + let mut map = UnhashMap::default(); + + for_each_expr(block, |expr| { + check_index(cx, expr, &mut map); + check_assert(cx, expr, &mut map); + ControlFlow::::Continue(()) + }); + + report_indexes(cx, &map); + } +} diff --git a/clippy_lints/src/incorrect_impls.rs b/clippy_lints/src/non_canonical_impls.rs similarity index 88% rename from clippy_lints/src/incorrect_impls.rs rename to clippy_lints/src/non_canonical_impls.rs index 3c59b839a39c6..4b24f059afdf5 100644 --- a/clippy_lints/src/incorrect_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -13,11 +13,12 @@ use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does - /// Checks for manual implementations of `Clone` when `Copy` is already implemented. + /// Checks for non-canonical implementations of `Clone` when `Copy` is already implemented. /// /// ### Why is this bad? - /// If both `Clone` and `Copy` are implemented, they must agree. This is done by dereferencing - /// `self` in `Clone`'s implementation. Anything else is incorrect. + /// If both `Clone` and `Copy` are implemented, they must agree. This can done by dereferencing + /// `self` in `Clone`'s implementation, which will avoid any possibility of the implementations + /// becoming out of sync. /// /// ### Example /// ```rust,ignore @@ -46,14 +47,13 @@ declare_clippy_lint! { /// impl Copy for A {} /// ``` #[clippy::version = "1.72.0"] - pub INCORRECT_CLONE_IMPL_ON_COPY_TYPE, - correctness, - "manual implementation of `Clone` on a `Copy` type" + pub NON_CANONICAL_CLONE_IMPL, + suspicious, + "non-canonical implementation of `Clone` on a `Copy` type" } declare_clippy_lint! { /// ### What it does - /// Checks for manual implementations of both `PartialOrd` and `Ord` when only `Ord` is - /// necessary. + /// Checks for non-canonical implementations of `PartialOrd` when `Ord` is already implemented. /// /// ### Why is this bad? /// If both `PartialOrd` and `Ord` are implemented, they must agree. This is commonly done by @@ -61,11 +61,8 @@ declare_clippy_lint! { /// introduce an error upon refactoring. /// /// ### Known issues - /// Code that calls the `.into()` method instead will be flagged as incorrect, despite `.into()` - /// wrapping it in `Some`. - /// - /// ### Limitations - /// Will not lint if `Self` and `Rhs` do not have the same type. + /// Code that calls the `.into()` method instead will be flagged, despite `.into()` wrapping it + /// in `Some`. /// /// ### Example /// ```rust @@ -107,13 +104,13 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "1.72.0"] - pub INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE, - correctness, - "manual implementation of `PartialOrd` when `Ord` is already implemented" + pub NON_CANONICAL_PARTIAL_ORD_IMPL, + suspicious, + "non-canonical implementation of `PartialOrd` on an `Ord` type" } -declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE, INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE]); +declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); -impl LateLintPass<'_> for IncorrectImpls { +impl LateLintPass<'_> for NonCanonicalImpls { #[expect(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { let Some(Node::Item(item)) = get_parent_node(cx.tcx, impl_item.hir_id()) else { @@ -154,9 +151,9 @@ impl LateLintPass<'_> for IncorrectImpls { {} else { span_lint_and_sugg( cx, - INCORRECT_CLONE_IMPL_ON_COPY_TYPE, + NON_CANONICAL_CLONE_IMPL, block.span, - "incorrect implementation of `clone` on a `Copy` type", + "non-canonical implementation of `clone` on a `Copy` type", "change this to", "{ *self }".to_owned(), Applicability::MaybeIncorrect, @@ -169,9 +166,9 @@ impl LateLintPass<'_> for IncorrectImpls { if impl_item.ident.name == sym::clone_from { span_lint_and_sugg( cx, - INCORRECT_CLONE_IMPL_ON_COPY_TYPE, + NON_CANONICAL_CLONE_IMPL, impl_item.span, - "incorrect implementation of `clone_from` on a `Copy` type", + "unnecessary implementation of `clone_from` on a `Copy` type", "remove it", String::new(), Applicability::MaybeIncorrect, @@ -222,9 +219,9 @@ impl LateLintPass<'_> for IncorrectImpls { span_lint_and_then( cx, - INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE, + NON_CANONICAL_PARTIAL_ORD_IMPL, item.span, - "incorrect implementation of `partial_cmp` on an `Ord` type", + "non-canonical implementation of `partial_cmp` on an `Ord` type", |diag| { let [_, other] = body.params else { return; diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index a687c7d29b56e..8072aded8513b 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -14,7 +14,12 @@ use {rustc_ast as ast, rustc_hir as hir}; const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]]; const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; -const INTEGER_METHODS: &[Symbol] = &[sym::saturating_div, sym::wrapping_div, sym::wrapping_rem, sym::wrapping_rem_euclid]; +const INTEGER_METHODS: &[Symbol] = &[ + sym::saturating_div, + sym::wrapping_div, + sym::wrapping_rem, + sym::wrapping_rem_euclid, +]; #[derive(Debug)] pub struct ArithmeticSideEffects { @@ -93,7 +98,14 @@ impl ArithmeticSideEffects { let is_non_zero_u = |symbol: Option| { matches!( symbol, - Some(sym::NonZeroU128 | sym::NonZeroU16 | sym::NonZeroU32 | sym::NonZeroU64 | sym::NonZeroU8 | sym::NonZeroUsize) + Some( + sym::NonZeroU128 + | sym::NonZeroU16 + | sym::NonZeroU32 + | sym::NonZeroU64 + | sym::NonZeroU8 + | sym::NonZeroUsize + ) ) }; let is_sat_or_wrap = |ty: Ty<'_>| { diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index f3e0c58a78711..bce6bdcaf61cd 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -17,7 +17,7 @@ pub(crate) fn check<'tcx>( left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { - if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { + if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index ccabb577cb722..e8018462d75f6 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -1,7 +1,7 @@ use std::iter::once; use std::ops::ControlFlow; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; @@ -9,6 +9,7 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{BytePos, Pos, Span}; declare_clippy_lint! { /// ### What it does @@ -76,14 +77,33 @@ impl EarlyLintPass for RawStrings { } if !str.contains(['\\', '"']) { - span_lint_and_sugg( + span_lint_and_then( cx, NEEDLESS_RAW_STRINGS, expr.span, "unnecessary raw string literal", - "try", - format!("{}\"{}\"", prefix.replace('r', ""), lit.symbol), - Applicability::MachineApplicable, + |diag| { + let (start, end) = hash_spans(expr.span, prefix, 0, max); + + // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text + let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1); + let start = start.with_lo(r_pos); + + if end.is_empty() { + diag.span_suggestion( + start, + "use a string literal instead", + format!("\"{}\"", str), + Applicability::MachineApplicable, + ); + } else { + diag.multipart_suggestion( + "try", + vec![(start, String::new()), (end, String::new())], + Applicability::MachineApplicable, + ); + } + }, ); return; @@ -96,13 +116,6 @@ impl EarlyLintPass for RawStrings { let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| { match b { b'"' if !following_quote => (following_quote, req) = (true, 1), - // I'm a bit surprised the compiler didn't optimize this out, there's no - // branch but it still ends up doing an unnecessary comparison, it's: - // - cmp r9b,1h - // - sbb cl,-1h - // which will add 1 if it's true. With this change, it becomes: - // - add cl,r9b - // isn't that so much nicer? b'#' => req += u8::from(following_quote), _ => { if following_quote { @@ -126,18 +139,58 @@ impl EarlyLintPass for RawStrings { }; if req < max { - let hashes = "#".repeat(req as usize); - - span_lint_and_sugg( + span_lint_and_then( cx, NEEDLESS_RAW_STRING_HASHES, expr.span, "unnecessary hashes around raw string literal", - "try", - format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol), - Applicability::MachineApplicable, + |diag| { + let (start, end) = hash_spans(expr.span, prefix, req, max); + + let message = match max - req { + _ if req == 0 => "remove all the hashes around the literal".to_string(), + 1 => "remove one hash from both sides of the literal".to_string(), + n => format!("remove {n} hashes from both sides of the literal"), + }; + + diag.multipart_suggestion( + message, + vec![(start, String::new()), (end, String::new())], + Applicability::MachineApplicable, + ); + }, ); } } } } + +/// Returns spans pointing at the unneeded hashes, e.g. for a `req` of `1` and `max` of `3`: +/// +/// ```ignore +/// r###".."### +/// ^^ ^^ +/// ``` +fn hash_spans(literal_span: Span, prefix: &str, req: u8, max: u8) -> (Span, Span) { + let literal_span = literal_span.data(); + + // BytePos: we checked prefix appears literally in the source text + let hash_start = literal_span.lo + BytePos::from_usize(prefix.len()); + let hash_end = literal_span.hi; + + // BytePos: req/max are counts of the ASCII character # + let start = Span::new( + hash_start + BytePos(req.into()), + hash_start + BytePos(max.into()), + literal_span.ctxt, + None, + ); + let end = Span::new( + hash_end - BytePos(req.into()), + hash_end - BytePos(max.into()), + literal_span.ctxt, + None, + ); + + (start, end) +} diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index fc1fabcc0ae59..613f1ecc6fbee 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -15,6 +15,8 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), + ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"), + ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"), ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), ("clippy::new_without_default_derive", "clippy::new_without_default"), @@ -38,12 +40,12 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::drop_bounds", "drop_bounds"), ("clippy::drop_copy", "dropping_copy_types"), ("clippy::drop_ref", "dropping_references"), + ("clippy::fn_null_check", "useless_ptr_null_checks"), ("clippy::for_loop_over_option", "for_loops_over_fallibles"), ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::forget_copy", "forgetting_copy_types"), ("clippy::forget_ref", "forgetting_references"), - ("clippy::fn_null_check", "useless_ptr_null_checks"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index c9ab622ad25d3..9db18c2976c22 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, @@ -148,6 +149,15 @@ impl SlowVectorInit { /// - `Some(InitializedSize::Uninitialized)` for `Vec::new()` /// - `None` for other, unrelated kinds of expressions fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option> { + // Generally don't warn if the vec initializer comes from an expansion, except for the vec! macro. + // This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an + // empty vec + if expr.span.from_expansion() + && root_macro_call(expr.span).map(|m| m.def_id) != cx.tcx.get_diagnostic_item(sym::vec_macro) + { + return None; + } + if let ExprKind::Call(func, [len_expr]) = expr.kind && is_expr_path_def_path(cx, func, &paths::VEC_WITH_CAPACITY) { @@ -205,7 +215,7 @@ impl SlowVectorInit { span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { diag.span_suggestion( - vec_alloc.allocation_expr.span, + vec_alloc.allocation_expr.span.source_callsite(), "consider replacing this with", format!("vec![0; {len_expr}]"), Applicability::Unspecified, diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index f239165276ffe..5f54a10d1c41d 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, Path, PathSegment}; @@ -99,17 +100,17 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { && let Some(first_segment) = get_first_segment(path) && is_stable(cx, def_id) { - let (lint, msg, help) = match first_segment.ident.name { + let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { sym::core => ( STD_INSTEAD_OF_CORE, - "used import from `std` instead of `core`", - "consider importing the item from `core`", + "std", + "core", ), sym::alloc => ( STD_INSTEAD_OF_ALLOC, - "used import from `std` instead of `alloc`", - "consider importing the item from `alloc`", + "std", + "alloc", ), _ => { self.prev_span = path.span; @@ -120,8 +121,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if cx.tcx.crate_name(def_id.krate) == sym::core { ( ALLOC_INSTEAD_OF_CORE, - "used import from `alloc` instead of `core`", - "consider importing the item from `core`", + "alloc", + "core", ) } else { self.prev_span = path.span; @@ -131,7 +132,14 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { _ => return, }; if path.span != self.prev_span { - span_lint_and_help(cx, lint, path.span, msg, None, help); + span_lint_and_sugg( + cx, + lint, + first_segment.ident.span, + &format!("used import from `{used_mod}` instead of `{replace_with}`"), + &format!("consider importing the item from `{replace_with}`"), + replace_with.to_string(), + Applicability::MachineApplicable); self.prev_span = path.span; } } diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index da8d8ed4c0f2c..6193fdeb433a2 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -341,44 +341,21 @@ fn block_parents_have_safety_comment( id: hir::HirId, ) -> bool { if let Some(node) = get_parent_node(cx.tcx, id) { - return match node { - Node::Expr(expr) => { - if let Some( - Node::Local(hir::Local { span, .. }) - | Node::Item(hir::Item { - kind: hir::ItemKind::Const(..) | ItemKind::Static(..), - span, - .. - }), - ) = get_parent_node(cx.tcx, expr.hir_id) - { - let hir_id = match get_parent_node(cx.tcx, expr.hir_id) { - Some(Node::Local(hir::Local { hir_id, .. })) => *hir_id, - Some(Node::Item(hir::Item { owner_id, .. })) => { - cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id) - }, - _ => unreachable!(), - }; - - // if unsafe block is part of a let/const/static statement, - // and accept_comment_above_statement is set to true - // we accept the safety comment in the line the precedes this statement. - accept_comment_above_statement - && span_with_attrs_in_body_has_safety_comment( - cx, - *span, - hir_id, - accept_comment_above_attributes, - ) - } else { - !is_branchy(expr) - && span_with_attrs_in_body_has_safety_comment( - cx, - expr.span, - expr.hir_id, - accept_comment_above_attributes, - ) - } + let (span, hir_id) = match node { + Node::Expr(expr) => match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Local(hir::Local { span, hir_id, .. })) => (*span, *hir_id), + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + span, + owner_id, + .. + })) => (*span, cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)), + _ => { + if is_branchy(expr) { + return false; + } + (expr.span, expr.hir_id) + }, }, Node::Stmt(hir::Stmt { kind: @@ -387,28 +364,27 @@ fn block_parents_have_safety_comment( | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), .. }) - | Node::Local(hir::Local { span, hir_id, .. }) => { - span_with_attrs_in_body_has_safety_comment(cx, *span, *hir_id, accept_comment_above_attributes) - }, + | Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id), Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), span, owner_id, .. - }) => span_with_attrs_in_body_has_safety_comment( - cx, - *span, - cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id), - accept_comment_above_attributes, - ), - _ => false, + }) => (*span, cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)), + _ => return false, }; + // if unsafe block is part of a let/const/static statement, + // and accept_comment_above_statement is set to true + // we accept the safety comment in the line the precedes this statement. + accept_comment_above_statement + && span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes) + } else { + false } - false } /// Extends `span` to also include its attributes, then checks if that span has a safety comment. -fn span_with_attrs_in_body_has_safety_comment( +fn span_with_attrs_has_safety_comment( cx: &LateContext<'_>, span: Span, hir_id: HirId, @@ -420,7 +396,7 @@ fn span_with_attrs_in_body_has_safety_comment( span }; - span_in_body_has_safety_comment(cx, span) + span_has_safety_comment(cx, span) } /// Checks if an expression is "branchy", e.g. loop, match/if/etc. @@ -444,7 +420,7 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { matches!( span_from_macro_expansion_has_safety_comment(cx, span), HasSafetyComment::Yes(_) - ) || span_in_body_has_safety_comment(cx, span) + ) || span_has_safety_comment(cx, span) } fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { @@ -633,29 +609,36 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option { let body = cx.enclosing_body?; let map = cx.tcx.hir(); let mut span = map.body(body).value.span; + let mut maybe_global_var = false; for (_, node) in map.parent_iter(body.hir_id) { match node { Node::Expr(e) => span = e.span, - Node::Block(_) - | Node::Arm(_) - | Node::Stmt(_) - | Node::Local(_) - | Node::Item(hir::Item { + Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (), + Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), .. - }) => (), + }) => maybe_global_var = true, + Node::Item(hir::Item { + kind: hir::ItemKind::Mod(_), + span: item_span, + .. + }) => { + span = *item_span; + break; + }, + Node::Crate(mod_) if maybe_global_var => { + span = mod_.spans.inner_span; + }, _ => break, } } Some(span) } -fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); - if ctxt == SyntaxContext::root() - && let Some(search_span) = get_body_search_span(cx) - { + if ctxt.is_root() && let Some(search_span) = get_body_search_span(cx) { if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root()) && let Ok(body_line) = source_map.lookup_line(body_span.lo()) diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index dd829ded0d0d6..de4b8738e35b9 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let mut twins = vec!((1, 1), (2, 2)); + /// let mut twins = vec![(1, 1), (2, 2)]; /// twins.sort_by_key(|x| { x.1; }); /// ``` #[clippy::version = "1.47.0"] diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index c99b0290c0c0e..9a0d83d83f1f7 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,15 +1,18 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::is_potentially_mutated; +use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{higher, path_to_local}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, PathSegment, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Ty; +use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -192,6 +195,55 @@ fn collect_unwrap_info<'tcx>( Vec::new() } +/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated, +/// *except* for if `Option::as_mut` is called. +/// The reason for why we allow that one specifically is that `.as_mut()` cannot change +/// the option to `None`, and that is important because this lint relies on the fact that +/// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if +/// the option is changed to None between `is_some` and `unwrap`. +/// (And also `.as_mut()` is a somewhat common method that is still worth linting on.) +struct MutationVisitor<'tcx> { + is_mutated: bool, + local_id: HirId, + tcx: TyCtxt<'tcx>, +} + +/// Checks if the parent of the expression pointed at by the given `HirId` is a call to +/// `Option::as_mut`. +/// +/// Used by the mutation visitor to specifically allow `.as_mut()` calls. +/// In particular, the `HirId` that the visitor receives is the id of the local expression +/// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent +/// expression: that will be where the actual method call is. +fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { + if let Node::Expr(mutating_expr) = tcx.hir().get_parent(expr_id) + && let ExprKind::MethodCall(path, ..) = mutating_expr.kind + { + path.ident.name.as_str() == "as_mut" + } else { + false + } +} + +impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { + fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { + if let ty::BorrowKind::MutBorrow = bk + && is_potentially_local_place(self.local_id, &cat.place) + && !is_option_as_mut_use(self.tcx, diag_expr_id) + { + self.is_mutated = true; + } + } + + fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) { + self.is_mutated = true; + } + + fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} +} + impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { fn visit_branch( &mut self, @@ -202,10 +254,26 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { ) { let prev_len = self.unwrappables.len(); for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { - if is_potentially_mutated(unwrap_info.local_id, cond, self.cx) - || is_potentially_mutated(unwrap_info.local_id, branch, self.cx) - { - // if the variable is mutated, we don't know whether it can be unwrapped: + let mut delegate = MutationVisitor { + tcx: self.cx.tcx, + is_mutated: false, + local_id: unwrap_info.local_id, + }; + + let infcx = self.cx.tcx.infer_ctxt().build(); + let mut vis = ExprUseVisitor::new( + &mut delegate, + &infcx, + cond.hir_id.owner.def_id, + self.cx.param_env, + self.cx.typeck_results(), + ); + vis.walk_expr(cond); + vis.walk_expr(branch); + + if delegate.is_mutated { + // if the variable is mutated, we don't know whether it can be unwrapped. + // it might have been changed to `None` in between `is_some` + `unwrap`. continue; } self.unwrappables.push(unwrap_info); @@ -215,6 +283,27 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { } } +enum AsRefKind { + AsRef, + AsMut, +} + +/// Checks if the expression is a method call to `as_{ref,mut}` and returns the receiver of it. +/// If it isn't, the expression itself is returned. +fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option) { + if let ExprKind::MethodCall(path, recv, ..) = expr.kind { + if path.ident.name == sym::as_ref { + (recv, Some(AsRefKind::AsRef)) + } else if path.ident.name.as_str() == "as_mut" { + (recv, Some(AsRefKind::AsMut)) + } else { + (expr, None) + } + } else { + (expr, None) + } +} + impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; @@ -233,6 +322,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { // find `unwrap[_err]()` calls: if_chain! { if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind; + let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg); if let Some(id) = path_to_local(self_arg); if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name); let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name); @@ -268,7 +358,12 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), "try", format!( - "if let {suggested_pattern} = {unwrappable_variable_name}", + "if let {suggested_pattern} = {borrow_prefix}{unwrappable_variable_name}", + borrow_prefix = match as_ref_kind { + Some(AsRefKind::AsRef) => "&", + Some(AsRefKind::AsMut) => "&mut ", + None => "", + }, ), // We don't track how the unwrapped value is used inside the // block or suggest deleting the unwrap, so we can't offer a diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 58ae0656db73e..2688947552253 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -561,6 +561,26 @@ define_Conf! { /// Which crates to allow absolute paths from (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet = rustc_data_structures::fx::FxHashSet::default()), + /// Lint: EXPLICIT_ITER_LOOP + /// + /// Whether to recommend using implicit into iter for reborrowed values. + /// + /// #### Example + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// let rmvec = &mut vec; + /// for _ in rmvec.iter() {} + /// for _ in rmvec.iter_mut() {} + /// ``` + /// + /// Use instead: + /// ``` + /// let mut vec = vec![1, 2, 3]; + /// let rmvec = &mut vec; + /// for _ in &*rmvec {} + /// for _ in &mut *rmvec {} + /// ``` + (enforce_iter_loop_reborrow: bool = false), } /// Search for the configuration file. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6c4cec5952471..4ef3ec19647e9 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -5,6 +5,7 @@ #![feature(lint_reasons)] #![feature(never_type)] #![feature(rustc_private)] +#![feature(assert_matches)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] @@ -110,6 +111,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, Span}; use rustc_target::abi::Integer; +use visitors::Visitable; use crate::consts::{constant, miri_to_const, Constant}; use crate::higher::Range; @@ -1286,7 +1288,7 @@ pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext< } /// Returns `true` if `expr` contains a return expression -pub fn contains_return(expr: &hir::Expr<'_>) -> bool { +pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool { for_each_expr(expr, |e| { if matches!(e.kind, hir::ExprKind::Ret(..)) { ControlFlow::Break(()) diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index a567c5cbdc92a..2fb24b5c7ed9d 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -49,6 +49,7 @@ pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; +pub const ITER_ONCE: [&str; 5] = ["core", "iter", "sources", "once", "Once"]; pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -163,3 +164,4 @@ pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"]; pub const ORD_CMP: [&str; 4] = ["core", "cmp", "Ord", "cmp"]; #[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so pub const BOOL_THEN: [&str; 4] = ["core", "bool", "", "then"]; +pub const ARRAY_INTO_ITER: [&str; 4] = ["core", "array", "iter", "IntoIter"]; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 600cd084c19f5..31cb421095ede 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -362,7 +362,7 @@ pub fn snippet_block_with_context<'a>( } /// Same as `snippet_with_applicability`, but first walks the span up to the given context. This -/// will result in the macro call, rather then the expansion, if the span is from a child context. +/// will result in the macro call, rather than the expansion, if the span is from a child context. /// If the span is not from a child context, it will be used directly instead. /// /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index ee5a49a207331..ae8ee371ffae3 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -88,7 +88,7 @@ impl<'a> Sugg<'a> { } /// Same as `hir`, but first walks the span up to the given context. This will result in the - /// macro call, rather then the expansion, if the span is from a child context. If the span is + /// macro call, rather than the expansion, if the span is from a child context. If the span is /// not from a child context, it will be used directly instead. /// /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a05f682aa8cda..f0b4ede35fbc5 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -27,6 +27,7 @@ use rustc_target::abi::{Size, VariantIdx}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; +use std::assert_matches::debug_assert_matches; use std::iter; use crate::{match_def_path, path_res, paths}; @@ -259,7 +260,11 @@ pub fn implements_trait_with_env_from_iter<'tcx>( })), ); - debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait); + debug_assert_matches!( + tcx.def_kind(trait_id), + DefKind::Trait | DefKind::TraitAlias, + "`DefId` must belong to a trait or trait alias" + ); #[cfg(debug_assertions)] assert_generic_args_match(tcx, trait_id, trait_ref.args); diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 06fd952904405..dc7756533acb0 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -150,7 +150,7 @@ fn generic_args_certainty(cx: &LateContext<'_>, args: &GenericArgs<'_>) -> Certa } /// Tries to tell whether a `QPath` resolves to something certain, e.g., whether all of its path -/// segments generic arguments are are instantiated. +/// segments generic arguments are instantiated. /// /// `qpath` could refer to either a type or a value. The heuristic never needs the `DefId` of a /// value. So `DefId`s are retained only when `resolves_to_type` is true. diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 39ef76348d751..ec131c7f6a318 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -4,7 +4,7 @@ use core::ops::ControlFlow; use hir::def::Res; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir, Expr, ExprKind, HirId, HirIdSet}; -use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, Place, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -37,6 +37,17 @@ pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: & mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable)) } +pub fn is_potentially_local_place(local_id: HirId, place: &Place<'_>) -> bool { + match place.base { + PlaceBase::Local(id) => id == local_id, + PlaceBase::Upvar(_) => { + // Conservatively assume yes. + true + }, + _ => false, + } +} + struct MutVarsDelegate { used_mutably: HirIdSet, skip: bool, diff --git a/rust-toolchain b/rust-toolchain index 19c09b58b9862..9f5116eb73bb2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-08-24" +channel = "nightly-2023-09-07" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c1c3d4a868f92..9fcc269dbf8c2 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -4,7 +4,7 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] -use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling, RustfixMode}; +use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling}; use std::collections::BTreeMap; use std::env::{self, set_var, var_os}; @@ -114,36 +114,26 @@ fn canonicalize(path: impl AsRef) -> PathBuf { } fn base_config(test_dir: &str) -> (Config, Args) { - let bless = var_os("RUSTC_BLESS").is_some_and(|v| v != "0") || env::args().any(|arg| arg == "--bless"); - - let args = Args { - filters: env::var("TESTNAME") - .map(|filters| filters.split(',').map(str::to_string).collect()) - .unwrap_or_default(), - quiet: false, - check: !bless, - threads: match std::env::var_os("RUST_TEST_THREADS") { - Some(n) => n.to_str().unwrap().parse().unwrap(), - None => std::thread::available_parallelism().unwrap(), - }, - skip: Vec::new(), - }; + let mut args = Args::test().unwrap(); + args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); let mut config = Config { mode: Mode::Yolo { - rustfix: RustfixMode::Everything, + rustfix: ui_test::RustfixMode::Everything, }, stderr_filters: vec![(Match::PathBackslash, b"/")], stdout_filters: vec![], - output_conflict_handling: if bless { - OutputConflictHandling::Bless - } else { - OutputConflictHandling::Error("cargo uibless".into()) - }, + filter_files: env::var("TESTNAME") + .map(|filters| filters.split(',').map(str::to_string).collect()) + .unwrap_or_default(), target: None, out_dir: canonicalize(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())).join("ui_test"), ..Config::rustc(Path::new("tests").join(test_dir)) }; + config.with_args(&args, /* bless by default */ false); + if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling { + *err = "cargo uibless".into(); + } let current_exe_path = env::current_exe().unwrap(); let deps_path = current_exe_path.parent().unwrap(); let profile_path = deps_path.parent().unwrap(); @@ -186,7 +176,6 @@ fn run_ui() { ui_test::run_tests_generic( vec![config], - args, ui_test::default_file_filter, ui_test::default_per_file_config, if quiet { @@ -211,7 +200,6 @@ fn run_internal_tests() { ui_test::run_tests_generic( vec![config], - args, ui_test::default_file_filter, ui_test::default_per_file_config, if quiet { @@ -245,7 +233,6 @@ fn run_ui_toml() { ui_test::run_tests_generic( vec![config], - args, ui_test::default_file_filter, |config, path, _file_contents| { config @@ -302,10 +289,16 @@ fn run_ui_cargo() { let quiet = args.quiet; + let ignored_32bit = |path: &Path| { + // FIXME: for some reason the modules are linted in a different order for this test + cfg!(target_pointer_width = "32") && path.ends_with("tests/ui-cargo/module_style/fail_mod/Cargo.toml") + }; + ui_test::run_tests_generic( vec![config], - args, - |path, args, _config| path.ends_with("Cargo.toml") && ui_test::default_filter_by_arg(path, args), + |path, config| { + path.ends_with("Cargo.toml") && ui_test::default_any_file_filter(path, config) && !ignored_32bit(path) + }, |config, path, _file_contents| { config.out_dir = canonicalize( std::env::current_dir() diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr index e161507b53395..4d97d54963d3d 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr @@ -1,6 +1,7 @@ error: package `cargo_common_metadata_fail` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]` error: package `cargo_common_metadata_fail` is missing `either package.license or package.license_file` metadata diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr index dbf494cc342d3..9eb884f08900b 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr @@ -1,6 +1,7 @@ error: package `cargo_common_metadata_fail_publish` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]` error: package `cargo_common_metadata_fail_publish` is missing `either package.license or package.license_file` metadata diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr index ae5967406f62f..f9685a784a33e 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr @@ -1,6 +1,7 @@ error: package `cargo_common_metadata_fail_publish_true` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]` error: package `cargo_common_metadata_fail_publish_true` is missing `either package.license or package.license_file` metadata diff --git a/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr b/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr index fde3a1e659915..912ea9bb4d111 100644 --- a/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr +++ b/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr @@ -9,6 +9,7 @@ error: file is loaded as a module multiple times: `src/b.rs` | = help: replace all but one `mod` item with `use` items = note: `-D clippy::duplicate-mod` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::duplicate_mod)]` error: file is loaded as a module multiple times: `src/c.rs` --> src/main.rs:9:1 diff --git a/tests/ui-cargo/feature_name/fail/Cargo.stderr b/tests/ui-cargo/feature_name/fail/Cargo.stderr index da2db45d3b836..388f49fb21351 100644 --- a/tests/ui-cargo/feature_name/fail/Cargo.stderr +++ b/tests/ui-cargo/feature_name/fail/Cargo.stderr @@ -2,6 +2,7 @@ error: the "no-" prefix in the feature name "no-qaq" is negative | = help: consider renaming the feature to "qaq", but make sure the feature adds functionality = note: `-D clippy::negative-feature-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::negative_feature_names)]` error: the "no_" prefix in the feature name "no_qaq" is negative | @@ -19,6 +20,7 @@ error: the "-support" suffix in the feature name "qvq-support" is redundant | = help: consider renaming the feature to "qvq" = note: `-D clippy::redundant-feature-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_feature_names)]` error: the "_support" suffix in the feature name "qvq_support" is redundant | diff --git a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr index c2907f319e643..902330e178530 100644 --- a/tests/ui-cargo/module_style/fail_mod/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_mod/Cargo.stderr @@ -6,6 +6,7 @@ error: `mod.rs` files are required, found `src/bad/inner.rs` | = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` --> src/bad/inner/stuff.rs:1:1 diff --git a/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr b/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr index fcf1a3c5e66c6..d776feb7f2e69 100644 --- a/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr @@ -6,5 +6,6 @@ error: `mod.rs` files are required, found `src/bad.rs` | = help: move `src/bad.rs` to `src/bad/mod.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` error: could not compile `fail-mod-remap` (bin "fail-mod-remap") due to previous error diff --git a/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr b/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr index f61642ca2efd7..22558bc4ce89d 100644 --- a/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr +++ b/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr @@ -6,5 +6,6 @@ error: `mod.rs` files are not allowed, found `src/bad/mod.rs` | = help: move `src/bad/mod.rs` to `src/bad.rs` = note: `-D clippy::mod-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mod_module_files)]` error: could not compile `fail-no-mod` (bin "fail-no-mod") due to previous error diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr index 5bcce920455cf..4beedc10830a1 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr @@ -1,5 +1,6 @@ error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9 | = note: `-D clippy::multiple-crate-versions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::multiple_crate_versions)]` error: could not compile `multiple_crate_versions` (bin "multiple_crate_versions") due to previous error diff --git a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr index b1578c9f32490..65a19bb0718da 100644 --- a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr +++ b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr @@ -1,5 +1,6 @@ error: wildcard dependency for `regex` | = note: `-D clippy::wildcard-dependencies` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wildcard_dependencies)]` error: could not compile `wildcard_dependencies` (bin "wildcard_dependencies") due to previous error diff --git a/tests/ui-internal/check_formulation.stderr b/tests/ui-internal/check_formulation.stderr index 10eabca4b9d74..96fa617601ff4 100644 --- a/tests/ui-internal/check_formulation.stderr +++ b/tests/ui-internal/check_formulation.stderr @@ -6,6 +6,7 @@ LL | /// Check for lint formulations that are correct | = help: try using `Checks for` instead = note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` error: non-standard lint formulation --> $DIR/check_formulation.rs:33:5 diff --git a/tests/ui-internal/if_chain_style.stderr b/tests/ui-internal/if_chain_style.stderr index b12df2786520e..ea04955323d18 100644 --- a/tests/ui-internal/if_chain_style.stderr +++ b/tests/ui-internal/if_chain_style.stderr @@ -16,6 +16,7 @@ help: this `let` statement can also be in the `if_chain!` LL | let x = ""; | ^^^^^^^^^^^ = note: `-D clippy::if-chain-style` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::if_chain_style)]` error: `if a && b;` should be `if a; if b;` --> $DIR/if_chain_style.rs:24:12 diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr index 0e85088691765..988d32d5259e2 100644 --- a/tests/ui-internal/invalid_paths.stderr +++ b/tests/ui-internal/invalid_paths.stderr @@ -5,6 +5,7 @@ LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute" | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::invalid-paths` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]` error: invalid path --> $DIR/invalid_paths.rs:18:5 diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 3ca45404e44bb..58b1fd92b5dc9 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -6,6 +6,7 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; | = help: convert all references to use `sym::Deref` = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` error: hardcoded path to a language item --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 diff --git a/tests/ui-toml/disallowed_macros/auxiliary/macros.rs b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs index fcaeace0e9890..f4166b227fc68 100644 --- a/tests/ui-toml/disallowed_macros/auxiliary/macros.rs +++ b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs @@ -30,3 +30,18 @@ macro_rules! item { const ITEM: usize = 1; }; } + +#[macro_export] +macro_rules! binop { + ($t:tt) => { + $t + $t + }; +} + +#[macro_export] +macro_rules! attr { + ($i:item) => { + #[repr(C)] + $i + }; +} diff --git a/tests/ui-toml/disallowed_macros/clippy.toml b/tests/ui-toml/disallowed_macros/clippy.toml index c8fe8be9a7704..85f1b71eb66b9 100644 --- a/tests/ui-toml/disallowed_macros/clippy.toml +++ b/tests/ui-toml/disallowed_macros/clippy.toml @@ -8,4 +8,6 @@ disallowed-macros = [ "macros::ty", "macros::pat", "macros::item", + "macros::binop", + "macros::attr", ] diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.rs b/tests/ui-toml/disallowed_macros/disallowed_macros.rs index ba919b4878854..4a3d55e13c9dc 100644 --- a/tests/ui-toml/disallowed_macros/disallowed_macros.rs +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.rs @@ -20,11 +20,14 @@ fn main() { let macros::pat!() = 1; let _: macros::ty!() = ""; macros::item!(); + let _ = macros::binop!(1); eprintln!("allowed"); } -struct S; +macros::attr! { + struct S; +} impl S { macros::item!(); diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr index 0eebd587a5f10..3c6f59b16e7c3 100644 --- a/tests/ui-toml/disallowed_macros/disallowed_macros.stderr +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -63,23 +63,37 @@ error: use of a disallowed macro `macros::item` LL | macros::item!(); | ^^^^^^^^^^^^^^^ +error: use of a disallowed macro `macros::binop` + --> $DIR/disallowed_macros.rs:23:13 + | +LL | let _ = macros::binop!(1); + | ^^^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::attr` + --> $DIR/disallowed_macros.rs:28:1 + | +LL | / macros::attr! { +LL | | struct S; +LL | | } + | |_^ + error: use of a disallowed macro `macros::item` - --> $DIR/disallowed_macros.rs:30:5 + --> $DIR/disallowed_macros.rs:33:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ error: use of a disallowed macro `macros::item` - --> $DIR/disallowed_macros.rs:34:5 + --> $DIR/disallowed_macros.rs:37:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ error: use of a disallowed macro `macros::item` - --> $DIR/disallowed_macros.rs:38:5 + --> $DIR/disallowed_macros.rs:41:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs b/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs deleted file mode 100644 index 60fbaaea3d340..0000000000000 --- a/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs +++ /dev/null @@ -1,469 +0,0 @@ -// NOTE: Copied from `ui/auxiliary/proc_macros.rs`, couldn't get `../` to work for some reason - -#![feature(let_chains)] -#![feature(proc_macro_span)] -#![allow(clippy::excessive_nesting, dead_code)] - -extern crate proc_macro; - -use core::mem; -use proc_macro::token_stream::IntoIter; -use proc_macro::Delimiter::{self, Brace, Parenthesis}; -use proc_macro::Spacing::{self, Alone, Joint}; -use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT}; - -type Result = core::result::Result; - -/// Make a `compile_error!` pointing to the given span. -fn make_error(msg: &str, span: Span) -> TokenStream { - TokenStream::from_iter([ - TT::Ident(Ident::new("compile_error", span)), - TT::Punct(punct_with_span('!', Alone, span)), - TT::Group({ - let mut msg = Literal::string(msg); - msg.set_span(span); - group_with_span(Parenthesis, TokenStream::from_iter([TT::Literal(msg)]), span) - }), - ]) -} - -fn expect_tt(tt: Option, f: impl FnOnce(TT) -> Option, expected: &str, span: Span) -> Result { - match tt { - None => Err(make_error( - &format!("unexpected end of input, expected {expected}"), - span, - )), - Some(tt) => { - let span = tt.span(); - match f(tt) { - Some(x) => Ok(x), - None => Err(make_error(&format!("unexpected token, expected {expected}"), span)), - } - }, - } -} - -fn punct_with_span(c: char, spacing: Spacing, span: Span) -> Punct { - let mut p = Punct::new(c, spacing); - p.set_span(span); - p -} - -fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Group { - let mut g = Group::new(delimiter, stream); - g.set_span(span); - g -} - -/// Token used to escape the following token from the macro's span rules. -const ESCAPE_CHAR: char = '$'; - -/// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their -/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`. -#[proc_macro] -pub fn with_span(input: TokenStream) -> TokenStream { - let mut iter = input.into_iter(); - let span = iter.next().unwrap().span(); - let mut res = TokenStream::new(); - if let Err(e) = write_with_span(span, iter, &mut res) { - e - } else { - res - } -} - -/// Takes a sequence of tokens and return the tokens with the span set such that they appear to be -/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`. -#[proc_macro] -pub fn external(input: TokenStream) -> TokenStream { - let mut res = TokenStream::new(); - if let Err(e) = write_with_span(Span::mixed_site(), input.into_iter(), &mut res) { - e - } else { - res - } -} - -/// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped -/// either by `#ident` or `#(tokens)`. -fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> { - while let Some(tt) = input.next() { - match tt { - TT::Punct(p) if p.as_char() == ESCAPE_CHAR => { - expect_tt( - input.next(), - |tt| match tt { - tt @ (TT::Ident(_) | TT::Literal(_)) => { - out.extend([tt]); - Some(()) - }, - TT::Punct(mut p) if p.as_char() == ESCAPE_CHAR => { - p.set_span(s); - out.extend([TT::Punct(p)]); - Some(()) - }, - TT::Group(g) if g.delimiter() == Parenthesis => { - out.extend([TT::Group(group_with_span(Delimiter::None, g.stream(), g.span()))]); - Some(()) - }, - _ => None, - }, - "an ident, a literal, or parenthesized tokens", - p.span(), - )?; - }, - TT::Group(g) => { - let mut stream = TokenStream::new(); - write_with_span(s, g.stream().into_iter(), &mut stream)?; - out.extend([TT::Group(group_with_span(g.delimiter(), stream, s))]); - }, - mut tt => { - tt.set_span(s); - out.extend([tt]); - }, - } - } - Ok(()) -} - -/// Within the item this attribute is attached to, an `inline!` macro is available which expands the -/// contained tokens as though they came from a macro expansion. -/// -/// Within the `inline!` macro, any token preceded by `$` is passed as though it were an argument -/// with an automatically chosen fragment specifier. `$ident` will be passed as `ident`, `$1` or -/// `$"literal"` will be passed as `literal`, `$'lt` will be passed as `lifetime`, and `$(...)` will -/// pass the contained tokens as a `tt` sequence (the wrapping parenthesis are removed). If another -/// specifier is required it can be specified within parenthesis like `$(@expr ...)`. This will -/// expand the remaining tokens as a single argument. -/// -/// Multiple `inline!` macros may be nested within each other. This will expand as nested macro -/// calls. However, any arguments will be passed as though they came from the outermost context. -#[proc_macro_attribute] -pub fn inline_macros(args: TokenStream, input: TokenStream) -> TokenStream { - let mut args = args.into_iter(); - let mac_name = match args.next() { - Some(TT::Ident(name)) => Some(name), - Some(tt) => { - return make_error( - "unexpected argument, expected either an ident or no arguments", - tt.span(), - ); - }, - None => None, - }; - if let Some(tt) = args.next() { - return make_error( - "unexpected argument, expected either an ident or no arguments", - tt.span(), - ); - }; - - let mac_name = if let Some(mac_name) = mac_name { - Ident::new(&format!("__inline_mac_{mac_name}"), Span::call_site()) - } else { - let mut input = match LookaheadIter::new(input.clone().into_iter()) { - Some(x) => x, - None => return input, - }; - loop { - match input.next() { - None => break Ident::new("__inline_mac", Span::call_site()), - Some(TT::Ident(kind)) => match &*kind.to_string() { - "impl" => break Ident::new("__inline_mac_impl", Span::call_site()), - kind @ ("struct" | "enum" | "union" | "fn" | "mod" | "trait" | "type" | "const" | "static") => { - if let TT::Ident(name) = &input.tt { - break Ident::new(&format!("__inline_mac_{kind}_{name}"), Span::call_site()); - } else { - break Ident::new(&format!("__inline_mac_{kind}"), Span::call_site()); - } - }, - _ => {}, - }, - _ => {}, - } - } - }; - - let mut expander = Expander::default(); - let mut mac = MacWriter::new(mac_name); - if let Err(e) = expander.expand(input.into_iter(), &mut mac) { - return e; - } - let mut out = TokenStream::new(); - mac.finish(&mut out); - out.extend(expander.expn); - out -} - -/// Wraps a `TokenStream` iterator with a single token lookahead. -struct LookaheadIter { - tt: TT, - iter: IntoIter, -} -impl LookaheadIter { - fn new(mut iter: IntoIter) -> Option { - iter.next().map(|tt| Self { tt, iter }) - } - - /// Get's the lookahead token, replacing it with the next token in the stream. - /// Note: If there isn't a next token, this will not return the lookahead token. - fn next(&mut self) -> Option { - self.iter.next().map(|tt| mem::replace(&mut self.tt, tt)) - } -} - -/// Builds the macro used to implement all the `inline!` macro calls. -struct MacWriter { - name: Ident, - macros: TokenStream, - next_idx: usize, -} -impl MacWriter { - fn new(name: Ident) -> Self { - Self { - name, - macros: TokenStream::new(), - next_idx: 0, - } - } - - /// Inserts a new `inline!` call. - fn insert(&mut self, name_span: Span, bang_span: Span, body: Group, expander: &mut Expander) -> Result<()> { - let idx = self.next_idx; - self.next_idx += 1; - - let mut inner = Expander::for_arm(idx); - inner.expand(body.stream().into_iter(), self)?; - let new_arm = inner.arm.unwrap(); - - self.macros.extend([ - TT::Group(Group::new(Parenthesis, new_arm.args_def)), - TT::Punct(Punct::new('=', Joint)), - TT::Punct(Punct::new('>', Alone)), - TT::Group(Group::new(Parenthesis, inner.expn)), - TT::Punct(Punct::new(';', Alone)), - ]); - - expander.expn.extend([ - TT::Ident({ - let mut name = self.name.clone(); - name.set_span(name_span); - name - }), - TT::Punct(punct_with_span('!', Alone, bang_span)), - ]); - let mut call_body = TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]); - if let Some(arm) = expander.arm.as_mut() { - if !new_arm.args.is_empty() { - arm.add_sub_args(new_arm.args, &mut call_body); - } - } else { - call_body.extend(new_arm.args); - } - let mut g = Group::new(body.delimiter(), call_body); - g.set_span(body.span()); - expander.expn.extend([TT::Group(g)]); - Ok(()) - } - - /// Creates the macro definition. - fn finish(self, out: &mut TokenStream) { - if self.next_idx != 0 { - out.extend([ - TT::Ident(Ident::new("macro_rules", Span::call_site())), - TT::Punct(Punct::new('!', Alone)), - TT::Ident(self.name), - TT::Group(Group::new(Brace, self.macros)), - ]) - } - } -} - -struct MacroArm { - args_def: TokenStream, - args: Vec, -} -impl MacroArm { - fn add_single_arg_def(&mut self, kind: &str, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { - let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); - self.args_def.extend([ - TT::Punct(Punct::new('$', Alone)), - TT::Ident(name.clone()), - TT::Punct(Punct::new(':', Alone)), - TT::Ident(Ident::new(kind, Span::call_site())), - ]); - name.set_span(arg_span); - out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]); - } - - fn add_parenthesized_arg_def(&mut self, kind: Ident, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { - let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); - self.args_def.extend([TT::Group(Group::new( - Parenthesis, - TokenStream::from_iter([ - TT::Punct(Punct::new('$', Alone)), - TT::Ident(name.clone()), - TT::Punct(Punct::new(':', Alone)), - TT::Ident(kind), - ]), - ))]); - name.set_span(arg_span); - out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]); - } - - fn add_multi_arg_def(&mut self, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { - let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); - self.args_def.extend([TT::Group(Group::new( - Parenthesis, - TokenStream::from_iter([ - TT::Punct(Punct::new('$', Alone)), - TT::Group(Group::new( - Parenthesis, - TokenStream::from_iter([ - TT::Punct(Punct::new('$', Alone)), - TT::Ident(name.clone()), - TT::Punct(Punct::new(':', Alone)), - TT::Ident(Ident::new("tt", Span::call_site())), - ]), - )), - TT::Punct(Punct::new('*', Alone)), - ]), - ))]); - name.set_span(arg_span); - out.extend([ - TT::Punct(punct_with_span('$', Alone, dollar_span)), - TT::Group(group_with_span( - Parenthesis, - TokenStream::from_iter([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]), - dollar_span, - )), - TT::Punct(punct_with_span('*', Alone, dollar_span)), - ]); - } - - fn add_arg(&mut self, dollar_span: Span, tt: TT, input: &mut IntoIter, out: &mut TokenStream) -> Result<()> { - match tt { - TT::Punct(p) if p.as_char() == ESCAPE_CHAR => out.extend([TT::Punct(p)]), - TT::Punct(p) if p.as_char() == '\'' && p.spacing() == Joint => { - let lt_name = expect_tt( - input.next(), - |tt| match tt { - TT::Ident(x) => Some(x), - _ => None, - }, - "lifetime name", - p.span(), - )?; - let arg_span = p.span().join(lt_name.span()).unwrap_or(p.span()); - self.add_single_arg_def("lifetime", dollar_span, arg_span, out); - self.args.extend([TT::Punct(p), TT::Ident(lt_name)]); - }, - TT::Ident(x) => { - self.add_single_arg_def("ident", dollar_span, x.span(), out); - self.args.push(TT::Ident(x)); - }, - TT::Literal(x) => { - self.add_single_arg_def("literal", dollar_span, x.span(), out); - self.args.push(TT::Literal(x)); - }, - TT::Group(g) if g.delimiter() == Parenthesis => { - let mut inner = g.stream().into_iter(); - if let Some(TT::Punct(p)) = inner.next() - && p.as_char() == '@' - { - let kind = expect_tt( - inner.next(), - |tt| match tt { - TT::Ident(kind) => Some(kind), - _ => None, - }, - "a macro fragment specifier", - p.span(), - )?; - self.add_parenthesized_arg_def(kind, dollar_span, g.span(), out); - self.args.push(TT::Group(group_with_span(Parenthesis, inner.collect(), g.span()))) - } else { - self.add_multi_arg_def(dollar_span, g.span(), out); - self.args.push(TT::Group(g)); - } - }, - tt => return Err(make_error("unsupported escape", tt.span())), - }; - Ok(()) - } - - fn add_sub_args(&mut self, args: Vec, out: &mut TokenStream) { - self.add_multi_arg_def(Span::call_site(), Span::call_site(), out); - self.args - .extend([TT::Group(Group::new(Parenthesis, TokenStream::from_iter(args)))]); - } -} - -#[derive(Default)] -struct Expander { - arm: Option, - expn: TokenStream, -} -impl Expander { - fn for_arm(idx: usize) -> Self { - Self { - arm: Some(MacroArm { - args_def: TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]), - args: Vec::new(), - }), - expn: TokenStream::new(), - } - } - - fn write_tt(&mut self, tt: TT, mac: &mut MacWriter) -> Result<()> { - match tt { - TT::Group(g) => { - let outer = mem::take(&mut self.expn); - self.expand(g.stream().into_iter(), mac)?; - let inner = mem::replace(&mut self.expn, outer); - self.expn - .extend([TT::Group(group_with_span(g.delimiter(), inner, g.span()))]); - }, - tt => self.expn.extend([tt]), - } - Ok(()) - } - - fn expand(&mut self, input: IntoIter, mac: &mut MacWriter) -> Result<()> { - let Some(mut input) = LookaheadIter::new(input) else { - return Ok(()); - }; - while let Some(tt) = input.next() { - if let TT::Punct(p) = &tt - && p.as_char() == ESCAPE_CHAR - && let Some(arm) = self.arm.as_mut() - { - arm.add_arg(p.span(), mem::replace(&mut input.tt, tt), &mut input.iter, &mut self.expn)?; - if input.next().is_none() { - return Ok(()); - } - } else if let TT::Punct(p) = &input.tt - && p.as_char() == '!' - && let TT::Ident(name) = &tt - && name.to_string() == "inline" - { - let g = expect_tt( - input.iter.next(), - |tt| match tt { - TT::Group(g) => Some(g), - _ => None, - }, - "macro arguments", - p.span(), - )?; - mac.insert(name.span(), p.span(), g, self)?; - if input.next().is_none() { - return Ok(()); - } - } else { - self.write_tt(tt, mac)?; - } - } - self.write_tt(input.tt, mac) - } -} diff --git a/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/tests/ui-toml/excessive_nesting/excessive_nesting.rs index 25f0d0d6230a7..d737a832dd169 100644 --- a/tests/ui-toml/excessive_nesting/excessive_nesting.rs +++ b/tests/ui-toml/excessive_nesting/excessive_nesting.rs @@ -1,4 +1,4 @@ -//@aux-build:proc_macros.rs +//@aux-build:../../ui/auxiliary/proc_macros.rs #![rustfmt::skip] #![feature(custom_inner_attributes)] #![allow(unused)] @@ -156,7 +156,7 @@ fn main() { for i in {{{{xx}}}} {{{{{{{{}}}}}}}} while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} - + while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} }; diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index cdabe6460cdca..b97bb144468b0 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -28,6 +28,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect disallowed-types doc-valid-idents enable-raw-pointer-heuristic-for-send + enforce-iter-loop-reborrow enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold @@ -99,6 +100,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect disallowed-types doc-valid-idents enable-raw-pointer-heuristic-for-send + enforce-iter-loop-reborrow enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold diff --git a/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs b/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs deleted file mode 100644 index 1c591fc76f3e9..0000000000000 --- a/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs +++ /dev/null @@ -1,13 +0,0 @@ -extern crate proc_macro; - -use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree}; - -#[proc_macro] -pub fn unsafe_block(input: TokenStream) -> TokenStream { - let span = input.into_iter().next().unwrap().span(); - TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), { - let mut group = Group::new(Delimiter::Brace, TokenStream::new()); - group.set_span(span); - TokenTree::Group(group) - }]) -} diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index c0976f0d6007b..b28e1b7d1802d 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -1,4 +1,4 @@ -//@aux-build:proc_macro_unsafe.rs +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] #![allow(deref_nullptr, clippy::let_unit_value, clippy::missing_safety_doc)] @@ -564,4 +564,18 @@ fn issue_8679() { unsafe {} } +mod issue_11246 { + // Safety: foo + const _: () = unsafe {}; + + // Safety: A safety comment + const FOO: () = unsafe {}; + + // Safety: bar + static BAR: u8 = unsafe { 0 }; +} + +// Safety: Another safety comment +const FOO: () = unsafe {}; + fn main() {} diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index db85247f47d24..e3f2ca72d1cbd 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -1,6 +1,6 @@ #![allow(clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index 0915f88544bc7..d1bc20d6831a5 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -1,6 +1,6 @@ #![allow(clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; diff --git a/tests/ui/cast_size_32bit.stderr b/tests/ui/cast_size.32bit.stderr similarity index 78% rename from tests/ui/cast_size_32bit.stderr rename to tests/ui/cast_size.32bit.stderr index fb51783a48788..379ca60862b9c 100644 --- a/tests/ui/cast_size_32bit.stderr +++ b/tests/ui/cast_size.32bit.stderr @@ -1,44 +1,46 @@ error: casting `isize` to `i8` may truncate the value - --> $DIR/cast_size_32bit.rs:12:5 + --> $DIR/cast_size.rs:15:5 | LL | 1isize as i8; | ^^^^^^^^^^^^ | = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]` help: ... or use `try_from` and handle the error accordingly | LL | i8::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size_32bit.rs:15:5 + --> $DIR/cast_size.rs:18:5 | LL | x0 as f64; | ^^^^^^^^^ | = note: `-D clippy::cast-precision-loss` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]` error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size_32bit.rs:16:5 + --> $DIR/cast_size.rs:19:5 | LL | x1 as f64; | ^^^^^^^^^ error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size_32bit.rs:17:5 + --> $DIR/cast_size.rs:20:5 | LL | x0 as f32; | ^^^^^^^^^ error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size_32bit.rs:18:5 + --> $DIR/cast_size.rs:21:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:19:5 + --> $DIR/cast_size.rs:22:5 | LL | 1isize as i32; | ^^^^^^^^^^^^^ @@ -50,7 +52,7 @@ LL | i32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:20:5 + --> $DIR/cast_size.rs:23:5 | LL | 1isize as u32; | ^^^^^^^^^^^^^ @@ -62,7 +64,7 @@ LL | u32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:21:5 + --> $DIR/cast_size.rs:24:5 | LL | 1usize as u32; | ^^^^^^^^^^^^^ @@ -74,7 +76,7 @@ LL | u32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:22:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -86,15 +88,16 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:22:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:24:5 + --> $DIR/cast_size.rs:26:5 | LL | 1i64 as isize; | ^^^^^^^^^^^^^ @@ -106,7 +109,7 @@ LL | isize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:25:5 + --> $DIR/cast_size.rs:27:5 | LL | 1i64 as usize; | ^^^^^^^^^^^^^ @@ -118,7 +121,7 @@ LL | usize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:26:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -130,13 +133,13 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:26:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:27:5 + --> $DIR/cast_size.rs:29:5 | LL | 1u64 as usize; | ^^^^^^^^^^^^^ @@ -148,24 +151,31 @@ LL | usize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:28:5 + --> $DIR/cast_size.rs:30:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size_32bit.rs:33:5 + --> $DIR/cast_size.rs:35:5 | LL | 999_999_999 as f32; | ^^^^^^^^^^^^^^^^^^ -error: casting integer literal to `f64` is unnecessary - --> $DIR/cast_size_32bit.rs:34:5 +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size.rs:36:5 + | +LL | 9_999_999_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: literal out of range for `usize` + --> $DIR/cast_size.rs:36:5 | -LL | 3_999_999_999usize as f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64` +LL | 9_999_999_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + = note: the literal `9_999_999_999_999_999usize` does not fit into the type `usize` whose range is `0..=4294967295` + = note: `#[deny(overflowing_literals)]` on by default -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/cast_size.stderr b/tests/ui/cast_size.64bit.stderr similarity index 94% rename from tests/ui/cast_size.stderr rename to tests/ui/cast_size.64bit.stderr index bc9224be64401..7fae92b12503d 100644 --- a/tests/ui/cast_size.stderr +++ b/tests/ui/cast_size.64bit.stderr @@ -1,5 +1,5 @@ error: casting `isize` to `i8` may truncate the value - --> $DIR/cast_size.rs:12:5 + --> $DIR/cast_size.rs:15:5 | LL | 1isize as i8; | ^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | i8::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size.rs:16:5 + --> $DIR/cast_size.rs:18:5 | LL | x0 as f64; | ^^^^^^^^^ @@ -28,19 +28,19 @@ LL | x1 as f64; | ^^^^^^^^^ error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size.rs:21:5 + --> $DIR/cast_size.rs:20:5 | LL | x0 as f32; | ^^^^^^^^^ error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size.rs:23:5 + --> $DIR/cast_size.rs:21:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:25:5 + --> $DIR/cast_size.rs:22:5 | LL | 1isize as i32; | ^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | i32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:27:5 + --> $DIR/cast_size.rs:23:5 | LL | 1isize as u32; | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | u32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:29:5 + --> $DIR/cast_size.rs:24:5 | LL | 1usize as u32; | ^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | u32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:31:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:31:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | 1usize as i32; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:36:5 + --> $DIR/cast_size.rs:26:5 | LL | 1i64 as isize; | ^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | isize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:38:5 + --> $DIR/cast_size.rs:27:5 | LL | 1i64 as usize; | ^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | usize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:40:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -133,13 +133,13 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:40:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:43:5 + --> $DIR/cast_size.rs:29:5 | LL | 1u64 as usize; | ^^^^^^^^^^^^^ @@ -151,19 +151,19 @@ LL | usize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:45:5 + --> $DIR/cast_size.rs:30:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size.rs:51:5 + --> $DIR/cast_size.rs:35:5 | LL | 999_999_999 as f32; | ^^^^^^^^^^^^^^^^^^ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size.rs:53:5 + --> $DIR/cast_size.rs:36:5 | LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/cast_size.rs b/tests/ui/cast_size.rs index 95626b20b27bf..d063a70ccdfcf 100644 --- a/tests/ui/cast_size.rs +++ b/tests/ui/cast_size.rs @@ -1,56 +1,37 @@ -//@ignore-32bit -#[warn( +//@stderr-per-bitwidth +//@no-rustfix + +#![warn( clippy::cast_precision_loss, clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap, clippy::cast_lossless )] -#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + fn main() { // Casting from *size 1isize as i8; - //~^ ERROR: casting `isize` to `i8` may truncate the value let x0 = 1isize; let x1 = 1usize; x0 as f64; - //~^ ERROR: casting `isize` to `f64` causes a loss of precision on targets with 64-bit - //~| NOTE: `-D clippy::cast-precision-loss` implied by `-D warnings` x1 as f64; - //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit x0 as f32; - //~^ ERROR: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 b x1 as f32; - //~^ ERROR: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 b 1isize as i32; - //~^ ERROR: casting `isize` to `i32` may truncate the value on targets with 64-bit wid 1isize as u32; - //~^ ERROR: casting `isize` to `u32` may truncate the value on targets with 64-bit wid 1usize as u32; - //~^ ERROR: casting `usize` to `u32` may truncate the value on targets with 64-bit wid 1usize as i32; - //~^ ERROR: casting `usize` to `i32` may truncate the value on targets with 64-bit wid - //~| ERROR: casting `usize` to `i32` may wrap around the value on targets with 32-bit - //~| NOTE: `-D clippy::cast-possible-wrap` implied by `-D warnings` - // Casting to *size 1i64 as isize; - //~^ ERROR: casting `i64` to `isize` may truncate the value on targets with 32-bit wid 1i64 as usize; - //~^ ERROR: casting `i64` to `usize` may truncate the value on targets with 32-bit wid 1u64 as isize; - //~^ ERROR: casting `u64` to `isize` may truncate the value on targets with 32-bit wid - //~| ERROR: casting `u64` to `isize` may wrap around the value on targets with 64-bit 1u64 as usize; - //~^ ERROR: casting `u64` to `usize` may truncate the value on targets with 32-bit wid 1u32 as isize; - //~^ ERROR: casting `u32` to `isize` may wrap around the value on targets with 32-bit 1u32 as usize; // Should not trigger any lint 1i32 as isize; // Neither should this 1i32 as usize; // Big integer literal to float 999_999_999 as f32; - //~^ ERROR: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, 9_999_999_999_999_999usize as f64; - //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit } -//@no-rustfix diff --git a/tests/ui/cast_size_32bit.rs b/tests/ui/cast_size_32bit.rs deleted file mode 100644 index 5a06e34bdb8fb..0000000000000 --- a/tests/ui/cast_size_32bit.rs +++ /dev/null @@ -1,56 +0,0 @@ -//@ignore-64bit -#[warn( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_possible_wrap, - clippy::cast_lossless -)] -#[allow(clippy::no_effect, clippy::unnecessary_operation)] -fn main() { - // Casting from *size - 1isize as i8; - //~^ ERROR: casting `isize` to `i8` may truncate the value - let x0 = 1isize; - let x1 = 1usize; - x0 as f64; - //~^ ERROR: casting `isize` to `f64` causes a loss of precision on targets with 64-bit - //~| NOTE: `-D clippy::cast-precision-loss` implied by `-D warnings` - x1 as f64; - //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit - x0 as f32; - //~^ ERROR: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 b - x1 as f32; - //~^ ERROR: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 b - 1isize as i32; - //~^ ERROR: casting `isize` to `i32` may truncate the value on targets with 64-bit wid - 1isize as u32; - //~^ ERROR: casting `isize` to `u32` may truncate the value on targets with 64-bit wid - 1usize as u32; - //~^ ERROR: casting `usize` to `u32` may truncate the value on targets with 64-bit wid - 1usize as i32; - //~^ ERROR: casting `usize` to `i32` may truncate the value on targets with 64-bit wid - //~| ERROR: casting `usize` to `i32` may wrap around the value on targets with 32-bit - //~| NOTE: `-D clippy::cast-possible-wrap` implied by `-D warnings` - // Casting to *size - 1i64 as isize; - //~^ ERROR: casting `i64` to `isize` may truncate the value on targets with 32-bit wid - 1i64 as usize; - //~^ ERROR: casting `i64` to `usize` may truncate the value on targets with 32-bit wid - 1u64 as isize; - //~^ ERROR: casting `u64` to `isize` may truncate the value on targets with 32-bit wid - //~| ERROR: casting `u64` to `isize` may wrap around the value on targets with 64-bit - 1u64 as usize; - //~^ ERROR: casting `u64` to `usize` may truncate the value on targets with 32-bit wid - 1u32 as isize; - //~^ ERROR: casting `u32` to `isize` may wrap around the value on targets with 32-bit - 1u32 as usize; // Should not trigger any lint - 1i32 as isize; // Neither should this - 1i32 as usize; - // Big integer literal to float - 999_999_999 as f32; - //~^ ERROR: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, - 3_999_999_999usize as f64; - //~^ ERROR: casting integer literal to `f64` is unnecessary - //~| NOTE: `-D clippy::unnecessary-cast` implied by `-D warnings` -} diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index e82e7bcb06cb0..02f80cc52ac72 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -128,6 +128,57 @@ fn main() { assert!(x.is_ok(), "{:?}", x.unwrap_err()); } +fn issue11371() { + let option = Some(()); + + if option.is_some() { + option.as_ref().unwrap(); + //~^ ERROR: called `unwrap` on `option` after checking its variant with `is_some` + } else { + option.as_ref().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + result.as_ref().unwrap(); + //~^ ERROR: called `unwrap` on `result` after checking its variant with `is_ok` + } else { + result.as_ref().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + let mut option = Some(()); + if option.is_some() { + option.as_mut().unwrap(); + //~^ ERROR: called `unwrap` on `option` after checking its variant with `is_some` + } else { + option.as_mut().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + let mut result = Ok::<(), ()>(()); + if result.is_ok() { + result.as_mut().unwrap(); + //~^ ERROR: called `unwrap` on `result` after checking its variant with `is_ok` + } else { + result.as_mut().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + // This should not lint. Statics are, at the time of writing, not linted on anyway, + // but if at some point they are supported by this lint, it should correctly see that + // `X` is being mutated and not suggest `if let Some(..) = X {}` + static mut X: Option = Some(123); + unsafe { + if X.is_some() { + X = None; + X.unwrap(); + } + } +} + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index ed603581ecddb..a5afbba7317e9 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -168,5 +168,73 @@ LL | if x.is_err() { LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: called `unwrap` on `option` after checking its variant with `is_some` + --> $DIR/simple_conditionals.rs:135:9 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some(..) = &option` +LL | option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:138:9 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> $DIR/simple_conditionals.rs:145:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok(..) = &result` +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:148:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `option` after checking its variant with `is_some` + --> $DIR/simple_conditionals.rs:154:9 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some(..) = &mut option` +LL | option.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:157:9 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | option.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> $DIR/simple_conditionals.rs:163:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok(..) = &mut result` +LL | result.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:166:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors diff --git a/tests/ui/clone_on_copy_impl.rs b/tests/ui/clone_on_copy_impl.rs index b7c186bef7724..2d03544ad8b6b 100644 --- a/tests/ui/clone_on_copy_impl.rs +++ b/tests/ui/clone_on_copy_impl.rs @@ -1,4 +1,4 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type)] +#![allow(clippy::non_canonical_clone_impl)] use std::fmt; use std::marker::PhantomData; diff --git a/tests/ui/crashes/ice-11337.rs b/tests/ui/crashes/ice-11337.rs new file mode 100644 index 0000000000000..0bed4035f6bf4 --- /dev/null +++ b/tests/ui/crashes/ice-11337.rs @@ -0,0 +1,9 @@ +#![feature(trait_alias)] + +trait Confusing = Fn(i32) where F: Fn(u32); + +fn alias, F>(_: T, _: F) {} + +fn main() { + alias(|_| {}, |_| {}); +} diff --git a/tests/ui/crashes/ice-11422.fixed b/tests/ui/crashes/ice-11422.fixed new file mode 100644 index 0000000000000..ca5721cbb2bae --- /dev/null +++ b/tests/ui/crashes/ice-11422.fixed @@ -0,0 +1,25 @@ +#![warn(clippy::implied_bounds_in_impls)] + +use std::fmt::Debug; +use std::ops::*; + +fn gen() -> impl PartialOrd + Debug {} + +struct Bar {} +trait Foo {} +trait FooNested> {} +impl Foo for Bar {} +impl FooNested for Bar {} + +fn foo() -> impl Foo + FooNested { + Bar {} +} + +fn test_impl_ops() -> impl Add + Sub + Mul + Div { + 1 +} +fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign { + 1 +} + +fn main() {} diff --git a/tests/ui/crashes/ice-11422.rs b/tests/ui/crashes/ice-11422.rs new file mode 100644 index 0000000000000..355ec2480bba4 --- /dev/null +++ b/tests/ui/crashes/ice-11422.rs @@ -0,0 +1,25 @@ +#![warn(clippy::implied_bounds_in_impls)] + +use std::fmt::Debug; +use std::ops::*; + +fn gen() -> impl PartialOrd + PartialEq + Debug {} + +struct Bar {} +trait Foo {} +trait FooNested> {} +impl Foo for Bar {} +impl FooNested for Bar {} + +fn foo() -> impl Foo + FooNested { + Bar {} +} + +fn test_impl_ops() -> impl Add + Sub + Mul + Div { + 1 +} +fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign { + 1 +} + +fn main() {} diff --git a/tests/ui/crashes/ice-11422.stderr b/tests/ui/crashes/ice-11422.stderr new file mode 100644 index 0000000000000..fb80b5b147f57 --- /dev/null +++ b/tests/ui/crashes/ice-11422.stderr @@ -0,0 +1,16 @@ +error: this bound is already specified as the supertrait of `PartialOrd` + --> $DIR/ice-11422.rs:6:31 + | +LL | fn gen() -> impl PartialOrd + PartialEq + Debug {} + | ^^^^^^^^^ + | + = note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::implied_bounds_in_impls)]` +help: try removing this bound + | +LL - fn gen() -> impl PartialOrd + PartialEq + Debug {} +LL + fn gen() -> impl PartialOrd + Debug {} + | + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-360.rs b/tests/ui/crashes/ice-360.rs index 28589e1efeda5..0d10932b0987e 100644 --- a/tests/ui/crashes/ice-360.rs +++ b/tests/ui/crashes/ice-360.rs @@ -3,7 +3,8 @@ fn main() {} fn no_panic(slice: &[T]) { let mut iter = slice.iter(); loop { - //~^ ERROR: this loop could be written as a `while let` loop + //~^ ERROR: this loop never actually loops + //~| ERROR: this loop could be written as a `while let` loop //~| NOTE: `-D clippy::while-let-loop` implied by `-D warnings` let _ = match iter.next() { Some(ele) => ele, diff --git a/tests/ui/crashes/ice-360.stderr b/tests/ui/crashes/ice-360.stderr index dd016355b5325..a84697a9f291d 100644 --- a/tests/ui/crashes/ice-360.stderr +++ b/tests/ui/crashes/ice-360.stderr @@ -1,10 +1,24 @@ +error: this loop never actually loops + --> $DIR/ice-360.rs:5:5 + | +LL | / loop { +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + error: this loop could be written as a `while let` loop --> $DIR/ice-360.rs:5:5 | LL | / loop { LL | | LL | | -LL | | let _ = match iter.next() { +LL | | ... | LL | | LL | | } @@ -14,7 +28,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::while_let_loop)]` error: empty `loop {}` wastes CPU cycles - --> $DIR/ice-360.rs:12:9 + --> $DIR/ice-360.rs:13:9 | LL | loop {} | ^^^^^^^ @@ -23,5 +37,5 @@ LL | loop {} = note: `-D clippy::empty-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::empty_loop)]` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/derivable_impls.fixed b/tests/ui/derivable_impls.fixed index 6cc202414f5f4..68c5a5c5ca457 100644 --- a/tests/ui/derivable_impls.fixed +++ b/tests/ui/derivable_impls.fixed @@ -287,4 +287,17 @@ mod issue10158 { } } +mod issue11368 { + pub struct A { + a: u32, + } + + impl Default for A { + #[track_caller] + fn default() -> Self { + Self { a: 0 } + } + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index 0aa9acd752dcc..21d73ba8b7778 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -323,4 +323,17 @@ mod issue10158 { } } +mod issue11368 { + pub struct A { + a: u32, + } + + impl Default for A { + #[track_caller] + fn default() -> Self { + Self { a: 0 } + } + } +} + fn main() {} diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index 310c701765ba9..20ac8a6e6be35 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -1,8 +1,4 @@ -#![allow( - clippy::incorrect_clone_impl_on_copy_type, - clippy::incorrect_partial_ord_impl_on_ord_type, - dead_code -)] +#![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)] #![warn(clippy::expl_impl_clone_on_copy)] diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index e9b2ee34760f2..88942d9543226 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:12:1 + --> $DIR/derive.rs:8:1 | LL | / impl Clone for Qux { LL | | @@ -10,7 +10,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:12:1 + --> $DIR/derive.rs:8:1 | LL | / impl Clone for Qux { LL | | @@ -23,7 +23,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:37:1 + --> $DIR/derive.rs:33:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -34,7 +34,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:37:1 + --> $DIR/derive.rs:33:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -45,7 +45,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:49:1 + --> $DIR/derive.rs:45:1 | LL | / impl Clone for BigArray { LL | | @@ -56,7 +56,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:49:1 + --> $DIR/derive.rs:45:1 | LL | / impl Clone for BigArray { LL | | @@ -67,7 +67,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:61:1 + --> $DIR/derive.rs:57:1 | LL | / impl Clone for FnPtr { LL | | @@ -78,7 +78,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:61:1 + --> $DIR/derive.rs:57:1 | LL | / impl Clone for FnPtr { LL | | @@ -89,7 +89,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:82:1 + --> $DIR/derive.rs:78:1 | LL | / impl Clone for Generic2 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:82:1 + --> $DIR/derive.rs:78:1 | LL | / impl Clone for Generic2 { LL | | diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 2c19942d420c9..1c7e6d1c20230 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,6 +1,6 @@ #![warn(clippy::derive_ord_xor_partial_ord)] #![allow(clippy::unnecessary_wraps)] -#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +#![allow(clippy::non_canonical_partial_ord_impl)] use std::cmp::Ordering; diff --git a/tests/ui/empty_loop.rs b/tests/ui/empty_loop.rs index 54e8fb4907c0f..be347563135c3 100644 --- a/tests/ui/empty_loop.rs +++ b/tests/ui/empty_loop.rs @@ -7,10 +7,12 @@ use proc_macros::{external, inline_macros}; fn should_trigger() { loop {} + #[allow(clippy::never_loop)] loop { loop {} } + #[allow(clippy::never_loop)] 'outer: loop { 'inner: loop {} } @@ -18,6 +20,7 @@ fn should_trigger() { #[inline_macros] fn should_not_trigger() { + #[allow(clippy::never_loop)] loop { panic!("This is fine") } diff --git a/tests/ui/empty_loop.stderr b/tests/ui/empty_loop.stderr index 84d7d61c7daa8..113556f673c8c 100644 --- a/tests/ui/empty_loop.stderr +++ b/tests/ui/empty_loop.stderr @@ -9,7 +9,7 @@ LL | loop {} = help: to override `-D warnings` add `#[allow(clippy::empty_loop)]` error: empty `loop {}` wastes CPU cycles - --> $DIR/empty_loop.rs:11:9 + --> $DIR/empty_loop.rs:12:9 | LL | loop {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | loop {} = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: empty `loop {}` wastes CPU cycles - --> $DIR/empty_loop.rs:15:9 + --> $DIR/empty_loop.rs:17:9 | LL | 'inner: loop {} | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/enum_clike_unportable_variant.rs b/tests/ui/enum_clike_unportable_variant.rs index 964e5634ddb11..c50404c5047bb 100644 --- a/tests/ui/enum_clike_unportable_variant.rs +++ b/tests/ui/enum_clike_unportable_variant.rs @@ -1,4 +1,4 @@ -//@ignore-target-x86 +//@ignore-32bit #![warn(clippy::enum_clike_unportable_variant)] #![allow(unused, non_upper_case_globals)] diff --git a/tests/ui/enum_clike_unportable_variant.stderr b/tests/ui/enum_clike_unportable_variant.stderr index 5935eea5e036e..93ad4daa97f77 100644 --- a/tests/ui/enum_clike_unportable_variant.stderr +++ b/tests/ui/enum_clike_unportable_variant.stderr @@ -5,51 +5,52 @@ LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::enum_clike_unportable_variant)]` error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:15:5 + --> $DIR/enum_clike_unportable_variant.rs:17:5 | LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:18:5 + --> $DIR/enum_clike_unportable_variant.rs:21:5 | LL | A = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:25:5 + --> $DIR/enum_clike_unportable_variant.rs:29:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:26:5 + --> $DIR/enum_clike_unportable_variant.rs:31:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:28:5 + --> $DIR/enum_clike_unportable_variant.rs:34:5 | LL | C = (i32::MIN as isize) - 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:34:5 + --> $DIR/enum_clike_unportable_variant.rs:41:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:35:5 + --> $DIR/enum_clike_unportable_variant.rs:43:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:40:5 + --> $DIR/enum_clike_unportable_variant.rs:49:5 | LL | X = ::Number, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index 86bee11eb8774..12158d0d12a2e 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -205,7 +205,7 @@ fn main() { } } - f_str(&&ref_str); // `needless_borrow` will suggest removing both references + f_str(&ref_str); // `needless_borrow` will suggest removing both references f_str(&ref_str); // `needless_borrow` will suggest removing only one reference let x = &&40; @@ -293,4 +293,32 @@ fn main() { fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc { *x } + + // Issue #11366 + let _: &mut u32 = match &mut Some(&mut 0u32) { + Some(x) => x, + None => panic!(), + }; + + // Issue #11474 + pub struct Variant { + pub anonymous: Variant0, + } + + pub union Variant0 { + pub anonymous: std::mem::ManuallyDrop, + } + + pub struct Variant00 { + pub anonymous: Variant000, + } + + pub union Variant000 { + pub val: i32, + } + + unsafe { + let mut p = core::mem::zeroed::(); + (*p.anonymous.anonymous).anonymous.val = 1; + } } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index 7a505bdf558c3..dec021c183443 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -293,4 +293,32 @@ fn main() { fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc { *x } + + // Issue #11366 + let _: &mut u32 = match &mut Some(&mut 0u32) { + Some(x) => &mut *x, + None => panic!(), + }; + + // Issue #11474 + pub struct Variant { + pub anonymous: Variant0, + } + + pub union Variant0 { + pub anonymous: std::mem::ManuallyDrop, + } + + pub struct Variant00 { + pub anonymous: Variant000, + } + + pub union Variant000 { + pub val: i32, + } + + unsafe { + let mut p = core::mem::zeroed::(); + (*p.anonymous.anonymous).anonymous.val = 1; + } } diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr index bea014d8ad555..3d2a7b0d9f4f3 100644 --- a/tests/ui/explicit_auto_deref.stderr +++ b/tests/ui/explicit_auto_deref.stderr @@ -194,10 +194,10 @@ LL | let _ = f_str(**ref_ref_str); | ^^^^^^^^^^^^^ help: try: `ref_ref_str` error: deref which would be done by auto-deref - --> $DIR/explicit_auto_deref.rs:208:13 + --> $DIR/explicit_auto_deref.rs:208:12 | LL | f_str(&&*ref_str); // `needless_borrow` will suggest removing both references - | ^^^^^^^^ help: try: `ref_str` + | ^^^^^^^^^ help: try: `ref_str` error: deref which would be done by auto-deref --> $DIR/explicit_auto_deref.rs:209:12 @@ -235,5 +235,11 @@ error: deref which would be done by auto-deref LL | *x | ^^ help: try: `x` -error: aborting due to 39 previous errors +error: deref which would be done by auto-deref + --> $DIR/explicit_auto_deref.rs:299:20 + | +LL | Some(x) => &mut *x, + | ^^^^^^^ help: try: `x` + +error: aborting due to 40 previous errors diff --git a/tests/ui/explicit_iter_loop.fixed b/tests/ui/explicit_iter_loop.fixed index 57292114e3882..f08397defa539 100644 --- a/tests/ui/explicit_iter_loop.fixed +++ b/tests/ui/explicit_iter_loop.fixed @@ -4,6 +4,7 @@ clippy::similar_names, clippy::needless_borrow, clippy::deref_addrof, + clippy::unnecessary_mut_passed, dead_code )] @@ -20,15 +21,15 @@ fn main() { for _ in rvec {} let rmvec = &mut vec; - for _ in &*rmvec {} - for _ in &mut *rmvec {} + for _ in rmvec.iter() {} + for _ in rmvec.iter_mut() {} for _ in &vec {} // these are fine for _ in &mut vec {} // these are fine for _ in &[1, 2, 3] {} - for _ in &*(&mut [1, 2, 3]) {} + for _ in (&mut [1, 2, 3]).iter() {} for _ in &[0; 32] {} for _ in &[0; 33] {} diff --git a/tests/ui/explicit_iter_loop.rs b/tests/ui/explicit_iter_loop.rs index 66280c23843d2..2ee6825d445c2 100644 --- a/tests/ui/explicit_iter_loop.rs +++ b/tests/ui/explicit_iter_loop.rs @@ -4,6 +4,7 @@ clippy::similar_names, clippy::needless_borrow, clippy::deref_addrof, + clippy::unnecessary_mut_passed, dead_code )] diff --git a/tests/ui/explicit_iter_loop.stderr b/tests/ui/explicit_iter_loop.stderr index af46d74e989af..725d9b63cf8d6 100644 --- a/tests/ui/explicit_iter_loop.stderr +++ b/tests/ui/explicit_iter_loop.stderr @@ -1,5 +1,5 @@ error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:16:14 + --> $DIR/explicit_iter_loop.rs:17:14 | LL | for _ in vec.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `&vec` @@ -11,133 +11,106 @@ LL | #![deny(clippy::explicit_iter_loop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:17:14 + --> $DIR/explicit_iter_loop.rs:18:14 | LL | for _ in vec.iter_mut() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:20:14 + --> $DIR/explicit_iter_loop.rs:21:14 | LL | for _ in rvec.iter() {} | ^^^^^^^^^^^ help: to write this more concisely, try: `rvec` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:23:14 - | -LL | for _ in rmvec.iter() {} - | ^^^^^^^^^^^^ help: to write this more concisely, try: `&*rmvec` - -error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:24:14 - | -LL | for _ in rmvec.iter_mut() {} - | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *rmvec` - -error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:29:14 + --> $DIR/explicit_iter_loop.rs:30:14 | LL | for _ in [1, 2, 3].iter() {} | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:31:14 - | -LL | for _ in (&mut [1, 2, 3]).iter() {} - | ^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&*(&mut [1, 2, 3])` - -error: the method `iter` doesn't need a mutable reference - --> $DIR/explicit_iter_loop.rs:31:14 - | -LL | for _ in (&mut [1, 2, 3]).iter() {} - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` - -error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:33:14 + --> $DIR/explicit_iter_loop.rs:34:14 | LL | for _ in [0; 32].iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:34:14 + --> $DIR/explicit_iter_loop.rs:35:14 | LL | for _ in [0; 33].iter() {} | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 33]` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:37:14 + --> $DIR/explicit_iter_loop.rs:38:14 | LL | for _ in ll.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&ll` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:39:14 + --> $DIR/explicit_iter_loop.rs:40:14 | LL | for _ in rll.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `rll` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:42:14 + --> $DIR/explicit_iter_loop.rs:43:14 | LL | for _ in vd.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&vd` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:44:14 + --> $DIR/explicit_iter_loop.rs:45:14 | LL | for _ in rvd.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `rvd` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:47:14 + --> $DIR/explicit_iter_loop.rs:48:14 | LL | for _ in bh.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bh` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:50:14 + --> $DIR/explicit_iter_loop.rs:51:14 | LL | for _ in hm.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hm` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:53:14 + --> $DIR/explicit_iter_loop.rs:54:14 | LL | for _ in bt.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bt` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:56:14 + --> $DIR/explicit_iter_loop.rs:57:14 | LL | for _ in hs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&hs` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:59:14 + --> $DIR/explicit_iter_loop.rs:60:14 | LL | for _ in bs.iter() {} | ^^^^^^^^^ help: to write this more concisely, try: `&bs` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:148:14 + --> $DIR/explicit_iter_loop.rs:149:14 | LL | for _ in x.iter() {} | ^^^^^^^^ help: to write this more concisely, try: `&x` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:149:14 + --> $DIR/explicit_iter_loop.rs:150:14 | LL | for _ in x.iter_mut() {} | ^^^^^^^^^^^^ help: to write this more concisely, try: `&mut x` error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/explicit_iter_loop.rs:152:14 + --> $DIR/explicit_iter_loop.rs:153:14 | LL | for _ in r.iter() {} | ^^^^^^^^ help: to write this more concisely, try: `r` -error: aborting due to 22 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs index a547a67430fd2..5057c64373291 100644 --- a/tests/ui/float_cmp.rs +++ b/tests/ui/float_cmp.rs @@ -41,6 +41,16 @@ impl PartialEq for X { } } +impl PartialEq for X { + fn eq(&self, o: &f32) -> bool { + if self.val.is_nan() { + o.is_nan() + } else { + self.val == *o // no error, inside "eq" fn + } + } +} + fn main() { ZERO == 0f32; //no error, comparison with zero is ok 1.0f32 != f32::INFINITY; // also comparison with infinity @@ -48,6 +58,9 @@ fn main() { ZERO == 0.0; //no error, comparison with zero is ok ZERO + ZERO != 1.0; //no error, comparison with zero is ok + let x = X { val: 1.0 }; + x == 1.0; // no error, custom type that implement PartialOrder for float is not checked + ONE == 1f32; ONE == 1.0 + 0.0; ONE + ONE == ZERO + ONE + ONE; diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index cbe529954d0c2..217e2987917ab 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:57:5 + --> $DIR/float_cmp.rs:70:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -9,7 +9,7 @@ LL | ONE as f64 != 2.0; = help: to override `-D warnings` add `#[allow(clippy::float_cmp)]` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:64:5 + --> $DIR/float_cmp.rs:77:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -17,7 +17,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:69:5 + --> $DIR/float_cmp.rs:82:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -25,7 +25,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:91:5 + --> $DIR/float_cmp.rs:104:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -33,7 +33,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> $DIR/float_cmp.rs:98:5 + --> $DIR/float_cmp.rs:111:5 | LL | a1 == a2; | ^^^^^^^^ @@ -41,7 +41,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:101:5 + --> $DIR/float_cmp.rs:114:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` diff --git a/tests/ui/fn_to_numeric_cast_32bit.stderr b/tests/ui/fn_to_numeric_cast.32bit.stderr similarity index 70% rename from tests/ui/fn_to_numeric_cast_32bit.stderr rename to tests/ui/fn_to_numeric_cast.32bit.stderr index 671347d2bcd54..ea08d8c9cc162 100644 --- a/tests/ui/fn_to_numeric_cast_32bit.stderr +++ b/tests/ui/fn_to_numeric_cast.32bit.stderr @@ -1,141 +1,143 @@ error: casting function pointer `foo` to `i8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:10:13 + --> $DIR/fn_to_numeric_cast.rs:10:13 | LL | let _ = foo as i8; | ^^^^^^^^^ help: try: `foo as usize` | = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]` error: casting function pointer `foo` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:11:13 + --> $DIR/fn_to_numeric_cast.rs:11:13 | LL | let _ = foo as i16; | ^^^^^^^^^^ help: try: `foo as usize` -error: casting function pointer `foo` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:12:13 +error: casting function pointer `foo` to `i32` + --> $DIR/fn_to_numeric_cast.rs:12:13 | LL | let _ = foo as i32; | ^^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]` error: casting function pointer `foo` to `i64` - --> $DIR/fn_to_numeric_cast_32bit.rs:13:13 + --> $DIR/fn_to_numeric_cast.rs:13:13 | LL | let _ = foo as i64; | ^^^^^^^^^^ help: try: `foo as usize` - | - = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` error: casting function pointer `foo` to `i128` - --> $DIR/fn_to_numeric_cast_32bit.rs:14:13 + --> $DIR/fn_to_numeric_cast.rs:14:13 | LL | let _ = foo as i128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `isize` - --> $DIR/fn_to_numeric_cast_32bit.rs:15:13 + --> $DIR/fn_to_numeric_cast.rs:15:13 | LL | let _ = foo as isize; | ^^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:17:13 + --> $DIR/fn_to_numeric_cast.rs:17:13 | LL | let _ = foo as u8; | ^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:18:13 + --> $DIR/fn_to_numeric_cast.rs:18:13 | LL | let _ = foo as u16; | ^^^^^^^^^^ help: try: `foo as usize` -error: casting function pointer `foo` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:19:13 +error: casting function pointer `foo` to `u32` + --> $DIR/fn_to_numeric_cast.rs:19:13 | LL | let _ = foo as u32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u64` - --> $DIR/fn_to_numeric_cast_32bit.rs:20:13 + --> $DIR/fn_to_numeric_cast.rs:20:13 | LL | let _ = foo as u64; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u128` - --> $DIR/fn_to_numeric_cast_32bit.rs:21:13 + --> $DIR/fn_to_numeric_cast.rs:21:13 | LL | let _ = foo as u128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `abc` to `i8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:34:13 + --> $DIR/fn_to_numeric_cast.rs:34:13 | LL | let _ = abc as i8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:35:13 + --> $DIR/fn_to_numeric_cast.rs:35:13 | LL | let _ = abc as i16; | ^^^^^^^^^^ help: try: `abc as usize` -error: casting function pointer `abc` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:36:13 +error: casting function pointer `abc` to `i32` + --> $DIR/fn_to_numeric_cast.rs:36:13 | LL | let _ = abc as i32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i64` - --> $DIR/fn_to_numeric_cast_32bit.rs:37:13 + --> $DIR/fn_to_numeric_cast.rs:37:13 | LL | let _ = abc as i64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i128` - --> $DIR/fn_to_numeric_cast_32bit.rs:38:13 + --> $DIR/fn_to_numeric_cast.rs:38:13 | LL | let _ = abc as i128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `isize` - --> $DIR/fn_to_numeric_cast_32bit.rs:39:13 + --> $DIR/fn_to_numeric_cast.rs:39:13 | LL | let _ = abc as isize; | ^^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:41:13 + --> $DIR/fn_to_numeric_cast.rs:41:13 | LL | let _ = abc as u8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:42:13 + --> $DIR/fn_to_numeric_cast.rs:42:13 | LL | let _ = abc as u16; | ^^^^^^^^^^ help: try: `abc as usize` -error: casting function pointer `abc` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:43:13 +error: casting function pointer `abc` to `u32` + --> $DIR/fn_to_numeric_cast.rs:43:13 | LL | let _ = abc as u32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u64` - --> $DIR/fn_to_numeric_cast_32bit.rs:44:13 + --> $DIR/fn_to_numeric_cast.rs:44:13 | LL | let _ = abc as u64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u128` - --> $DIR/fn_to_numeric_cast_32bit.rs:45:13 + --> $DIR/fn_to_numeric_cast.rs:45:13 | LL | let _ = abc as u128; | ^^^^^^^^^^^ help: try: `abc as usize` -error: casting function pointer `f` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:52:5 +error: casting function pointer `f` to `i32` + --> $DIR/fn_to_numeric_cast.rs:52:5 | LL | f as i32 | ^^^^^^^^ help: try: `f as usize` diff --git a/tests/ui/fn_to_numeric_cast.stderr b/tests/ui/fn_to_numeric_cast.64bit.stderr similarity index 85% rename from tests/ui/fn_to_numeric_cast.stderr rename to tests/ui/fn_to_numeric_cast.64bit.stderr index 53e8ac3c4b420..62f3bfa70ea50 100644 --- a/tests/ui/fn_to_numeric_cast.stderr +++ b/tests/ui/fn_to_numeric_cast.64bit.stderr @@ -8,19 +8,19 @@ LL | let _ = foo as i8; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]` error: casting function pointer `foo` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:13:13 + --> $DIR/fn_to_numeric_cast.rs:11:13 | LL | let _ = foo as i16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:15:13 + --> $DIR/fn_to_numeric_cast.rs:12:13 | LL | let _ = foo as i32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i64` - --> $DIR/fn_to_numeric_cast.rs:17:13 + --> $DIR/fn_to_numeric_cast.rs:13:13 | LL | let _ = foo as i64; | ^^^^^^^^^^ help: try: `foo as usize` @@ -29,115 +29,115 @@ LL | let _ = foo as i64; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]` error: casting function pointer `foo` to `i128` - --> $DIR/fn_to_numeric_cast.rs:20:13 + --> $DIR/fn_to_numeric_cast.rs:14:13 | LL | let _ = foo as i128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `isize` - --> $DIR/fn_to_numeric_cast.rs:22:13 + --> $DIR/fn_to_numeric_cast.rs:15:13 | LL | let _ = foo as isize; | ^^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:25:13 + --> $DIR/fn_to_numeric_cast.rs:17:13 | LL | let _ = foo as u8; | ^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:27:13 + --> $DIR/fn_to_numeric_cast.rs:18:13 | LL | let _ = foo as u16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:29:13 + --> $DIR/fn_to_numeric_cast.rs:19:13 | LL | let _ = foo as u32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u64` - --> $DIR/fn_to_numeric_cast.rs:31:13 + --> $DIR/fn_to_numeric_cast.rs:20:13 | LL | let _ = foo as u64; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u128` - --> $DIR/fn_to_numeric_cast.rs:33:13 + --> $DIR/fn_to_numeric_cast.rs:21:13 | LL | let _ = foo as u128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `abc` to `i8`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:47:13 + --> $DIR/fn_to_numeric_cast.rs:34:13 | LL | let _ = abc as i8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:49:13 + --> $DIR/fn_to_numeric_cast.rs:35:13 | LL | let _ = abc as i16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:51:13 + --> $DIR/fn_to_numeric_cast.rs:36:13 | LL | let _ = abc as i32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i64` - --> $DIR/fn_to_numeric_cast.rs:53:13 + --> $DIR/fn_to_numeric_cast.rs:37:13 | LL | let _ = abc as i64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i128` - --> $DIR/fn_to_numeric_cast.rs:55:13 + --> $DIR/fn_to_numeric_cast.rs:38:13 | LL | let _ = abc as i128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `isize` - --> $DIR/fn_to_numeric_cast.rs:57:13 + --> $DIR/fn_to_numeric_cast.rs:39:13 | LL | let _ = abc as isize; | ^^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:60:13 + --> $DIR/fn_to_numeric_cast.rs:41:13 | LL | let _ = abc as u8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:62:13 + --> $DIR/fn_to_numeric_cast.rs:42:13 | LL | let _ = abc as u16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:64:13 + --> $DIR/fn_to_numeric_cast.rs:43:13 | LL | let _ = abc as u32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u64` - --> $DIR/fn_to_numeric_cast.rs:66:13 + --> $DIR/fn_to_numeric_cast.rs:44:13 | LL | let _ = abc as u64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u128` - --> $DIR/fn_to_numeric_cast.rs:68:13 + --> $DIR/fn_to_numeric_cast.rs:45:13 | LL | let _ = abc as u128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `f` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:76:5 + --> $DIR/fn_to_numeric_cast.rs:52:5 | LL | f as i32 | ^^^^^^^^ help: try: `f as usize` diff --git a/tests/ui/fn_to_numeric_cast.rs b/tests/ui/fn_to_numeric_cast.rs index 09128d8176e08..9501eb5da4b20 100644 --- a/tests/ui/fn_to_numeric_cast.rs +++ b/tests/ui/fn_to_numeric_cast.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@stderr-per-bitwidth //@no-rustfix #![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] @@ -8,30 +8,17 @@ fn foo() -> String { fn test_function_to_numeric_cast() { let _ = foo as i8; - //~^ ERROR: casting function pointer `foo` to `i8`, which truncates the value - //~| NOTE: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` let _ = foo as i16; - //~^ ERROR: casting function pointer `foo` to `i16`, which truncates the value let _ = foo as i32; - //~^ ERROR: casting function pointer `foo` to `i32`, which truncates the value let _ = foo as i64; - //~^ ERROR: casting function pointer `foo` to `i64` - //~| NOTE: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` let _ = foo as i128; - //~^ ERROR: casting function pointer `foo` to `i128` let _ = foo as isize; - //~^ ERROR: casting function pointer `foo` to `isize` let _ = foo as u8; - //~^ ERROR: casting function pointer `foo` to `u8`, which truncates the value let _ = foo as u16; - //~^ ERROR: casting function pointer `foo` to `u16`, which truncates the value let _ = foo as u32; - //~^ ERROR: casting function pointer `foo` to `u32`, which truncates the value let _ = foo as u64; - //~^ ERROR: casting function pointer `foo` to `u64` let _ = foo as u128; - //~^ ERROR: casting function pointer `foo` to `u128` // Casting to usize is OK and should not warn let _ = foo as usize; @@ -45,28 +32,17 @@ fn test_function_var_to_numeric_cast() { let abc: fn() -> String = foo; let _ = abc as i8; - //~^ ERROR: casting function pointer `abc` to `i8`, which truncates the value let _ = abc as i16; - //~^ ERROR: casting function pointer `abc` to `i16`, which truncates the value let _ = abc as i32; - //~^ ERROR: casting function pointer `abc` to `i32`, which truncates the value let _ = abc as i64; - //~^ ERROR: casting function pointer `abc` to `i64` let _ = abc as i128; - //~^ ERROR: casting function pointer `abc` to `i128` let _ = abc as isize; - //~^ ERROR: casting function pointer `abc` to `isize` let _ = abc as u8; - //~^ ERROR: casting function pointer `abc` to `u8`, which truncates the value let _ = abc as u16; - //~^ ERROR: casting function pointer `abc` to `u16`, which truncates the value let _ = abc as u32; - //~^ ERROR: casting function pointer `abc` to `u32`, which truncates the value let _ = abc as u64; - //~^ ERROR: casting function pointer `abc` to `u64` let _ = abc as u128; - //~^ ERROR: casting function pointer `abc` to `u128` // Casting to usize is OK and should not warn let _ = abc as usize; @@ -74,7 +50,6 @@ fn test_function_var_to_numeric_cast() { fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { f as i32 - //~^ ERROR: casting function pointer `f` to `i32`, which truncates the value } fn main() {} diff --git a/tests/ui/fn_to_numeric_cast_32bit.rs b/tests/ui/fn_to_numeric_cast_32bit.rs deleted file mode 100644 index 93e9361f4dc5b..0000000000000 --- a/tests/ui/fn_to_numeric_cast_32bit.rs +++ /dev/null @@ -1,80 +0,0 @@ -//@ignore-64bit - -#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] - -fn foo() -> String { - String::new() -} - -fn test_function_to_numeric_cast() { - let _ = foo as i8; - //~^ ERROR: casting function pointer `foo` to `i8`, which truncates the value - //~| NOTE: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` - let _ = foo as i16; - //~^ ERROR: casting function pointer `foo` to `i16`, which truncates the value - let _ = foo as i32; - //~^ ERROR: casting function pointer `foo` to `i32`, which truncates the value - let _ = foo as i64; - //~^ ERROR: casting function pointer `foo` to `i64` - //~| NOTE: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` - let _ = foo as i128; - //~^ ERROR: casting function pointer `foo` to `i128` - let _ = foo as isize; - //~^ ERROR: casting function pointer `foo` to `isize` - - let _ = foo as u8; - //~^ ERROR: casting function pointer `foo` to `u8`, which truncates the value - let _ = foo as u16; - //~^ ERROR: casting function pointer `foo` to `u16`, which truncates the value - let _ = foo as u32; - //~^ ERROR: casting function pointer `foo` to `u32`, which truncates the value - let _ = foo as u64; - //~^ ERROR: casting function pointer `foo` to `u64` - let _ = foo as u128; - //~^ ERROR: casting function pointer `foo` to `u128` - - // Casting to usize is OK and should not warn - let _ = foo as usize; - - // Cast `f` (a `FnDef`) to `fn()` should not warn - fn f() {} - let _ = f as fn(); -} - -fn test_function_var_to_numeric_cast() { - let abc: fn() -> String = foo; - - let _ = abc as i8; - //~^ ERROR: casting function pointer `abc` to `i8`, which truncates the value - let _ = abc as i16; - //~^ ERROR: casting function pointer `abc` to `i16`, which truncates the value - let _ = abc as i32; - //~^ ERROR: casting function pointer `abc` to `i32`, which truncates the value - let _ = abc as i64; - //~^ ERROR: casting function pointer `abc` to `i64` - let _ = abc as i128; - //~^ ERROR: casting function pointer `abc` to `i128` - let _ = abc as isize; - //~^ ERROR: casting function pointer `abc` to `isize` - - let _ = abc as u8; - //~^ ERROR: casting function pointer `abc` to `u8`, which truncates the value - let _ = abc as u16; - //~^ ERROR: casting function pointer `abc` to `u16`, which truncates the value - let _ = abc as u32; - //~^ ERROR: casting function pointer `abc` to `u32`, which truncates the value - let _ = abc as u64; - //~^ ERROR: casting function pointer `abc` to `u64` - let _ = abc as u128; - //~^ ERROR: casting function pointer `abc` to `u128` - - // Casting to usize is OK and should not warn - let _ = abc as usize; -} - -fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { - f as i32 - //~^ ERROR: casting function pointer `f` to `i32`, which truncates the value -} - -fn main() {} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 8fa0f34a6c423..77abd663e0a6e 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -117,3 +117,15 @@ fn f(b: bool, v: Option<()>) -> Option<()> { None } } + +fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { + let x = if b { + #[allow(clippy::let_unit_value)] + let _ = v?; + Some(()) + } else { + None + }; + + Ok(()) +} diff --git a/tests/ui/ignored_unit_patterns.fixed b/tests/ui/ignored_unit_patterns.fixed index 2912eede4d9b6..6c6f21fee16b0 100644 --- a/tests/ui/ignored_unit_patterns.fixed +++ b/tests/ui/ignored_unit_patterns.fixed @@ -1,5 +1,5 @@ #![warn(clippy::ignored_unit_patterns)] -#![allow(clippy::redundant_pattern_matching, clippy::single_match)] +#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] fn foo() -> Result<(), ()> { unimplemented!() @@ -7,9 +7,19 @@ fn foo() -> Result<(), ()> { fn main() { match foo() { - Ok(()) => {}, - Err(()) => {}, + Ok(()) => {}, //~ ERROR: matching over `()` is more explicit + Err(()) => {}, //~ ERROR: matching over `()` is more explicit } if let Ok(()) = foo() {} + //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|()| todo!()); + //~^ ERROR: matching over `()` is more explicit +} + +#[allow(unused)] +pub fn moo(_: ()) { + let () = foo().unwrap(); + //~^ ERROR: matching over `()` is more explicit + let _: () = foo().unwrap(); + let _: () = (); } diff --git a/tests/ui/ignored_unit_patterns.rs b/tests/ui/ignored_unit_patterns.rs index d180cd8d2fd36..5e8c2e03ba2cb 100644 --- a/tests/ui/ignored_unit_patterns.rs +++ b/tests/ui/ignored_unit_patterns.rs @@ -1,5 +1,5 @@ #![warn(clippy::ignored_unit_patterns)] -#![allow(clippy::redundant_pattern_matching, clippy::single_match)] +#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] fn foo() -> Result<(), ()> { unimplemented!() @@ -7,9 +7,19 @@ fn foo() -> Result<(), ()> { fn main() { match foo() { - Ok(_) => {}, - Err(_) => {}, + Ok(_) => {}, //~ ERROR: matching over `()` is more explicit + Err(_) => {}, //~ ERROR: matching over `()` is more explicit } if let Ok(_) = foo() {} + //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|_| todo!()); + //~^ ERROR: matching over `()` is more explicit +} + +#[allow(unused)] +pub fn moo(_: ()) { + let _ = foo().unwrap(); + //~^ ERROR: matching over `()` is more explicit + let _: () = foo().unwrap(); + let _: () = (); } diff --git a/tests/ui/ignored_unit_patterns.stderr b/tests/ui/ignored_unit_patterns.stderr index a8ced22397dbb..df5e1d89e906e 100644 --- a/tests/ui/ignored_unit_patterns.stderr +++ b/tests/ui/ignored_unit_patterns.stderr @@ -20,10 +20,16 @@ LL | if let Ok(_) = foo() {} | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:14:28 + --> $DIR/ignored_unit_patterns.rs:15:28 | LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` -error: aborting due to 4 previous errors +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:21:9 + | +LL | let _ = foo().unwrap(); + | ^ help: use `()` instead of `_`: `()` + +error: aborting due to 5 previous errors diff --git a/tests/ui/implied_bounds_in_impls.fixed b/tests/ui/implied_bounds_in_impls.fixed index 952c2b70619bb..a50fa0ccf6e7f 100644 --- a/tests/ui/implied_bounds_in_impls.fixed +++ b/tests/ui/implied_bounds_in_impls.fixed @@ -65,4 +65,61 @@ impl SomeTrait for SomeStruct { } } +mod issue11422 { + use core::fmt::Debug; + // Some additional tests that would cause ICEs: + + // `PartialOrd` has a default generic parameter and does not need to be explicitly specified. + // This needs special handling. + fn default_generic_param1() -> impl PartialOrd + Debug {} + fn default_generic_param2() -> impl PartialOrd + Debug {} + + // Referring to `Self` in the supertrait clause needs special handling. + trait Trait1 {} + trait Trait2: Trait1 {} + impl Trait1<()> for () {} + impl Trait2 for () {} + + fn f() -> impl Trait1<()> + Trait2 {} +} + +mod issue11435 { + // Associated type needs to be included on DoubleEndedIterator in the suggestion + fn my_iter() -> impl DoubleEndedIterator { + 0..5 + } + + // Removing the `Clone` bound should include the `+` behind it in its remove suggestion + fn f() -> impl Copy { + 1 + } + + trait Trait1 { + type U; + } + impl Trait1 for () { + type U = i64; + } + trait Trait2: Trait1 {} + impl Trait2 for () {} + + // When the other trait has generics, it shouldn't add another pair of `<>` + fn f2() -> impl Trait2 {} + + trait Trait3 { + type X; + type Y; + } + trait Trait4: Trait3 {} + impl Trait3 for () { + type X = i32; + type Y = i128; + } + impl Trait4 for () {} + + // Associated type `X` is specified, but `Y` is not, so only that associated type should be moved + // over + fn f3() -> impl Trait4 {} +} + fn main() {} diff --git a/tests/ui/implied_bounds_in_impls.rs b/tests/ui/implied_bounds_in_impls.rs index 475f2621bbf62..e74ed4425b81e 100644 --- a/tests/ui/implied_bounds_in_impls.rs +++ b/tests/ui/implied_bounds_in_impls.rs @@ -65,4 +65,61 @@ impl SomeTrait for SomeStruct { } } +mod issue11422 { + use core::fmt::Debug; + // Some additional tests that would cause ICEs: + + // `PartialOrd` has a default generic parameter and does not need to be explicitly specified. + // This needs special handling. + fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {} + fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {} + + // Referring to `Self` in the supertrait clause needs special handling. + trait Trait1 {} + trait Trait2: Trait1 {} + impl Trait1<()> for () {} + impl Trait2 for () {} + + fn f() -> impl Trait1<()> + Trait2 {} +} + +mod issue11435 { + // Associated type needs to be included on DoubleEndedIterator in the suggestion + fn my_iter() -> impl Iterator + DoubleEndedIterator { + 0..5 + } + + // Removing the `Clone` bound should include the `+` behind it in its remove suggestion + fn f() -> impl Copy + Clone { + 1 + } + + trait Trait1 { + type U; + } + impl Trait1 for () { + type U = i64; + } + trait Trait2: Trait1 {} + impl Trait2 for () {} + + // When the other trait has generics, it shouldn't add another pair of `<>` + fn f2() -> impl Trait1 + Trait2 {} + + trait Trait3 { + type X; + type Y; + } + trait Trait4: Trait3 {} + impl Trait3 for () { + type X = i32; + type Y = i128; + } + impl Trait4 for () {} + + // Associated type `X` is specified, but `Y` is not, so only that associated type should be moved + // over + fn f3() -> impl Trait3 + Trait4 {} +} + fn main() {} diff --git a/tests/ui/implied_bounds_in_impls.stderr b/tests/ui/implied_bounds_in_impls.stderr index e2b1ecb9f1ec7..72dc2a183a383 100644 --- a/tests/ui/implied_bounds_in_impls.stderr +++ b/tests/ui/implied_bounds_in_impls.stderr @@ -120,5 +120,77 @@ LL - fn f() -> impl Deref + DerefMut { LL + fn f() -> impl DerefMut { | -error: aborting due to 10 previous errors +error: this bound is already specified as the supertrait of `PartialOrd` + --> $DIR/implied_bounds_in_impls.rs:74:41 + | +LL | fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {} + | ^^^^^^^^^ + | +help: try removing this bound + | +LL - fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {} +LL + fn default_generic_param1() -> impl PartialOrd + Debug {} + | + +error: this bound is already specified as the supertrait of `PartialOrd` + --> $DIR/implied_bounds_in_impls.rs:75:54 + | +LL | fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {} + | ^^^^^^^^^ + | +help: try removing this bound + | +LL - fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {} +LL + fn default_generic_param2() -> impl PartialOrd + Debug {} + | + +error: this bound is already specified as the supertrait of `DoubleEndedIterator` + --> $DIR/implied_bounds_in_impls.rs:88:26 + | +LL | fn my_iter() -> impl Iterator + DoubleEndedIterator { + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn my_iter() -> impl Iterator + DoubleEndedIterator { +LL + fn my_iter() -> impl DoubleEndedIterator { + | + +error: this bound is already specified as the supertrait of `Copy` + --> $DIR/implied_bounds_in_impls.rs:93:27 + | +LL | fn f() -> impl Copy + Clone { + | ^^^^^ + | +help: try removing this bound + | +LL - fn f() -> impl Copy + Clone { +LL + fn f() -> impl Copy { + | + +error: this bound is already specified as the supertrait of `Trait2` + --> $DIR/implied_bounds_in_impls.rs:107:21 + | +LL | fn f2() -> impl Trait1 + Trait2 {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn f2() -> impl Trait1 + Trait2 {} +LL + fn f2() -> impl Trait2 {} + | + +error: this bound is already specified as the supertrait of `Trait4` + --> $DIR/implied_bounds_in_impls.rs:122:21 + | +LL | fn f3() -> impl Trait3 + Trait4 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn f3() -> impl Trait3 + Trait4 {} +LL + fn f3() -> impl Trait4 {} + | + +error: aborting due to 16 previous errors diff --git a/tests/ui/iter_out_of_bounds.rs b/tests/ui/iter_out_of_bounds.rs new file mode 100644 index 0000000000000..3cfe6e82fc1e3 --- /dev/null +++ b/tests/ui/iter_out_of_bounds.rs @@ -0,0 +1,71 @@ +//@no-rustfix + +#![deny(clippy::iter_out_of_bounds)] +#![allow(clippy::useless_vec)] + +fn opaque_empty_iter() -> impl Iterator { + std::iter::empty() +} + +fn main() { + #[allow(clippy::never_loop)] + for _ in [1, 2, 3].iter().skip(4) { + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + unreachable!(); + } + for (i, _) in [1, 2, 3].iter().take(4).enumerate() { + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + assert!(i <= 2); + } + + #[allow(clippy::needless_borrow)] + for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + + for _ in [1, 2, 3].iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + for _ in [1; 3].iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + // Should not lint + for _ in opaque_empty_iter().skip(1) {} + + for _ in vec![1, 2, 3].iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + for _ in vec![1; 3].iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + let x = [1, 2, 3]; + for _ in x.iter().skip(4) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + let n = 4; + for _ in x.iter().skip(n) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + let empty = std::iter::empty::; + + for _ in empty().skip(1) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + for _ in empty().take(1) {} + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + + for _ in std::iter::once(1).skip(2) {} + //~^ ERROR: this `.skip()` call skips more items than the iterator will produce + + for _ in std::iter::once(1).take(2) {} + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + + for x in [].iter().take(1) { + //~^ ERROR: this `.take()` call takes more items than the iterator will produce + let _: &i32 = x; + } + + // ok, not out of bounds + for _ in [1].iter().take(1) {} + for _ in [1, 2, 3].iter().take(2) {} + for _ in [1, 2, 3].iter().skip(2) {} +} diff --git a/tests/ui/iter_out_of_bounds.stderr b/tests/ui/iter_out_of_bounds.stderr new file mode 100644 index 0000000000000..f235faec8e59c --- /dev/null +++ b/tests/ui/iter_out_of_bounds.stderr @@ -0,0 +1,119 @@ +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:12:14 + | +LL | for _ in [1, 2, 3].iter().skip(4) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator +note: the lint level is defined here + --> $DIR/iter_out_of_bounds.rs:3:9 + | +LL | #![deny(clippy::iter_out_of_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:16:19 + | +LL | for (i, _) in [1, 2, 3].iter().take(4).enumerate() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:22:14 + | +LL | for _ in (&&&&&&[1, 2, 3]).iter().take(4) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:25:14 + | +LL | for _ in [1, 2, 3].iter().skip(4) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:28:14 + | +LL | for _ in [1; 3].iter().skip(4) {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:34:14 + | +LL | for _ in vec![1, 2, 3].iter().skip(4) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:37:14 + | +LL | for _ in vec![1; 3].iter().skip(4) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:41:14 + | +LL | for _ in x.iter().skip(4) {} + | ^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:45:14 + | +LL | for _ in x.iter().skip(n) {} + | ^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:50:14 + | +LL | for _ in empty().skip(1) {} + | ^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:53:14 + | +LL | for _ in empty().take(1) {} + | ^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: this `.skip()` call skips more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:56:14 + | +LL | for _ in std::iter::once(1).skip(2) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and will create an empty iterator + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:59:14 + | +LL | for _ in std::iter::once(1).take(2) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: this `.take()` call takes more items than the iterator will produce + --> $DIR/iter_out_of_bounds.rs:62:14 + | +LL | for x in [].iter().take(1) { + | ^^^^^^^^^^^^^^^^^ + | + = note: this operation is useless and the returned iterator will simply yield the same items + +error: aborting due to 14 previous errors + diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 9dd046fec1fee..7d8a584b0224c 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -63,11 +63,9 @@ fn main() { let _ = vec.iter().for_each(|x| assert!(!x.is_empty())); - // Not implemented yet - let _ = vec.iter().cloned().all(|x| x.len() == 1); + let _ = vec.iter().all(|x| x.len() == 1); - // Not implemented yet - let _ = vec.iter().cloned().any(|x| x.len() == 1); + let _ = vec.iter().any(|x| x.len() == 1); // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index 3cab366955481..58c374ab8cd10 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -64,10 +64,8 @@ fn main() { let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); - // Not implemented yet let _ = vec.iter().cloned().all(|x| x.len() == 1); - // Not implemented yet let _ = vec.iter().cloned().any(|x| x.len() == 1); // Should probably stay as it is. diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index fbcd33066d876..a9a739688ebf2 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -148,5 +148,21 @@ LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | | | help: try: `.for_each(|x| assert!(!x.is_empty()))` -error: aborting due to 17 previous errors +error: unneeded cloning of iterator items + --> $DIR/iter_overeager_cloned.rs:67:13 + | +LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); + | ^^^^^^^^^^------------------------------- + | | + | help: try: `.all(|x| x.len() == 1)` + +error: unneeded cloning of iterator items + --> $DIR/iter_overeager_cloned.rs:69:13 + | +LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); + | ^^^^^^^^^^------------------------------- + | | + | help: try: `.any(|x| x.len() == 1)` + +error: aborting due to 19 previous errors diff --git a/tests/ui/iter_skip_next.fixed b/tests/ui/iter_skip_next.fixed index 310b24a9cde17..3e41b36324902 100644 --- a/tests/ui/iter_skip_next.fixed +++ b/tests/ui/iter_skip_next.fixed @@ -4,6 +4,7 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::iter_nth)] #![allow(clippy::useless_vec)] +#![allow(clippy::iter_out_of_bounds)] #![allow(unused_mut, dead_code)] extern crate option_helpers; diff --git a/tests/ui/iter_skip_next.rs b/tests/ui/iter_skip_next.rs index 222d6a2a1848b..6d96441ca96f5 100644 --- a/tests/ui/iter_skip_next.rs +++ b/tests/ui/iter_skip_next.rs @@ -4,6 +4,7 @@ #![allow(clippy::disallowed_names)] #![allow(clippy::iter_nth)] #![allow(clippy::useless_vec)] +#![allow(clippy::iter_out_of_bounds)] #![allow(unused_mut, dead_code)] extern crate option_helpers; diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr index 0b6bf652b1f1f..39b173e758693 100644 --- a/tests/ui/iter_skip_next.stderr +++ b/tests/ui/iter_skip_next.stderr @@ -1,5 +1,5 @@ error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:16:28 + --> $DIR/iter_skip_next.rs:17:28 | LL | let _ = some_vec.iter().skip(42).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` @@ -8,37 +8,37 @@ LL | let _ = some_vec.iter().skip(42).next(); = help: to override `-D warnings` add `#[allow(clippy::iter_skip_next)]` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:17:36 + --> $DIR/iter_skip_next.rs:18:36 | LL | let _ = some_vec.iter().cycle().skip(42).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:18:20 + --> $DIR/iter_skip_next.rs:19:20 | LL | let _ = (1..10).skip(10).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:19:33 + --> $DIR/iter_skip_next.rs:20:33 | LL | let _ = &some_vec[..].iter().skip(3).next(); | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:27:26 + --> $DIR/iter_skip_next.rs:28:26 | LL | let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect(); | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:29:29 + --> $DIR/iter_skip_next.rs:30:29 | LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` error: called `skip(..).next()` on an iterator - --> $DIR/iter_skip_next.rs:35:29 + --> $DIR/iter_skip_next.rs:36:29 | LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect(); | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)` diff --git a/tests/ui/iter_skip_next_unfixable.rs b/tests/ui/iter_skip_next_unfixable.rs index 9c224f4117d3f..6c98bdc8c8808 100644 --- a/tests/ui/iter_skip_next_unfixable.rs +++ b/tests/ui/iter_skip_next_unfixable.rs @@ -1,5 +1,5 @@ #![warn(clippy::iter_skip_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::iter_out_of_bounds)] //@no-rustfix /// Checks implementation of `ITER_SKIP_NEXT` lint fn main() { diff --git a/tests/ui/iter_skip_zero.fixed b/tests/ui/iter_skip_zero.fixed index 62a83d5905b41..447d07100e953 100644 --- a/tests/ui/iter_skip_zero.fixed +++ b/tests/ui/iter_skip_zero.fixed @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs -#![allow(clippy::useless_vec, unused)] +#![allow(clippy::useless_vec, clippy::iter_out_of_bounds, unused)] #![warn(clippy::iter_skip_zero)] #[macro_use] diff --git a/tests/ui/iter_skip_zero.rs b/tests/ui/iter_skip_zero.rs index c96696dde65d4..ba63c3981808e 100644 --- a/tests/ui/iter_skip_zero.rs +++ b/tests/ui/iter_skip_zero.rs @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs -#![allow(clippy::useless_vec, unused)] +#![allow(clippy::useless_vec, clippy::iter_out_of_bounds, unused)] #![warn(clippy::iter_skip_zero)] #[macro_use] diff --git a/tests/ui/large_enum_variant.32bit.stderr b/tests/ui/large_enum_variant.32bit.stderr new file mode 100644 index 0000000000000..0e0eee21cf30a --- /dev/null +++ b/tests/ui/large_enum_variant.32bit.stderr @@ -0,0 +1,280 @@ +error: large size difference between variants + --> $DIR/large_enum_variant.rs:11:1 + | +LL | / enum LargeEnum { +LL | | A(i32), + | | ------ the second-largest variant contains at least 4 bytes +LL | | B([i32; 8000]), + | | -------------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | + = note: `-D clippy::large-enum-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[i32; 8000]>), + | ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:35:1 + | +LL | / enum LargeEnum2 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | ContainingLargeEnum(LargeEnum), + | | ------------------------------ the largest variant contains at least 32004 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingLargeEnum(Box), + | ~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:40:1 + | +LL | / enum LargeEnum3 { +LL | | ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), + | | --------------------------------------------------------- the largest variant contains at least 70004 bytes +LL | | VoidVariant, +LL | | StructLikeLittle { x: i32, y: i32 }, + | | ----------------------------------- the second-largest variant contains at least 8 bytes +LL | | } + | |_^ the entire enum is at least 70008 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>), + | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:46:1 + | +LL | / enum LargeEnum4 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | StructLikeLarge { x: [i32; 8000], y: i32 }, + | | ------------------------------------------ the largest variant contains at least 32004 bytes +LL | | } + | |_^ the entire enum is at least 32008 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | StructLikeLarge { x: Box<[i32; 8000]>, y: i32 }, + | ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:51:1 + | +LL | / enum LargeEnum5 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | StructLikeLarge2 { x: [i32; 8000] }, + | | ----------------------------------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | StructLikeLarge2 { x: Box<[i32; 8000]> }, + | ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:67:1 + | +LL | / enum LargeEnum7 { +LL | | A, +LL | | B([u8; 1255]), + | | ------------- the largest variant contains at least 1255 bytes +LL | | C([u8; 200]), + | | ------------ the second-largest variant contains at least 200 bytes +LL | | } + | |_^ the entire enum is at least 1256 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[u8; 1255]>), + | ~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:73:1 + | +LL | / enum LargeEnum8 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]), + | | ------------------------------------------------------------------------- the largest variant contains at least 70128 bytes +LL | | } + | |_^ the entire enum is at least 70132 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]), + | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:78:1 + | +LL | / enum LargeEnum9 { +LL | | A(Struct<()>), + | | ------------- the second-largest variant contains at least 4 bytes +LL | | B(Struct2), + | | ---------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box), + | ~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:83:1 + | +LL | / enum LargeEnumOk2 { +LL | | A(T), + | | ---- the second-largest variant contains at least 0 bytes +LL | | B(Struct2), + | | ---------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32000 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box), + | ~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:88:1 + | +LL | / enum LargeEnumOk3 { +LL | | A(Struct), + | | ------------ the second-largest variant contains at least 4 bytes +LL | | B(Struct2), + | | ---------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32000 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box), + | ~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:103:1 + | +LL | / enum CopyableLargeEnum { +LL | | A(bool), + | | ------- the second-largest variant contains at least 1 bytes +LL | | B([u64; 8000]), + | | -------------- the largest variant contains at least 64000 bytes +LL | | } + | |_^ the entire enum is at least 64004 bytes + | +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:103:6 + | +LL | enum CopyableLargeEnum { + | ^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:105:5 + | +LL | B([u64; 8000]), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:108:1 + | +LL | / enum ManuallyCopyLargeEnum { +LL | | A(bool), + | | ------- the second-largest variant contains at least 1 bytes +LL | | B([u64; 8000]), + | | -------------- the largest variant contains at least 64000 bytes +LL | | } + | |_^ the entire enum is at least 64004 bytes + | +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:108:6 + | +LL | enum ManuallyCopyLargeEnum { + | ^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:110:5 + | +LL | B([u64; 8000]), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:121:1 + | +LL | / enum SomeGenericPossiblyCopyEnum { +LL | | A(bool, std::marker::PhantomData), + | | ------------------------------------ the second-largest variant contains at least 1 bytes +LL | | B([u64; 4000]), + | | -------------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:121:6 + | +LL | enum SomeGenericPossiblyCopyEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:123:5 + | +LL | B([u64; 4000]), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:134:1 + | +LL | / enum LargeEnumWithGenerics { +LL | | Small, + | | ----- the second-largest variant carries no data at all +LL | | Large((T, [u8; 512])), + | | --------------------- the largest variant contains at least 512 bytes +LL | | } + | |_^ the entire enum is at least 512 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | Large(Box<(T, [u8; 512])>), + | ~~~~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:143:1 + | +LL | / enum WithGenerics { +LL | | Large([Foo; 64]), + | | --------------------- the largest variant contains at least 512 bytes +LL | | Small(u8), + | | --------- the second-largest variant contains at least 1 bytes +LL | | } + | |_^ the entire enum is at least 516 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | Large(Box<[Foo; 64]>), + | ~~~~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:153:1 + | +LL | / enum LargeEnumOfConst { +LL | | Ok, + | | -- the second-largest variant carries no data at all +LL | | Error(PossiblyLargeEnumWithConst<256>), + | | -------------------------------------- the largest variant contains at least 514 bytes +LL | | } + | |_^ the entire enum is at least 514 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | Error(Box>), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/large_enum_variant.stderr b/tests/ui/large_enum_variant.64bit.stderr similarity index 91% rename from tests/ui/large_enum_variant.stderr rename to tests/ui/large_enum_variant.64bit.stderr index 7026ca785f34c..3eba43e05ece5 100644 --- a/tests/ui/large_enum_variant.stderr +++ b/tests/ui/large_enum_variant.64bit.stderr @@ -1,5 +1,5 @@ error: large size difference between variants - --> $DIR/large_enum_variant.rs:10:1 + --> $DIR/large_enum_variant.rs:11:1 | LL | / enum LargeEnum { LL | | A(i32), @@ -17,7 +17,7 @@ LL | B(Box<[i32; 8000]>), | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:34:1 + --> $DIR/large_enum_variant.rs:35:1 | LL | / enum LargeEnum2 { LL | | VariantOk(i32, u32), @@ -33,7 +33,7 @@ LL | ContainingLargeEnum(Box), | ~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:39:1 + --> $DIR/large_enum_variant.rs:40:1 | LL | / enum LargeEnum3 { LL | | ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), @@ -50,7 +50,7 @@ LL | ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>), | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:45:1 + --> $DIR/large_enum_variant.rs:46:1 | LL | / enum LargeEnum4 { LL | | VariantOk(i32, u32), @@ -66,7 +66,7 @@ LL | StructLikeLarge { x: Box<[i32; 8000]>, y: i32 }, | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:50:1 + --> $DIR/large_enum_variant.rs:51:1 | LL | / enum LargeEnum5 { LL | | VariantOk(i32, u32), @@ -82,7 +82,7 @@ LL | StructLikeLarge2 { x: Box<[i32; 8000]> }, | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:66:1 + --> $DIR/large_enum_variant.rs:67:1 | LL | / enum LargeEnum7 { LL | | A, @@ -99,7 +99,7 @@ LL | B(Box<[u8; 1255]>), | ~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:72:1 + --> $DIR/large_enum_variant.rs:73:1 | LL | / enum LargeEnum8 { LL | | VariantOk(i32, u32), @@ -115,7 +115,7 @@ LL | ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]> | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:77:1 + --> $DIR/large_enum_variant.rs:78:1 | LL | / enum LargeEnum9 { LL | | A(Struct<()>), @@ -131,7 +131,7 @@ LL | B(Box), | ~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:82:1 + --> $DIR/large_enum_variant.rs:83:1 | LL | / enum LargeEnumOk2 { LL | | A(T), @@ -147,7 +147,7 @@ LL | B(Box), | ~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:87:1 + --> $DIR/large_enum_variant.rs:88:1 | LL | / enum LargeEnumOk3 { LL | | A(Struct), @@ -163,7 +163,7 @@ LL | B(Box), | ~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:102:1 + --> $DIR/large_enum_variant.rs:103:1 | LL | / enum CopyableLargeEnum { LL | | A(bool), @@ -174,18 +174,18 @@ LL | | } | |_^ the entire enum is at least 64008 bytes | note: boxing a variant would require the type no longer be `Copy` - --> $DIR/large_enum_variant.rs:102:6 + --> $DIR/large_enum_variant.rs:103:6 | LL | enum CopyableLargeEnum { | ^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:104:5 + --> $DIR/large_enum_variant.rs:105:5 | LL | B([u64; 8000]), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:107:1 + --> $DIR/large_enum_variant.rs:108:1 | LL | / enum ManuallyCopyLargeEnum { LL | | A(bool), @@ -196,18 +196,18 @@ LL | | } | |_^ the entire enum is at least 64008 bytes | note: boxing a variant would require the type no longer be `Copy` - --> $DIR/large_enum_variant.rs:107:6 + --> $DIR/large_enum_variant.rs:108:6 | LL | enum ManuallyCopyLargeEnum { | ^^^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:109:5 + --> $DIR/large_enum_variant.rs:110:5 | LL | B([u64; 8000]), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:120:1 + --> $DIR/large_enum_variant.rs:121:1 | LL | / enum SomeGenericPossiblyCopyEnum { LL | | A(bool, std::marker::PhantomData), @@ -218,18 +218,18 @@ LL | | } | |_^ the entire enum is at least 32008 bytes | note: boxing a variant would require the type no longer be `Copy` - --> $DIR/large_enum_variant.rs:120:6 + --> $DIR/large_enum_variant.rs:121:6 | LL | enum SomeGenericPossiblyCopyEnum { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:122:5 + --> $DIR/large_enum_variant.rs:123:5 | LL | B([u64; 4000]), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:133:1 + --> $DIR/large_enum_variant.rs:134:1 | LL | / enum LargeEnumWithGenerics { LL | | Small, @@ -245,7 +245,7 @@ LL | Large(Box<(T, [u8; 512])>), | ~~~~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:142:1 + --> $DIR/large_enum_variant.rs:143:1 | LL | / enum WithGenerics { LL | | Large([Foo; 64]), @@ -261,7 +261,7 @@ LL | Large(Box<[Foo; 64]>), | ~~~~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:152:1 + --> $DIR/large_enum_variant.rs:153:1 | LL | / enum LargeEnumOfConst { LL | | Ok, diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index f101bda76a895..3625c011dbfa1 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -1,3 +1,4 @@ +//@stderr-per-bitwidth //@aux-build:proc_macros.rs //@no-rustfix #![allow(dead_code)] diff --git a/tests/ui/manual_range_patterns.fixed b/tests/ui/manual_range_patterns.fixed index ea06e27d1534e..b348d7071f6e9 100644 --- a/tests/ui/manual_range_patterns.fixed +++ b/tests/ui/manual_range_patterns.fixed @@ -13,8 +13,8 @@ fn main() { let _ = matches!(f, 1 | 2147483647); let _ = matches!(f, 0 | 2147483647); let _ = matches!(f, -2147483647 | 2147483647); - let _ = matches!(f, 1 | (2..=4)); - let _ = matches!(f, 1 | (2..4)); + let _ = matches!(f, 1..=4); + let _ = matches!(f, 1..4); let _ = matches!(f, 1..=48324729); let _ = matches!(f, 0..=48324730); let _ = matches!(f, 0..=3); @@ -25,9 +25,20 @@ fn main() { }; let _ = matches!(f, -5..=3); let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1); // 2 is missing - let _ = matches!(f, -1000001..=1000001); + let _ = matches!(f, -1_000_001..=1_000_001); let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002); + matches!(f, 0x00..=0x03); + matches!(f, 0x00..=0x07); + matches!(f, -0x09..=0x00); + + matches!(f, 0..=5); + matches!(f, 0..5); + + matches!(f, 0..10); + matches!(f, 0..=10); + matches!(f, 0..=10); + macro_rules! mac { ($e:expr) => { matches!($e, 1..=10) diff --git a/tests/ui/manual_range_patterns.rs b/tests/ui/manual_range_patterns.rs index eb29987b89fd1..a0750f54b73f1 100644 --- a/tests/ui/manual_range_patterns.rs +++ b/tests/ui/manual_range_patterns.rs @@ -28,6 +28,17 @@ fn main() { let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002); + matches!(f, 0x00 | 0x01 | 0x02 | 0x03); + matches!(f, 0x00..=0x05 | 0x06 | 0x07); + matches!(f, -0x09 | -0x08 | -0x07..=0x00); + + matches!(f, 0..5 | 5); + matches!(f, 0 | 1..5); + + matches!(f, 0..=5 | 6..10); + matches!(f, 0..5 | 5..=10); + matches!(f, 5..=10 | 0..5); + macro_rules! mac { ($e:expr) => { matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) diff --git a/tests/ui/manual_range_patterns.stderr b/tests/ui/manual_range_patterns.stderr index 3eee39af86e7b..fbeb9455769df 100644 --- a/tests/ui/manual_range_patterns.stderr +++ b/tests/ui/manual_range_patterns.stderr @@ -13,6 +13,18 @@ error: this OR pattern can be rewritten using a range LL | let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 7 | 8 | 10); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:16:25 + | +LL | let _ = matches!(f, 1 | (2..=4)); + | ^^^^^^^^^^^ help: try: `1..=4` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:17:25 + | +LL | let _ = matches!(f, 1 | (2..4)); + | ^^^^^^^^^^ help: try: `1..4` + error: this OR pattern can be rewritten using a range --> $DIR/manual_range_patterns.rs:18:25 | @@ -47,10 +59,58 @@ error: this OR pattern can be rewritten using a range --> $DIR/manual_range_patterns.rs:28:25 | LL | let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1000001..=1000001` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1_000_001..=1_000_001` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:31:17 + | +LL | matches!(f, 0x00 | 0x01 | 0x02 | 0x03); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x03` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:32:17 + | +LL | matches!(f, 0x00..=0x05 | 0x06 | 0x07); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x07` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:33:17 + | +LL | matches!(f, -0x09 | -0x08 | -0x07..=0x00); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-0x09..=0x00` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:35:17 + | +LL | matches!(f, 0..5 | 5); + | ^^^^^^^^ help: try: `0..=5` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:36:17 + | +LL | matches!(f, 0 | 1..5); + | ^^^^^^^^ help: try: `0..5` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:38:17 + | +LL | matches!(f, 0..=5 | 6..10); + | ^^^^^^^^^^^^^ help: try: `0..10` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:39:17 + | +LL | matches!(f, 0..5 | 5..=10); + | ^^^^^^^^^^^^^ help: try: `0..=10` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:40:17 + | +LL | matches!(f, 5..=10 | 0..5); + | ^^^^^^^^^^^^^ help: try: `0..=10` error: this OR pattern can be rewritten using a range - --> $DIR/manual_range_patterns.rs:33:26 + --> $DIR/manual_range_patterns.rs:44:26 | LL | matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` @@ -60,5 +120,5 @@ LL | mac!(f); | = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 9 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/missing_asserts_for_indexing.fixed b/tests/ui/missing_asserts_for_indexing.fixed new file mode 100644 index 0000000000000..a96827259f530 --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing.fixed @@ -0,0 +1,121 @@ +#![allow(unused)] +#![warn(clippy::missing_asserts_for_indexing)] + +// ok +fn sum_with_assert(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_other_way(v: &[u8]) -> u8 { + assert!(5 <= v.len()); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_ge(v: &[u8]) -> u8 { + assert!(v.len() >= 5); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 { + assert!(4 < v.len()); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +fn sum_with_assert_lt(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_assert_le(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +// ok, don't lint for single array access +fn single_access(v: &[u8]) -> u8 { + v[0] +} + +// ok +fn subslice_ok(v: &[u8]) { + assert!(v.len() > 3); + let _ = v[0]; + let _ = v[1..4]; +} + +fn subslice_bad(v: &[u8]) { + assert!(v.len() > 3); + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v[1..4]; +} + +// ok +fn subslice_inclusive_ok(v: &[u8]) { + assert!(v.len() > 4); + let _ = v[0]; + let _ = v[1..=4]; +} + +fn subslice_inclusive_bad(v: &[u8]) { + assert!(v.len() > 4); + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v[1..=4]; +} + +fn index_different_slices_ok(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + let _ = v2[5] + v2[15]; +} + +fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[5] + v2[15]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} +fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[5] + v2[15]; +} + +fn side_effect() -> &'static [u8] { + &[] +} + +fn index_side_effect_expr() { + let _ = side_effect()[0] + side_effect()[1]; +} + +// ok, single access for different slices +fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) { + let _ = v1[0] + v2[1]; +} + +fn main() {} diff --git a/tests/ui/missing_asserts_for_indexing.rs b/tests/ui/missing_asserts_for_indexing.rs new file mode 100644 index 0000000000000..0b4b883acf8ac --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing.rs @@ -0,0 +1,121 @@ +#![allow(unused)] +#![warn(clippy::missing_asserts_for_indexing)] + +// ok +fn sum_with_assert(v: &[u8]) -> u8 { + assert!(v.len() > 4); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_other_way(v: &[u8]) -> u8 { + assert!(5 <= v.len()); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_ge(v: &[u8]) -> u8 { + assert!(v.len() >= 5); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +// ok +fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 { + assert!(4 < v.len()); + v[0] + v[1] + v[2] + v[3] + v[4] +} + +fn sum_with_assert_lt(v: &[u8]) -> u8 { + assert!(v.len() < 5); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_assert_le(v: &[u8]) -> u8 { + assert!(v.len() <= 5); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 { + assert!(v.len() > 3); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 { + assert!(v.len() >= 4); + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} + +// ok, don't lint for single array access +fn single_access(v: &[u8]) -> u8 { + v[0] +} + +// ok +fn subslice_ok(v: &[u8]) { + assert!(v.len() > 3); + let _ = v[0]; + let _ = v[1..4]; +} + +fn subslice_bad(v: &[u8]) { + assert!(v.len() >= 3); + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v[1..4]; +} + +// ok +fn subslice_inclusive_ok(v: &[u8]) { + assert!(v.len() > 4); + let _ = v[0]; + let _ = v[1..=4]; +} + +fn subslice_inclusive_bad(v: &[u8]) { + assert!(v.len() >= 4); + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v[1..=4]; +} + +fn index_different_slices_ok(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + let _ = v2[5] + v2[15]; +} + +fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) { + assert!(v1.len() >= 12); + assert!(v2.len() >= 15); + let _ = v1[0] + v1[12]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[5] + v2[15]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the +} +fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) { + assert!(v1.len() >= 12); + assert!(v2.len() > 15); + let _ = v1[0] + v1[12]; + //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the + let _ = v2[5] + v2[15]; +} + +fn side_effect() -> &'static [u8] { + &[] +} + +fn index_side_effect_expr() { + let _ = side_effect()[0] + side_effect()[1]; +} + +// ok, single access for different slices +fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) { + let _ = v1[0] + v2[1]; +} + +fn main() {} diff --git a/tests/ui/missing_asserts_for_indexing.stderr b/tests/ui/missing_asserts_for_indexing.stderr new file mode 100644 index 0000000000000..a3e66d7958e98 --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing.stderr @@ -0,0 +1,253 @@ +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:30:5 + | +LL | assert!(v.len() < 5); + | -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:30:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:36:5 + | +LL | assert!(v.len() <= 5); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:36:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:42:5 + | +LL | assert!(v.len() > 3); + | -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:42:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:48:5 + | +LL | assert!(v.len() >= 4); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:48:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:66:13 + | +LL | assert!(v.len() >= 3); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 3)` +LL | let _ = v[0]; + | _____________^ +LL | | +LL | | let _ = v[1..4]; + | |___________________^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:66:13 + | +LL | let _ = v[0]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:68:13 + | +LL | let _ = v[1..4]; + | ^^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:80:13 + | +LL | assert!(v.len() >= 4); + | --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)` +LL | let _ = v[0]; + | _____________^ +LL | | +LL | | let _ = v[1..=4]; + | |____________________^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:80:13 + | +LL | let _ = v[0]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:82:13 + | +LL | let _ = v[1..=4]; + | ^^^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:95:13 + | +LL | assert!(v1.len() >= 12); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)` +LL | assert!(v2.len() >= 15); +LL | let _ = v1[0] + v1[12]; + | ^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:95:13 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:95:21 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:97:13 + | +LL | assert!(v2.len() >= 15); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v2.len() > 15)` +... +LL | let _ = v2[5] + v2[15]; + | ^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:97:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:97:21 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> $DIR/missing_asserts_for_indexing.rs:103:13 + | +LL | assert!(v1.len() >= 12); + | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)` +LL | assert!(v2.len() > 15); +LL | let _ = v1[0] + v1[12]; + | ^^^^^^^^^^^^^^ + | +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:103:13 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing.rs:103:21 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 9 previous errors + diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.rs b/tests/ui/missing_asserts_for_indexing_unfixable.rs new file mode 100644 index 0000000000000..4346ed892f277 --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing_unfixable.rs @@ -0,0 +1,49 @@ +#![allow(unused)] +#![warn(clippy::missing_asserts_for_indexing)] + +fn sum(v: &[u8]) -> u8 { + v[0] + v[1] + v[2] + v[3] + v[4] + //~^ ERROR: indexing into a slice multiple times without an `assert` +} + +fn subslice(v: &[u8]) { + let _ = v[0]; + //~^ ERROR: indexing into a slice multiple times without an `assert` + let _ = v[1..4]; +} + +fn variables(v: &[u8]) -> u8 { + let a = v[0]; + //~^ ERROR: indexing into a slice multiple times without an `assert` + let b = v[1]; + let c = v[2]; + a + b + c +} + +fn index_different_slices(v1: &[u8], v2: &[u8]) { + let _ = v1[0] + v1[12]; + let _ = v2[5] + v2[15]; +} + +fn index_different_slices2(v1: &[u8], v2: &[u8]) { + assert!(v1.len() > 12); + let _ = v1[0] + v1[12]; + let _ = v2[5] + v2[15]; +} + +struct Foo<'a> { + v: &'a [u8], + v2: &'a [u8], +} + +fn index_struct_field(f: &Foo<'_>) { + let _ = f.v[0] + f.v[1]; + //~^ ERROR: indexing into a slice multiple times without an `assert` +} + +fn index_struct_different_fields(f: &Foo<'_>) { + // ok, different fields + let _ = f.v[0] + f.v2[1]; +} + +fn main() {} diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/tests/ui/missing_asserts_for_indexing_unfixable.stderr new file mode 100644 index 0000000000000..12c9eed5d6619 --- /dev/null +++ b/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -0,0 +1,164 @@ +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v.len() > 4);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:12 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:19 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:26 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:33 + | +LL | v[0] + v[1] + v[2] + v[3] + v[4] + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13 + | +LL | let _ = v[0]; + | _____________^ +LL | | +LL | | let _ = v[1..4]; + | |___________________^ + | + = help: consider asserting the length before indexing: `assert!(v.len() > 3);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13 + | +LL | let _ = v[0]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:12:13 + | +LL | let _ = v[1..4]; + | ^^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13 + | +LL | let a = v[0]; + | _____________^ +LL | | +LL | | let b = v[1]; +LL | | let c = v[2]; + | |________________^ + | + = help: consider asserting the length before indexing: `assert!(v.len() > 2);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13 + | +LL | let a = v[0]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:18:13 + | +LL | let b = v[1]; + | ^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:19:13 + | +LL | let c = v[2]; + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v1.len() > 12);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:21 + | +LL | let _ = v1[0] + v1[12]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:21 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:21 + | +LL | let _ = v2[5] + v2[15]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times without an `assert` + --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13 + | +LL | let _ = f.v[0] + f.v[1]; + | ^^^^^^^^^^^^^^^ + | + = help: consider asserting the length before indexing: `assert!(f.v.len() > 1);` +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13 + | +LL | let _ = f.v[0] + f.v[1]; + | ^^^^^^ +note: slice indexed here + --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:22 + | +LL | let _ = f.v[0] + f.v[1]; + | ^^^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 7 previous errors + diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index ee67224b4a800..0a52b25229d76 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -503,3 +503,16 @@ mod issue_10535 { { } } + +mod issue_10253 { + struct S; + trait X { + fn f(&self); + } + impl X for &S { + fn f(&self) {} + } + fn f() { + (&S).f::<()>(); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 1444f47d92099..34a95d184635d 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -503,3 +503,16 @@ mod issue_10535 { { } } + +mod issue_10253 { + struct S; + trait X { + fn f(&self); + } + impl X for &S { + fn f(&self) {} + } + fn f() { + (&S).f::<()>(); + } +} diff --git a/tests/ui/needless_raw_string.fixed b/tests/ui/needless_raw_string.fixed index 4db375178b4f2..855498105135e 100644 --- a/tests/ui/needless_raw_string.fixed +++ b/tests/ui/needless_raw_string.fixed @@ -9,8 +9,13 @@ fn main() { b"aaa"; br#""aaa""#; br#"\s"#; - // currently disabled: https://github.com/rust-lang/rust/issues/113333 - // cr#"aaa"#; - // cr#""aaa""#; - // cr#"\s"#; + c"aaa"; + cr#""aaa""#; + cr#"\s"#; + + " + a + multiline + string + "; } diff --git a/tests/ui/needless_raw_string.rs b/tests/ui/needless_raw_string.rs index 59c75fda41bef..06d4973038717 100644 --- a/tests/ui/needless_raw_string.rs +++ b/tests/ui/needless_raw_string.rs @@ -9,8 +9,13 @@ fn main() { br#"aaa"#; br#""aaa""#; br#"\s"#; - // currently disabled: https://github.com/rust-lang/rust/issues/113333 - // cr#"aaa"#; - // cr#""aaa""#; - // cr#"\s"#; + cr#"aaa"#; + cr#""aaa""#; + cr#"\s"#; + + r#" + a + multiline + string + "#; } diff --git a/tests/ui/needless_raw_string.stderr b/tests/ui/needless_raw_string.stderr index 83cc6d332ee56..e6806b31b1d96 100644 --- a/tests/ui/needless_raw_string.stderr +++ b/tests/ui/needless_raw_string.stderr @@ -2,16 +2,58 @@ error: unnecessary raw string literal --> $DIR/needless_raw_string.rs:6:5 | LL | r#"aaa"#; - | ^^^^^^^^ help: try: `"aaa"` + | ^^^^^^^^ | = note: `-D clippy::needless-raw-strings` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]` +help: try + | +LL - r#"aaa"#; +LL + "aaa"; + | error: unnecessary raw string literal --> $DIR/needless_raw_string.rs:9:5 | LL | br#"aaa"#; - | ^^^^^^^^^ help: try: `b"aaa"` + | ^^^^^^^^^ + | +help: try + | +LL - br#"aaa"#; +LL + b"aaa"; + | + +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:12:5 + | +LL | cr#"aaa"#; + | ^^^^^^^^^ + | +help: try + | +LL - cr#"aaa"#; +LL + c"aaa"; + | + +error: unnecessary raw string literal + --> $DIR/needless_raw_string.rs:16:5 + | +LL | / r#" +LL | | a +LL | | multiline +LL | | string +LL | | "#; + | |______^ + | +help: try + | +LL ~ " +LL | a +LL | multiline +LL | string +LL ~ "; + | -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/needless_raw_string_hashes.fixed b/tests/ui/needless_raw_string_hashes.fixed index 84902157ab4fc..e980adeeff4c1 100644 --- a/tests/ui/needless_raw_string_hashes.fixed +++ b/tests/ui/needless_raw_string_hashes.fixed @@ -3,17 +3,22 @@ #![feature(c_str_literals)] fn main() { - r#"aaa"#; + r"\aaa"; r#"Hello "world"!"#; r####" "### "## "# "####; r###" "aa" "# "## "###; - br#"aaa"#; + br"\aaa"; br#"Hello "world"!"#; br####" "### "## "# "####; br###" "aa" "# "## "###; - // currently disabled: https://github.com/rust-lang/rust/issues/113333 - // cr#"aaa"#; - // cr##"Hello "world"!"##; - // cr######" "### "## "# "######; - // cr######" "aa" "# "## "######; + cr"\aaa"; + cr#"Hello "world"!"#; + cr####" "### "## "# "####; + cr###" "aa" "# "## "###; + + r" + \a + multiline + string + "; } diff --git a/tests/ui/needless_raw_string_hashes.rs b/tests/ui/needless_raw_string_hashes.rs index 62abae8385926..6113c5f25ae64 100644 --- a/tests/ui/needless_raw_string_hashes.rs +++ b/tests/ui/needless_raw_string_hashes.rs @@ -3,17 +3,22 @@ #![feature(c_str_literals)] fn main() { - r#"aaa"#; + r#"\aaa"#; r##"Hello "world"!"##; r######" "### "## "# "######; r######" "aa" "# "## "######; - br#"aaa"#; + br#"\aaa"#; br##"Hello "world"!"##; br######" "### "## "# "######; br######" "aa" "# "## "######; - // currently disabled: https://github.com/rust-lang/rust/issues/113333 - // cr#"aaa"#; - // cr##"Hello "world"!"##; - // cr######" "### "## "# "######; - // cr######" "aa" "# "## "######; + cr#"\aaa"#; + cr##"Hello "world"!"##; + cr######" "### "## "# "######; + cr######" "aa" "# "## "######; + + r#" + \a + multiline + string + "#; } diff --git a/tests/ui/needless_raw_string_hashes.stderr b/tests/ui/needless_raw_string_hashes.stderr index 94c51c423b86d..5a8e3d04543af 100644 --- a/tests/ui/needless_raw_string_hashes.stderr +++ b/tests/ui/needless_raw_string_hashes.stderr @@ -1,41 +1,167 @@ error: unnecessary hashes around raw string literal - --> $DIR/needless_raw_string_hashes.rs:7:5 + --> $DIR/needless_raw_string_hashes.rs:6:5 | -LL | r##"Hello "world"!"##; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `r#"Hello "world"!"#` +LL | r#"\aaa"#; + | ^^^^^^^^^ | = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_string_hashes)]` +help: remove all the hashes around the literal + | +LL - r#"\aaa"#; +LL + r"\aaa"; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:7:5 + | +LL | r##"Hello "world"!"##; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: remove one hash from both sides of the literal + | +LL - r##"Hello "world"!"##; +LL + r#"Hello "world"!"#; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:8:5 | LL | r######" "### "## "# "######; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r####" "### "## "# "####` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 2 hashes from both sides of the literal + | +LL - r######" "### "## "# "######; +LL + r####" "### "## "# "####; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:9:5 | LL | r######" "aa" "# "## "######; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r###" "aa" "# "## "###` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 3 hashes from both sides of the literal + | +LL - r######" "aa" "# "## "######; +LL + r###" "aa" "# "## "###; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:10:5 + | +LL | br#"\aaa"#; + | ^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - br#"\aaa"#; +LL + br"\aaa"; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:11:5 | LL | br##"Hello "world"!"##; - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `br#"Hello "world"!"#` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove one hash from both sides of the literal + | +LL - br##"Hello "world"!"##; +LL + br#"Hello "world"!"#; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:12:5 | LL | br######" "### "## "# "######; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br####" "### "## "# "####` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 2 hashes from both sides of the literal + | +LL - br######" "### "## "# "######; +LL + br####" "### "## "# "####; + | error: unnecessary hashes around raw string literal --> $DIR/needless_raw_string_hashes.rs:13:5 | LL | br######" "aa" "# "## "######; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br###" "aa" "# "## "###` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 3 hashes from both sides of the literal + | +LL - br######" "aa" "# "## "######; +LL + br###" "aa" "# "## "###; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:14:5 + | +LL | cr#"\aaa"#; + | ^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - cr#"\aaa"#; +LL + cr"\aaa"; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:15:5 + | +LL | cr##"Hello "world"!"##; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove one hash from both sides of the literal + | +LL - cr##"Hello "world"!"##; +LL + cr#"Hello "world"!"#; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:16:5 + | +LL | cr######" "### "## "# "######; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 2 hashes from both sides of the literal + | +LL - cr######" "### "## "# "######; +LL + cr####" "### "## "# "####; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:17:5 + | +LL | cr######" "aa" "# "## "######; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 3 hashes from both sides of the literal + | +LL - cr######" "aa" "# "## "######; +LL + cr###" "aa" "# "## "###; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:19:5 + | +LL | / r#" +LL | | \a +LL | | multiline +LL | | string +LL | | "#; + | |______^ + | +help: remove all the hashes around the literal + | +LL ~ r" +LL | \a +LL | multiline +LL | string +LL ~ "; + | -error: aborting due to 6 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 001b94391ca81..c67a6d4494e05 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -337,10 +337,8 @@ pub fn test26() { pub fn test27() { loop { - //~^ ERROR: this loop never actually loops 'label: { let x = true; - // Lints because we cannot prove it's always `true` if x { break 'label; } @@ -349,6 +347,59 @@ pub fn test27() { } } +// issue 11004 +pub fn test29() { + loop { + 'label: { + if true { + break 'label; + } + return; + } + } +} + +pub fn test30() { + 'a: loop { + 'b: { + for j in 0..2 { + if j == 1 { + break 'b; + } + } + break 'a; + } + } +} + +pub fn test31(b: bool) { + 'a: loop { + 'b: { + 'c: loop { + //~^ ERROR: this loop never actually loops + if b { break 'c } else { break 'b } + } + continue 'a; + } + break 'a; + } +} + +pub fn test32() { + loop { + //~^ ERROR: this loop never actually loops + panic!("oh no"); + } + loop { + //~^ ERROR: this loop never actually loops + unimplemented!("not yet"); + } + loop { + // no error + todo!("maybe later"); + } +} + fn main() { test1(); test2(); diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 234780007b1af..3982f36cea9f8 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -154,16 +154,31 @@ LL | if let Some(_) = (0..20).next() { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: this loop never actually loops - --> $DIR/never_loop.rs:339:5 + --> $DIR/never_loop.rs:378:13 + | +LL | / 'c: loop { +LL | | +LL | | if b { break 'c } else { break 'b } +LL | | } + | |_____________^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:389:5 | LL | / loop { LL | | -LL | | 'label: { -LL | | let x = true; -... | -LL | | } +LL | | panic!("oh no"); +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:393:5 + | +LL | / loop { +LL | | +LL | | unimplemented!("not yet"); LL | | } | |_____^ -error: aborting due to 14 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/incorrect_clone_impl_on_copy_type.fixed b/tests/ui/non_canonical_clone_impl.fixed similarity index 100% rename from tests/ui/incorrect_clone_impl_on_copy_type.fixed rename to tests/ui/non_canonical_clone_impl.fixed diff --git a/tests/ui/incorrect_clone_impl_on_copy_type.rs b/tests/ui/non_canonical_clone_impl.rs similarity index 100% rename from tests/ui/incorrect_clone_impl_on_copy_type.rs rename to tests/ui/non_canonical_clone_impl.rs diff --git a/tests/ui/incorrect_clone_impl_on_copy_type.stderr b/tests/ui/non_canonical_clone_impl.stderr similarity index 54% rename from tests/ui/incorrect_clone_impl_on_copy_type.stderr rename to tests/ui/non_canonical_clone_impl.stderr index 566a1a4b14b58..44196751b053b 100644 --- a/tests/ui/incorrect_clone_impl_on_copy_type.stderr +++ b/tests/ui/non_canonical_clone_impl.stderr @@ -1,5 +1,5 @@ -error: incorrect implementation of `clone` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:9:29 +error: non-canonical implementation of `clone` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:9:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -7,10 +7,11 @@ LL | | Self(self.0) LL | | } | |_____^ help: change this to: `{ *self }` | - = note: `#[deny(clippy::incorrect_clone_impl_on_copy_type)]` on by default + = note: `-D clippy::non-canonical-clone-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_canonical_clone_impl)]` -error: incorrect implementation of `clone_from` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:13:5 +error: unnecessary implementation of `clone_from` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:13:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); @@ -18,8 +19,8 @@ LL | | *self = source.clone(); LL | | } | |_____^ help: remove it -error: incorrect implementation of `clone` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:80:29 +error: non-canonical implementation of `clone` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:80:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -27,8 +28,8 @@ LL | | Self(self.0) LL | | } | |_____^ help: change this to: `{ *self }` -error: incorrect implementation of `clone_from` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:84:5 +error: unnecessary implementation of `clone_from` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:84:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed b/tests/ui/non_canonical_partial_ord_impl.fixed similarity index 100% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed rename to tests/ui/non_canonical_partial_ord_impl.fixed diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs b/tests/ui/non_canonical_partial_ord_impl.rs similarity index 100% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type.rs rename to tests/ui/non_canonical_partial_ord_impl.rs diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr b/tests/ui/non_canonical_partial_ord_impl.stderr similarity index 63% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr rename to tests/ui/non_canonical_partial_ord_impl.stderr index 1f706984662b6..05cc717b9ba1d 100644 --- a/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr +++ b/tests/ui/non_canonical_partial_ord_impl.stderr @@ -1,5 +1,5 @@ -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:16:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl.rs:16:1 | LL | / impl PartialOrd for A { LL | | fn partial_cmp(&self, other: &Self) -> Option { @@ -10,10 +10,11 @@ LL | || } LL | | } | |__^ | - = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default + = note: `-D clippy::non-canonical-partial-ord-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:50:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl.rs:50:1 | LL | / impl PartialOrd for C { LL | | fn partial_cmp(&self, _: &Self) -> Option { diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs b/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs similarity index 82% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs rename to tests/ui/non_canonical_partial_ord_impl_fully_qual.rs index 1173a95d06599..2f8d5cf30c77a 100644 --- a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs +++ b/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs @@ -21,8 +21,6 @@ impl cmp::Ord for A { } impl PartialOrd for A { - //~^ ERROR: incorrect implementation of `partial_cmp` on an `Ord` type - //~| NOTE: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default fn partial_cmp(&self, other: &Self) -> Option { // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't // automatically applied @@ -46,7 +44,6 @@ impl cmp::Ord for B { } impl PartialOrd for B { - //~^ ERROR: incorrect implementation of `partial_cmp` on an `Ord` type fn partial_cmp(&self, other: &Self) -> Option { // This calls `B.cmp`, not `Ord::cmp`! Some(self.cmp(other)) diff --git a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr b/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr similarity index 67% rename from tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr rename to tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr index 09d7a32e3342a..4978d7a873960 100644 --- a/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr +++ b/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr @@ -1,9 +1,7 @@ -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:23:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl_fully_qual.rs:23:1 | LL | / impl PartialOrd for A { -LL | | -LL | | LL | | fn partial_cmp(&self, other: &Self) -> Option { | | _____________________________________________________________- LL | || // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't @@ -14,13 +12,13 @@ LL | || } LL | | } | |__^ | - = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default + = note: `-D clippy::non-canonical-partial-ord-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:48:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl_fully_qual.rs:46:1 | LL | / impl PartialOrd for B { -LL | | LL | | fn partial_cmp(&self, other: &Self) -> Option { | | _____________________________________________________________- LL | || // This calls `B.cmp`, not `Ord::cmp`! diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 0fd68eb92b1f2..4df9be2c21d9a 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -14,6 +14,8 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::non_canonical_clone_impl)] +#![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] @@ -33,10 +35,10 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forgetting_references)] -#![allow(useless_ptr_null_checks)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -62,6 +64,8 @@ #![warn(clippy::mixed_read_write_in_expression)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] +#![warn(clippy::non_canonical_clone_impl)] +#![warn(clippy::non_canonical_partial_ord_impl)] #![warn(clippy::arithmetic_side_effects)] #![warn(clippy::overly_complex_bool_expr)] #![warn(clippy::new_without_default)] @@ -85,12 +89,12 @@ #![warn(drop_bounds)] #![warn(dropping_copy_types)] #![warn(dropping_references)] +#![warn(useless_ptr_null_checks)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(forgetting_copy_types)] #![warn(forgetting_references)] -#![warn(useless_ptr_null_checks)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 927937fba83a9..940e60068e7b9 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -14,6 +14,8 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::non_canonical_clone_impl)] +#![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] @@ -33,10 +35,10 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forgetting_references)] -#![allow(useless_ptr_null_checks)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -62,6 +64,8 @@ #![warn(clippy::eval_order_dependence)] #![warn(clippy::identity_conversion)] #![warn(clippy::if_let_some_result)] +#![warn(clippy::incorrect_clone_impl_on_copy_type)] +#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] #![warn(clippy::integer_arithmetic)] #![warn(clippy::logic_bug)] #![warn(clippy::new_without_default_derive)] @@ -85,12 +89,12 @@ #![warn(clippy::drop_bounds)] #![warn(clippy::drop_copy)] #![warn(clippy::drop_ref)] +#![warn(clippy::fn_null_check)] #![warn(clippy::for_loop_over_option)] #![warn(clippy::for_loop_over_result)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::forget_copy)] #![warn(clippy::forget_ref)] -#![warn(clippy::fn_null_check)] #![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] #![warn(clippy::invalid_ref)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index d98fc7b30db57..30824e154b8bf 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,322 +8,334 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` + --> $DIR/rename.rs:67:9 + | +LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` + +error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` + --> $DIR/rename.rs:68:9 + | +LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` + error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` + --> $DIR/rename.rs:92:9 + | +LL | #![warn(clippy::fn_null_check)] + | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` + error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` -error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> $DIR/rename.rs:93:9 - | -LL | #![warn(clippy::fn_null_check)] - | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` - error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:98:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:95:9 + --> $DIR/rename.rs:99:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:96:9 + --> $DIR/rename.rs:100:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> $DIR/rename.rs:97:9 + --> $DIR/rename.rs:101:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:98:9 + --> $DIR/rename.rs:102:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:99:9 + --> $DIR/rename.rs:103:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:100:9 + --> $DIR/rename.rs:104:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:101:9 + --> $DIR/rename.rs:105:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:102:9 + --> $DIR/rename.rs:106:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> $DIR/rename.rs:103:9 + --> $DIR/rename.rs:107:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:104:9 + --> $DIR/rename.rs:108:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:105:9 + --> $DIR/rename.rs:109:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 54 previous errors +error: aborting due to 56 previous errors diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index 14a1f7e1db57f..b25348bf99617 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -1,3 +1,5 @@ +//@ignore-32bit + #![warn(clippy::result_large_err)] #![allow(clippy::large_enum_variant)] diff --git a/tests/ui/result_large_err.stderr b/tests/ui/result_large_err.stderr index d42dd6600a324..6602f396a9c0d 100644 --- a/tests/ui/result_large_err.stderr +++ b/tests/ui/result_large_err.stderr @@ -1,5 +1,5 @@ error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:8:23 + --> $DIR/result_large_err.rs:10:23 | LL | pub fn large_err() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -9,7 +9,7 @@ LL | pub fn large_err() -> Result<(), [u8; 512]> { = help: to override `-D warnings` add `#[allow(clippy::result_large_err)]` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:20:21 + --> $DIR/result_large_err.rs:22:21 | LL | pub fn ret() -> Result<(), Self> { | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes @@ -17,7 +17,7 @@ LL | pub fn ret() -> Result<(), Self> { = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:26:26 + --> $DIR/result_large_err.rs:28:26 | LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes @@ -25,7 +25,7 @@ LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> { = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:32:45 + --> $DIR/result_large_err.rs:34:45 | LL | pub fn large_err_via_type_alias(x: T) -> Fdlr { | ^^^^^^^ the `Err`-variant is at least 240 bytes @@ -33,7 +33,7 @@ LL | pub fn large_err_via_type_alias(x: T) -> Fdlr { = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:41:34 + --> $DIR/result_large_err.rs:43:34 | LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeError)> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes @@ -41,7 +41,7 @@ LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeErro = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:53:34 + --> $DIR/result_large_err.rs:55:34 | LL | _Omg([u8; 512]), | --------------- the largest variant contains at least 512 bytes @@ -52,7 +52,7 @@ LL | pub fn large_enum_error() -> Result<(), Self> { = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:66:30 + --> $DIR/result_large_err.rs:68:30 | LL | _Biggest([u8; 1024]), | -------------------- the largest variant contains at least 1024 bytes @@ -65,7 +65,7 @@ LL | fn large_enum_error() -> Result<(), Self> { = help: try reducing the size of `MultipleLargeVariants`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:73:25 + --> $DIR/result_large_err.rs:75:25 | LL | fn large_error() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -73,7 +73,7 @@ LL | fn large_error() -> Result<(), [u8; 512]> { = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:93:29 + --> $DIR/result_large_err.rs:95:29 | LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -81,7 +81,7 @@ LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:103:40 + --> $DIR/result_large_err.rs:105:40 | LL | pub fn param_large_union() -> Result<(), UnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -89,7 +89,7 @@ LL | pub fn param_large_union() -> Result<(), UnionError> { = help: try reducing the size of `UnionError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:113:34 + --> $DIR/result_large_err.rs:115:34 | LL | pub fn array_error_subst() -> Result<(), ArrayError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes @@ -97,7 +97,7 @@ LL | pub fn array_error_subst() -> Result<(), ArrayError> { = help: try reducing the size of `ArrayError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:118:31 + --> $DIR/result_large_err.rs:120:31 | LL | pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index c5a941316da30..f46af56c6e2b0 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -3,6 +3,7 @@ unused, clippy::println_empty_string, clippy::empty_loop, + clippy::never_loop, clippy::diverging_sub_expression, clippy::let_unit_value )] diff --git a/tests/ui/similar_names.stderr b/tests/ui/similar_names.stderr index 011dbe679c082..44ae3532a4862 100644 --- a/tests/ui/similar_names.stderr +++ b/tests/ui/similar_names.stderr @@ -1,11 +1,11 @@ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:21:9 + --> $DIR/similar_names.rs:22:9 | LL | let bpple: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:19:9 + --> $DIR/similar_names.rs:20:9 | LL | let apple: i32; | ^^^^^ @@ -13,73 +13,73 @@ LL | let apple: i32; = help: to override `-D warnings` add `#[allow(clippy::similar_names)]` error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:24:9 + --> $DIR/similar_names.rs:25:9 | LL | let cpple: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:19:9 + --> $DIR/similar_names.rs:20:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:49:9 + --> $DIR/similar_names.rs:50:9 | LL | let bluby: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:48:9 + --> $DIR/similar_names.rs:49:9 | LL | let blubx: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:54:9 + --> $DIR/similar_names.rs:55:9 | LL | let coke: i32; | ^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:52:9 + --> $DIR/similar_names.rs:53:9 | LL | let cake: i32; | ^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:73:9 + --> $DIR/similar_names.rs:74:9 | LL | let xyzeabc: i32; | ^^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:71:9 + --> $DIR/similar_names.rs:72:9 | LL | let xyz1abc: i32; | ^^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:78:9 + --> $DIR/similar_names.rs:79:9 | LL | let parsee: i32; | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:76:9 + --> $DIR/similar_names.rs:77:9 | LL | let parser: i32; | ^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:100:16 + --> $DIR/similar_names.rs:101:16 | LL | bpple: sprang, | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:99:16 + --> $DIR/similar_names.rs:100:16 | LL | apple: spring, | ^^^^^^ diff --git a/tests/ui/single_call_fn.rs b/tests/ui/single_call_fn.rs index d6493f234133d..3cc8061647dff 100644 --- a/tests/ui/single_call_fn.rs +++ b/tests/ui/single_call_fn.rs @@ -1,3 +1,4 @@ +//@ignore-32bit //@aux-build:proc_macros.rs #![allow(clippy::redundant_closure_call, unused)] #![warn(clippy::single_call_fn)] diff --git a/tests/ui/single_call_fn.stderr b/tests/ui/single_call_fn.stderr index 4acb383c40846..d5cd707754c2f 100644 --- a/tests/ui/single_call_fn.stderr +++ b/tests/ui/single_call_fn.stderr @@ -1,5 +1,5 @@ error: this function is only used once - --> $DIR/single_call_fn.rs:33:1 + --> $DIR/single_call_fn.rs:34:1 | LL | / fn c() { LL | | println!("really"); @@ -9,7 +9,7 @@ LL | | } | |_^ | help: used here - --> $DIR/single_call_fn.rs:40:5 + --> $DIR/single_call_fn.rs:41:5 | LL | c(); | ^ @@ -17,37 +17,37 @@ LL | c(); = help: to override `-D warnings` add `#[allow(clippy::single_call_fn)]` error: this function is only used once - --> $DIR/single_call_fn.rs:12:1 + --> $DIR/single_call_fn.rs:13:1 | LL | fn i() {} | ^^^^^^^^^ | help: used here - --> $DIR/single_call_fn.rs:17:13 + --> $DIR/single_call_fn.rs:18:13 | LL | let a = i; | ^ error: this function is only used once - --> $DIR/single_call_fn.rs:43:1 + --> $DIR/single_call_fn.rs:44:1 | LL | fn a() {} | ^^^^^^^^^ | help: used here - --> $DIR/single_call_fn.rs:46:5 + --> $DIR/single_call_fn.rs:47:5 | LL | a(); | ^ error: this function is only used once - --> $DIR/single_call_fn.rs:13:1 + --> $DIR/single_call_fn.rs:14:1 | LL | fn j() {} | ^^^^^^^^^ | help: used here - --> $DIR/single_call_fn.rs:24:9 + --> $DIR/single_call_fn.rs:25:9 | LL | j(); | ^ diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index f8d85ed38cd5a..5c3086c9d69ea 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -1,5 +1,5 @@ -use std::iter::repeat; //@no-rustfix +use std::iter::repeat; fn main() { resize_vector(); extend_vector(); @@ -86,6 +86,20 @@ fn from_empty_vec() { vec1 = Vec::new(); vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + + vec1 = vec![]; + vec1.resize(10, 0); + //~^ ERROR: slow zero-filling initialization + + macro_rules! x { + () => { + vec![] + }; + } + + // `vec![]` comes from another macro, don't warn + vec1 = x!(); + vec1.resize(10, 0); } fn do_stuff(vec: &mut [u8]) {} diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr index f4501551656a2..4d24400ecb596 100644 --- a/tests/ui/slow_vector_initialization.stderr +++ b/tests/ui/slow_vector_initialization.stderr @@ -97,8 +97,16 @@ LL | vec1 = Vec::new(); LL | vec1.resize(10, 0); | ^^^^^^^^^^^^^^^^^^ +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:91:5 + | +LL | vec1 = vec![]; + | ------ help: consider replacing this with: `vec![0; 10]` +LL | vec1.resize(10, 0); + | ^^^^^^^^^^^^^^^^^^ + error: this argument is a mutable reference, but not used mutably - --> $DIR/slow_vector_initialization.rs:91:18 + --> $DIR/slow_vector_initialization.rs:105:18 | LL | fn do_stuff(vec: &mut [u8]) {} | ^^^^^^^^^ help: consider changing to: `&[u8]` @@ -106,5 +114,5 @@ LL | fn do_stuff(vec: &mut [u8]) {} = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed new file mode 100644 index 0000000000000..8027c053fb5b0 --- /dev/null +++ b/tests/ui/std_instead_of_core.fixed @@ -0,0 +1,62 @@ +#![warn(clippy::std_instead_of_core)] +#![allow(unused_imports)] + +extern crate alloc; + +#[warn(clippy::std_instead_of_core)] +fn std_instead_of_core() { + // Regular import + use core::hash::Hasher; + //~^ ERROR: used import from `std` instead of `core` + // Absolute path + use ::core::hash::Hash; + //~^ ERROR: used import from `std` instead of `core` + // Don't lint on `env` macro + use std::env; + + // Multiple imports + use core::fmt::{Debug, Result}; + //~^ ERROR: used import from `std` instead of `core` + + // Function calls + let ptr = core::ptr::null::(); + //~^ ERROR: used import from `std` instead of `core` + let ptr_mut = ::core::ptr::null_mut::(); + //~^ ERROR: used import from `std` instead of `core` + + // Types + let cell = core::cell::Cell::new(8u32); + //~^ ERROR: used import from `std` instead of `core` + let cell_absolute = ::core::cell::Cell::new(8u32); + //~^ ERROR: used import from `std` instead of `core` + + let _ = std::env!("PATH"); + + // do not lint until `error_in_core` is stable + use std::error::Error; + + // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator` + use core::iter::Iterator; + //~^ ERROR: used import from `std` instead of `core` +} + +#[warn(clippy::std_instead_of_alloc)] +fn std_instead_of_alloc() { + // Only lint once. + use alloc::vec; + //~^ ERROR: used import from `std` instead of `alloc` + use alloc::vec::Vec; + //~^ ERROR: used import from `std` instead of `alloc` +} + +#[warn(clippy::alloc_instead_of_core)] +fn alloc_instead_of_core() { + use core::slice::from_ref; + //~^ ERROR: used import from `alloc` instead of `core` +} + +fn main() { + std_instead_of_core(); + std_instead_of_alloc(); + alloc_instead_of_core(); +} diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 2e44487d77e35..63a096384d716 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -17,7 +17,6 @@ fn std_instead_of_core() { // Multiple imports use std::fmt::{Debug, Result}; //~^ ERROR: used import from `std` instead of `core` - //~| ERROR: used import from `std` instead of `core` // Function calls let ptr = std::ptr::null::(); diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 7435d716157ae..ca26f77bd37f9 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -2,103 +2,76 @@ error: used import from `std` instead of `core` --> $DIR/std_instead_of_core.rs:9:9 | LL | use std::hash::Hasher; - | ^^^^^^^^^^^^^^^^^ + | ^^^ help: consider importing the item from `core`: `core` | - = help: consider importing the item from `core` = note: `-D clippy::std-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:12:9 + --> $DIR/std_instead_of_core.rs:12:11 | LL | use ::std::hash::Hash; - | ^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:18:20 + --> $DIR/std_instead_of_core.rs:18:9 | LL | use std::fmt::{Debug, Result}; - | ^^^^^ - | - = help: consider importing the item from `core` - -error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:18:27 - | -LL | use std::fmt::{Debug, Result}; - | ^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:23:15 + --> $DIR/std_instead_of_core.rs:22:15 | LL | let ptr = std::ptr::null::(); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:25:19 + --> $DIR/std_instead_of_core.rs:24:21 | LL | let ptr_mut = ::std::ptr::null_mut::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:29:16 + --> $DIR/std_instead_of_core.rs:28:16 | LL | let cell = std::cell::Cell::new(8u32); - | ^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:31:25 + --> $DIR/std_instead_of_core.rs:30:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:40:9 + --> $DIR/std_instead_of_core.rs:39:9 | LL | use std::iter::Iterator; - | ^^^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:47:9 + --> $DIR/std_instead_of_core.rs:46:9 | LL | use std::vec; - | ^^^^^^^^ + | ^^^ help: consider importing the item from `alloc`: `alloc` | - = help: consider importing the item from `alloc` = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:49:9 + --> $DIR/std_instead_of_core.rs:48:9 | LL | use std::vec::Vec; - | ^^^^^^^^^^^^^ - | - = help: consider importing the item from `alloc` + | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> $DIR/std_instead_of_core.rs:55:9 + --> $DIR/std_instead_of_core.rs:54:9 | LL | use alloc::slice::from_ref; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ help: consider importing the item from `core`: `core` | - = help: consider importing the item from `core` = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/transmute_32bit.stderr b/tests/ui/transmute_32bit.stderr index 75ddca60d2aaa..baa819e30fded 100644 --- a/tests/ui/transmute_32bit.stderr +++ b/tests/ui/transmute_32bit.stderr @@ -1,39 +1,29 @@ -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `f32` to a pointer --> $DIR/transmute_32bit.rs:6:31 | LL | let _: *const usize = std::mem::transmute(6.0f32); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: source type: `f32` (32 bits) - = note: target type: `*const usize` (64 bits) + = note: `-D clippy::wrong-transmute` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wrong_transmute)]` -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `f32` to a pointer --> $DIR/transmute_32bit.rs:8:29 | LL | let _: *mut usize = std::mem::transmute(6.0f32); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `f32` (32 bits) - = note: target type: `*mut usize` (64 bits) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `char` to a pointer --> $DIR/transmute_32bit.rs:10:31 | LL | let _: *const usize = std::mem::transmute('x'); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `char` (32 bits) - = note: target type: `*const usize` (64 bits) + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `char` to a pointer --> $DIR/transmute_32bit.rs:12:29 | LL | let _: *mut usize = std::mem::transmute('x'); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `char` (32 bits) - = note: target type: `*mut usize` (64 bits) + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/unnecessary_struct_initialization.fixed b/tests/ui/unnecessary_struct_initialization.fixed index b5dedef5f4c87..f3cf65da2d6ad 100644 --- a/tests/ui/unnecessary_struct_initialization.fixed +++ b/tests/ui/unnecessary_struct_initialization.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)] +#![allow(clippy::non_canonical_clone_impl, unused)] #![warn(clippy::unnecessary_struct_initialization)] struct S { diff --git a/tests/ui/unnecessary_struct_initialization.rs b/tests/ui/unnecessary_struct_initialization.rs index 2222c3ddf3708..bd5302f9d85db 100644 --- a/tests/ui/unnecessary_struct_initialization.rs +++ b/tests/ui/unnecessary_struct_initialization.rs @@ -1,4 +1,4 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)] +#![allow(clippy::non_canonical_clone_impl, unused)] #![warn(clippy::unnecessary_struct_initialization)] struct S { diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index 3ff2acbe28f49..bcbca971a7820 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -120,6 +120,7 @@ fn issue11075() { stringify!($e) }; } + #[allow(clippy::never_loop)] for _string in [repro!(true), repro!(null)] { unimplemented!(); } diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index 2ab025f424ac6..087425585de8b 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -120,6 +120,7 @@ fn issue11075() { stringify!($e) }; } + #[allow(clippy::never_loop)] for _string in vec![repro!(true), repro!(null)] { unimplemented!(); } diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr index a28024d236a1e..fc261838fe36d 100644 --- a/tests/ui/vec.stderr +++ b/tests/ui/vec.stderr @@ -86,31 +86,31 @@ LL | for _ in vec![1, 2, 3] {} | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` error: useless use of `vec!` - --> $DIR/vec.rs:123:20 + --> $DIR/vec.rs:124:20 | LL | for _string in vec![repro!(true), repro!(null)] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[repro!(true), repro!(null)]` error: useless use of `vec!` - --> $DIR/vec.rs:140:18 + --> $DIR/vec.rs:141:18 | LL | in_macro!(1, vec![1, 2], vec![1; 2]); | ^^^^^^^^^^ help: you can use an array directly: `[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:140:30 + --> $DIR/vec.rs:141:30 | LL | in_macro!(1, vec![1, 2], vec![1; 2]); | ^^^^^^^^^^ help: you can use an array directly: `[1; 2]` error: useless use of `vec!` - --> $DIR/vec.rs:159:14 + --> $DIR/vec.rs:160:14 | LL | for a in vec![1, 2, 3] { | ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]` error: useless use of `vec!` - --> $DIR/vec.rs:163:14 + --> $DIR/vec.rs:164:14 | LL | for a in vec![String::new(), String::new()] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]` diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr index fc24dba45437c..6d382a267ad81 100644 --- a/tests/ui/write_literal_2.stderr +++ b/tests/ui/write_literal_2.stderr @@ -2,7 +2,9 @@ error: unnecessary raw string literal --> $DIR/write_literal_2.rs:13:24 | LL | writeln!(v, r"{}", r"{hello}"); - | ^^^^^^^^^^ help: try: `"{hello}"` + | -^^^^^^^^^ + | | + | help: use a string literal instead: `"{hello}"` | = note: `-D clippy::needless-raw-strings` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]` From 7f870201d3c46f40e6be34424ded337fe702fecb Mon Sep 17 00:00:00 2001 From: mojave2 Date: Wed, 13 Sep 2023 11:13:51 +0800 Subject: [PATCH 14/78] add `byref` checking for the guard's local --- clippy_lints/src/matches/redundant_guards.rs | 77 +++++++++++++++----- tests/ui/redundant_guards.fixed | 41 +++++++++++ tests/ui/redundant_guards.rs | 41 +++++++++++ tests/ui/redundant_guards.stderr | 38 +++++++++- 4 files changed, 176 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 29af4812351e3..1f61e4df30dbf 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -34,24 +34,45 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { ], MatchSource::Normal, ) = if_expr.kind + && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, scrutinee, outer_arm) { + if is_field && is_byref { return; } + let pat_span = if let PatKind::Ref(pat, _) = arm.pat.kind { + if is_byref { pat.span } else { continue; } + } else { + if is_byref { continue; } + arm.pat.span + }; + emit_redundant_guards( cx, outer_arm, if_expr.span, - scrutinee, - arm.pat.span, + pat_span, + binding_span, + is_field, arm.guard, ); } // `Some(x) if let Some(2) = x` - else if let Guard::IfLet(let_expr) = guard { + else if let Guard::IfLet(let_expr) = guard + && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, let_expr.init, outer_arm) + { + if is_field && is_byref { return; } + let pat_span = if let PatKind::Ref(pat, _) = let_expr.pat.kind { + if is_byref && !is_field { pat.span } else { continue; } + } else { + if is_byref { continue; } + let_expr.pat.span + }; + emit_redundant_guards( cx, outer_arm, let_expr.span, - let_expr.init, - let_expr.pat.span, + pat_span, + binding_span, + is_field, None, ); } @@ -67,31 +88,48 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { // // This isn't necessary in the other two checks, as they must be a pattern already. && cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat) + && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, local, outer_arm) { + if is_field && is_byref { return; } + let pat_span = if let ExprKind::AddrOf(rustc_ast::BorrowKind::Ref, _, expr) = pat.kind { + if is_byref { expr.span } else { continue; } + } else { + if is_byref { continue; } + pat.span + }; + emit_redundant_guards( cx, outer_arm, if_expr.span, - local, - pat.span, + pat_span, + binding_span, + is_field, None, ); } } } -fn get_pat_binding<'tcx>(cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>) -> Option<(Span, bool)> { +fn get_pat_binding<'tcx>( + cx: &LateContext<'tcx>, + guard_expr: &Expr<'_>, + outer_arm: &Arm<'tcx>, +) -> Option<(Span, bool, bool)> { if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) { let mut span = None; let mut multiple_bindings = false; + let mut is_byref = false; // `each_binding` gives the `HirId` of the `Pat` itself, not the binding outer_arm.pat.walk(|pat| { - if let PatKind::Binding(_, hir_id, _, _) = pat.kind + if let PatKind::Binding(bind_annot, hir_id, _, _) = pat.kind && hir_id == local - && span.replace(pat.span).is_some() { - multiple_bindings = true; - return false; + is_byref = matches!(bind_annot.0, rustc_ast::ByRef::Yes); + if span.replace(pat.span).is_some() { + multiple_bindings = true; + return false; + } } true @@ -102,7 +140,8 @@ fn get_pat_binding<'tcx>(cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_ar return span.map(|span| { ( span, - !matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), + matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), + is_byref, ) }); } @@ -115,14 +154,12 @@ fn emit_redundant_guards<'tcx>( cx: &LateContext<'tcx>, outer_arm: &Arm<'tcx>, guard_span: Span, - local: &Expr<'_>, pat_span: Span, + binding_span: Span, + field_binding: bool, inner_guard: Option>, ) { let mut app = Applicability::MaybeIncorrect; - let Some((pat_binding, can_use_shorthand)) = get_pat_binding(cx, local, outer_arm) else { - return; - }; span_lint_and_then( cx, @@ -134,10 +171,10 @@ fn emit_redundant_guards<'tcx>( diag.multipart_suggestion_verbose( "try", vec![ - if can_use_shorthand { - (pat_binding, binding_replacement.into_owned()) + if field_binding { + (binding_span.shrink_to_hi(), format!(": {binding_replacement}")) } else { - (pat_binding.shrink_to_hi(), format!(": {binding_replacement}")) + (binding_span, binding_replacement.into_owned()) }, ( guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()), diff --git a/tests/ui/redundant_guards.fixed b/tests/ui/redundant_guards.fixed index 9a1ec3a4d36a6..6793b9c0f6d38 100644 --- a/tests/ui/redundant_guards.fixed +++ b/tests/ui/redundant_guards.fixed @@ -143,3 +143,44 @@ fn g(opt_s: Option) { _ => {}, } } + +mod issue11465 { + enum A { + Foo([u8; 3]), + } + + struct B { + b: String, + c: i32, + } + + fn issue11465() { + let c = Some(1); + match c { + Some(1) => {}, + Some(2) => {}, + Some(3) => {}, + _ => {}, + }; + + let enum_a = A::Foo([98, 97, 114]); + match enum_a { + A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if let b"bar" = arr => {}, + A::Foo(ref arr) if matches!(arr, b"baz") => {}, + _ => {}, + }; + + let struct_b = B { + b: "bar".to_string(), + c: 42, + }; + match struct_b { + B { ref b, .. } if b == "bar" => {}, + B { ref c, .. } if c == &1 => {}, + B { ref c, .. } if let &1 = c => {}, + B { ref c, .. } if matches!(c, &1) => {}, + _ => {}, + } + } +} diff --git a/tests/ui/redundant_guards.rs b/tests/ui/redundant_guards.rs index e2e0ee816c51b..e63cd29e8afe9 100644 --- a/tests/ui/redundant_guards.rs +++ b/tests/ui/redundant_guards.rs @@ -143,3 +143,44 @@ fn g(opt_s: Option) { _ => {}, } } + +mod issue11465 { + enum A { + Foo([u8; 3]), + } + + struct B { + b: String, + c: i32, + } + + fn issue11465() { + let c = Some(1); + match c { + Some(ref x) if x == &1 => {}, + Some(ref x) if let &2 = x => {}, + Some(ref x) if matches!(x, &3) => {}, + _ => {}, + }; + + let enum_a = A::Foo([98, 97, 114]); + match enum_a { + A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if let b"bar" = arr => {}, + A::Foo(ref arr) if matches!(arr, b"baz") => {}, + _ => {}, + }; + + let struct_b = B { + b: "bar".to_string(), + c: 42, + }; + match struct_b { + B { ref b, .. } if b == "bar" => {}, + B { ref c, .. } if c == &1 => {}, + B { ref c, .. } if let &1 = c => {}, + B { ref c, .. } if matches!(c, &1) => {}, + _ => {}, + } + } +} diff --git a/tests/ui/redundant_guards.stderr b/tests/ui/redundant_guards.stderr index 5007723438263..54b5f2464e6a6 100644 --- a/tests/ui/redundant_guards.stderr +++ b/tests/ui/redundant_guards.stderr @@ -94,5 +94,41 @@ LL - x if matches!(x, Some(0)) => .., LL + Some(0) => .., | -error: aborting due to 8 previous errors +error: redundant guard + --> $DIR/redundant_guards.rs:160:28 + | +LL | Some(ref x) if x == &1 => {}, + | ^^^^^^^ + | +help: try + | +LL - Some(ref x) if x == &1 => {}, +LL + Some(1) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:161:28 + | +LL | Some(ref x) if let &2 = x => {}, + | ^^^^^^^^^^ + | +help: try + | +LL - Some(ref x) if let &2 = x => {}, +LL + Some(2) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:162:28 + | +LL | Some(ref x) if matches!(x, &3) => {}, + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL - Some(ref x) if matches!(x, &3) => {}, +LL + Some(3) => {}, + | + +error: aborting due to 11 previous errors From 5cd391c4adf0a50d07fe403ce7ddcbeb9922c8b9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 12 Sep 2023 23:47:47 +0200 Subject: [PATCH 15/78] make the set of methods between our two Const types more consistent --- clippy_lints/src/matches/overlapping_arms.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 8be3c178a2912..6e13148f2fc18 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -37,22 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; - let min_constant = mir::ConstantKind::from_value( - cx.tcx.valtree_to_const_val((ty, min_val_const.to_valtree())), - ty, - ); - miri_to_const(cx, min_constant)? + miri_to_const(cx, mir::ConstantKind::from_ty_const(min_val_const, cx.tcx))? }, }; let rhs_const = match rhs { Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; - let max_constant = mir::ConstantKind::from_value( - cx.tcx.valtree_to_const_val((ty, max_val_const.to_valtree())), - ty, - ); - miri_to_const(cx, max_constant)? + miri_to_const(cx, mir::ConstantKind::from_ty_const(max_val_const, cx.tcx))? }, }; let lhs_val = lhs_const.int_value(cx, ty)?; From e3267b1fe7bf90348f76b42fba6ebd09ef8ef714 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 13 Sep 2023 14:39:41 +0200 Subject: [PATCH 16/78] Fix mutaby used async function argument in closure for `needless_pass_by_ref_mut` --- clippy_lints/src/needless_pass_by_ref_mut.rs | 85 +++++++++++++++----- tests/ui/needless_pass_by_ref_mut.rs | 28 ++++++- tests/ui/needless_pass_by_ref_mut.stderr | 26 +++++- 3 files changed, 118 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 7b00eabf97b41..bea44ed92ef38 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -4,12 +4,13 @@ use clippy_utils::source::snippet; use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor}; +use rustc_hir::intravisit::{walk_fn, walk_qpath, FnKind, Visitor}; use rustc_hir::{ - Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath, + Body, BodyId, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, + PatKind, QPath, }; use rustc_hir_typeck::expr_use_visitor as euv; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::associated_body; use rustc_middle::hir::nested_filter::OnlyBodies; @@ -95,6 +96,30 @@ fn should_skip<'tcx>( is_from_proc_macro(cx, &input) } +fn check_closures<'tcx>( + ctx: &mut MutablyUsedVariablesCtxt<'tcx>, + cx: &LateContext<'tcx>, + infcx: &InferCtxt<'tcx>, + checked_closures: &mut FxHashSet, + closures: FxHashSet, +) { + let hir = cx.tcx.hir(); + for closure in closures { + if !checked_closures.insert(closure) { + continue; + } + ctx.prev_bind = None; + ctx.prev_move_to_closure.clear(); + if let Some(body) = hir + .find_by_def_id(closure) + .and_then(associated_body) + .map(|(_, body_id)| hir.body(body_id)) + { + euv::ExprUseVisitor::new(ctx, infcx, closure, cx.param_env, cx.typeck_results()).consume_body(body); + } + } +} + impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { fn check_fn( &mut self, @@ -161,25 +186,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); if is_async { let mut checked_closures = FxHashSet::default(); + + // We retrieve all the closures declared in the async function because they will + // not be found by `euv::Delegate`. + let mut closures_retriever = ClosuresRetriever { + cx, + closures: FxHashSet::default(), + }; + walk_fn(&mut closures_retriever, kind, decl, body.id(), fn_def_id); + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures_retriever.closures); + while !ctx.async_closures.is_empty() { let closures = ctx.async_closures.clone(); ctx.async_closures.clear(); - let hir = cx.tcx.hir(); - for closure in closures { - if !checked_closures.insert(closure) { - continue; - } - ctx.prev_bind = None; - ctx.prev_move_to_closure.clear(); - if let Some(body) = hir - .find_by_def_id(closure) - .and_then(associated_body) - .map(|(_, body_id)| hir.body(body_id)) - { - euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results()) - .consume_body(body); - } - } + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); } } ctx @@ -439,3 +459,30 @@ impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> { } } } + +struct ClosuresRetriever<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + closures: FxHashSet, +} + +impl<'a, 'tcx> Visitor<'tcx> for ClosuresRetriever<'a, 'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_fn( + &mut self, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'tcx>, + body_id: BodyId, + _span: Span, + fn_def_id: LocalDefId, + ) { + if matches!(kind, FnKind::Closure) { + self.closures.insert(fn_def_id); + } + walk_fn(self, kind, decl, body_id, fn_def_id); + } +} diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index e1e5e8fd220b1..da2a72caacb4e 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -1,4 +1,4 @@ -#![allow(clippy::if_same_then_else, clippy::no_effect)] +#![allow(clippy::if_same_then_else, clippy::no_effect, clippy::redundant_closure_call)] #![feature(lint_reasons)] //@no-rustfix use std::ptr::NonNull; @@ -231,6 +231,32 @@ async fn async_vec2(b: &mut Vec) { b.push(true); } +// Should not warn. +pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { + || { + *n += 1; + } +} + +// Should warn. +pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { + //~^ ERROR: this argument is a mutable reference, but not used mutably + || *n + 1 +} + +// Should not warn. +pub async fn closure3(n: &mut usize) { + (|| *n += 1)(); +} + +// Should warn. +pub async fn closure4(n: &mut usize) { + //~^ ERROR: this argument is a mutable reference, but not used mutably + (|| { + let _x = *n + 1; + })(); +} + fn main() { let mut u = 0; let mut v = vec![0]; diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index df3df045776b8..0fb9458d79488 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -107,5 +107,29 @@ error: this argument is a mutable reference, but not used mutably LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` -error: aborting due to 17 previous errors +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:235:25 + | +LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { + | ^^^^^^^^^^ help: consider changing to: `&usize` + | + = warning: changing this function will impact semver compatibility + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:242:20 + | +LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { + | ^^^^^^^^^^ help: consider changing to: `&usize` + | + = warning: changing this function will impact semver compatibility + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:253:26 + | +LL | pub async fn closure4(n: &mut usize) { + | ^^^^^^^^^^ help: consider changing to: `&usize` + | + = warning: changing this function will impact semver compatibility + +error: aborting due to 20 previous errors From 0c7dcba28a502f8a6e9a5da533cb74feda0adb4c Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 13 Sep 2023 18:58:47 +0000 Subject: [PATCH 17/78] Remove `derive_new` test dependency It is the last thing depending on syn 1.0 in clippy --- Cargo.toml | 1 - tests/compile-test.rs | 2 -- tests/ui/redundant_field_names.fixed | 11 +++++++++-- tests/ui/redundant_field_names.rs | 11 +++++++++-- tests/ui/redundant_field_names.stderr | 2 +- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d8b590dbe311..66786004f6e72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ itertools = "0.10.1" # UI test dependencies clippy_utils = { path = "clippy_utils" } -derive-new = "0.5" if_chain = "1.0" quote = "1.0" serde = { version = "1.0.125", features = ["derive"] } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 9fcc269dbf8c2..f340cf5938a7d 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -18,7 +18,6 @@ use test_utils::IS_RUSTC_TEST_SUITE; // in the depinfo file (otherwise cargo thinks they are unused) extern crate clippy_lints; extern crate clippy_utils; -extern crate derive_new; extern crate futures; extern crate if_chain; extern crate itertools; @@ -33,7 +32,6 @@ mod test_utils; static TEST_DEPENDENCIES: &[&str] = &[ "clippy_lints", "clippy_utils", - "derive_new", "futures", "if_chain", "itertools", diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index bbe3b38e547bb..c578e786426f8 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,8 +1,9 @@ +//@aux-build:proc_macros.rs #![warn(clippy::redundant_field_names)] #![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] -extern crate derive_new; +extern crate proc_macros; use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; @@ -18,7 +19,6 @@ struct Person { foo: u8, } -#[derive(new)] pub struct S { v: String, } @@ -57,6 +57,13 @@ fn main() { let _ = Range { start, end }; let _ = RangeInclusive::new(start, end); let _ = RangeToInclusive { end }; + + external! { + let v = String::new(); + let _ = S { + v: v + }; + } } fn issue_3476() { diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 9afa191ce7c70..d8c2286d5ad65 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,8 +1,9 @@ +//@aux-build:proc_macros.rs #![warn(clippy::redundant_field_names)] #![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] -extern crate derive_new; +extern crate proc_macros; use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; @@ -18,7 +19,6 @@ struct Person { foo: u8, } -#[derive(new)] pub struct S { v: String, } @@ -57,6 +57,13 @@ fn main() { let _ = Range { start: start, end: end }; let _ = RangeInclusive::new(start, end); let _ = RangeToInclusive { end: end }; + + external! { + let v = String::new(); + let _ = S { + v: v + }; + } } fn issue_3476() { diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 5fee60b8ea411..6eb1cc7531928 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -44,7 +44,7 @@ LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:81:25 + --> $DIR/redundant_field_names.rs:88:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` From 2aaaeb4e8166cf7ba52cf606eec2219730a06395 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 11 Sep 2023 20:01:48 +0200 Subject: [PATCH 18/78] use AllocId instead of Allocation in ConstValue::ByRef --- clippy_utils/src/consts.rs | 53 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index adeb673b6b9dc..03341feffb8cd 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -684,34 +684,37 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t }, _ => None, }, - mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() { - ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), - ty::Array(sub_type, len) => match sub_type.kind() { - ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) { - Some(len) => alloc - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap())) - .to_owned() - .array_chunks::<4>() - .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk)))) - .collect::>>>() - .map(Constant::Vec), - _ => None, - }, - ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) { - Some(len) => alloc - .inner() - .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap())) - .to_owned() - .array_chunks::<8>() - .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk)))) - .collect::>>>() - .map(Constant::Vec), + mir::ConstantKind::Val(ConstValue::ByRef { alloc_id, offset: _ }, _) => { + let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); + match result.ty().kind() { + ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), + ty::Array(sub_type, len) => match sub_type.kind() { + ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) { + Some(len) => alloc + .inner() + .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap())) + .to_owned() + .array_chunks::<4>() + .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk)))) + .collect::>>>() + .map(Constant::Vec), + _ => None, + }, + ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) { + Some(len) => alloc + .inner() + .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap())) + .to_owned() + .array_chunks::<8>() + .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk)))) + .collect::>>>() + .map(Constant::Vec), + _ => None, + }, _ => None, }, _ => None, - }, - _ => None, + } }, _ => None, } From b2d5d68c58a745c80bc0187dba8c85e1519faabc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 12 Sep 2023 07:49:25 +0200 Subject: [PATCH 19/78] =?UTF-8?q?cleanup=20op=5Fto=5Fconst=20a=20bit;=20re?= =?UTF-8?q?name=20ConstValue::ByRef=20=E2=86=92=20Indirect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs | 2 +- clippy_utils/src/consts.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index f66f33fee1669..4a5b6fa5c18d6 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -232,7 +232,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + ConstValue::Indirect { alloc, offset } if offset.bytes() == 0 => { read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity()) }, _ => None, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 03341feffb8cd..61b944667a4ae 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -684,7 +684,7 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t }, _ => None, }, - mir::ConstantKind::Val(ConstValue::ByRef { alloc_id, offset: _ }, _) => { + mir::ConstantKind::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), From 8d3bbb09640479b43b48a3c3a81580a1d3fbbc04 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Thu, 14 Sep 2023 14:57:05 +0800 Subject: [PATCH 20/78] handle the byref binding in the struct pattern --- clippy_lints/src/matches/redundant_guards.rs | 99 ++++++++++---------- tests/ui/redundant_guards.fixed | 6 +- tests/ui/redundant_guards.stderr | 38 +++++++- 3 files changed, 90 insertions(+), 53 deletions(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index 1f61e4df30dbf..d51d7edc8e353 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::{for_each_expr, is_local_used}; -use rustc_ast::LitKind; +use rustc_ast::{BorrowKind, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind}; use rustc_lint::LateContext; +use rustc_span::symbol::Ident; use rustc_span::Span; use std::ops::ControlFlow; @@ -34,45 +35,37 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { ], MatchSource::Normal, ) = if_expr.kind - && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, scrutinee, outer_arm) + && let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm) { - if is_field && is_byref { return; } - let pat_span = if let PatKind::Ref(pat, _) = arm.pat.kind { - if is_byref { pat.span } else { continue; } - } else { - if is_byref { continue; } - arm.pat.span + let pat_span = match (arm.pat.kind, binding.byref_ident) { + (PatKind::Ref(pat, _), Some(_)) => pat.span, + (PatKind::Ref(..), None) | (_, Some(_)) => continue, + _ => arm.pat.span, }; - emit_redundant_guards( cx, outer_arm, if_expr.span, pat_span, - binding_span, - is_field, + &binding, arm.guard, ); } // `Some(x) if let Some(2) = x` else if let Guard::IfLet(let_expr) = guard - && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, let_expr.init, outer_arm) + && let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm) { - if is_field && is_byref { return; } - let pat_span = if let PatKind::Ref(pat, _) = let_expr.pat.kind { - if is_byref && !is_field { pat.span } else { continue; } - } else { - if is_byref { continue; } - let_expr.pat.span + let pat_span = match (let_expr.pat.kind, binding.byref_ident) { + (PatKind::Ref(pat, _), Some(_)) => pat.span, + (PatKind::Ref(..), None) | (_, Some(_)) => continue, + _ => let_expr.pat.span, }; - emit_redundant_guards( cx, outer_arm, let_expr.span, pat_span, - binding_span, - is_field, + &binding, None, ); } @@ -88,61 +81,63 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { // // This isn't necessary in the other two checks, as they must be a pattern already. && cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat) - && let Some((binding_span, is_field, is_byref)) = get_pat_binding(cx, local, outer_arm) + && let Some(binding) = get_pat_binding(cx, local, outer_arm) { - if is_field && is_byref { return; } - let pat_span = if let ExprKind::AddrOf(rustc_ast::BorrowKind::Ref, _, expr) = pat.kind { - if is_byref { expr.span } else { continue; } - } else { - if is_byref { continue; } - pat.span + let pat_span = match (pat.kind, binding.byref_ident) { + (ExprKind::AddrOf(BorrowKind::Ref, _, expr), Some(_)) => expr.span, + (ExprKind::AddrOf(..), None) | (_, Some(_)) => continue, + _ => pat.span, }; - emit_redundant_guards( cx, outer_arm, if_expr.span, pat_span, - binding_span, - is_field, + &binding, None, ); } } } +struct PatBindingInfo { + span: Span, + byref_ident: Option, + is_field: bool, +} + fn get_pat_binding<'tcx>( cx: &LateContext<'tcx>, guard_expr: &Expr<'_>, outer_arm: &Arm<'tcx>, -) -> Option<(Span, bool, bool)> { +) -> Option { if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) { let mut span = None; + let mut byref_ident = None; let mut multiple_bindings = false; - let mut is_byref = false; // `each_binding` gives the `HirId` of the `Pat` itself, not the binding outer_arm.pat.walk(|pat| { - if let PatKind::Binding(bind_annot, hir_id, _, _) = pat.kind + if let PatKind::Binding(bind_annot, hir_id, ident, _) = pat.kind && hir_id == local { - is_byref = matches!(bind_annot.0, rustc_ast::ByRef::Yes); + if matches!(bind_annot.0, rustc_ast::ByRef::Yes) { + let _ = byref_ident.insert(ident); + } + // the second call of `replce()` returns a `Some(span)`, meaning a multi-binding pattern if span.replace(pat.span).is_some() { multiple_bindings = true; return false; } } - true }); // Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)` if !multiple_bindings { - return span.map(|span| { - ( - span, - matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), - is_byref, - ) + return span.map(|span| PatBindingInfo { + span, + byref_ident, + is_field: matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), }); } } @@ -155,8 +150,7 @@ fn emit_redundant_guards<'tcx>( outer_arm: &Arm<'tcx>, guard_span: Span, pat_span: Span, - binding_span: Span, - field_binding: bool, + pat_binding: &PatBindingInfo, inner_guard: Option>, ) { let mut app = Applicability::MaybeIncorrect; @@ -168,14 +162,21 @@ fn emit_redundant_guards<'tcx>( "redundant guard", |diag| { let binding_replacement = snippet_with_applicability(cx, pat_span, "", &mut app); + let suggestion_span = match *pat_binding { + PatBindingInfo { + span, + byref_ident: Some(ident), + is_field: true, + } => (span, format!("{ident}: {binding_replacement}")), + PatBindingInfo { + span, is_field: true, .. + } => (span.shrink_to_hi(), format!(": {binding_replacement}")), + PatBindingInfo { span, .. } => (span, binding_replacement.into_owned()), + }; diag.multipart_suggestion_verbose( "try", vec![ - if field_binding { - (binding_span.shrink_to_hi(), format!(": {binding_replacement}")) - } else { - (binding_span, binding_replacement.into_owned()) - }, + suggestion_span, ( guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()), inner_guard.map_or_else(String::new, |guard| { diff --git a/tests/ui/redundant_guards.fixed b/tests/ui/redundant_guards.fixed index 6793b9c0f6d38..20fcc1254a66d 100644 --- a/tests/ui/redundant_guards.fixed +++ b/tests/ui/redundant_guards.fixed @@ -177,9 +177,9 @@ mod issue11465 { }; match struct_b { B { ref b, .. } if b == "bar" => {}, - B { ref c, .. } if c == &1 => {}, - B { ref c, .. } if let &1 = c => {}, - B { ref c, .. } if matches!(c, &1) => {}, + B { c: 1, .. } => {}, + B { c: 1, .. } => {}, + B { c: 1, .. } => {}, _ => {}, } } diff --git a/tests/ui/redundant_guards.stderr b/tests/ui/redundant_guards.stderr index 54b5f2464e6a6..0a6146413d39a 100644 --- a/tests/ui/redundant_guards.stderr +++ b/tests/ui/redundant_guards.stderr @@ -130,5 +130,41 @@ LL - Some(ref x) if matches!(x, &3) => {}, LL + Some(3) => {}, | -error: aborting due to 11 previous errors +error: redundant guard + --> $DIR/redundant_guards.rs:180:32 + | +LL | B { ref c, .. } if c == &1 => {}, + | ^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if c == &1 => {}, +LL + B { c: 1, .. } => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:181:32 + | +LL | B { ref c, .. } if let &1 = c => {}, + | ^^^^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if let &1 = c => {}, +LL + B { c: 1, .. } => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:182:32 + | +LL | B { ref c, .. } if matches!(c, &1) => {}, + | ^^^^^^^^^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if matches!(c, &1) => {}, +LL + B { c: 1, .. } => {}, + | + +error: aborting due to 14 previous errors From 272df70b50992885dcc70b8c34471234985b3be2 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 13 Sep 2023 13:55:23 +0000 Subject: [PATCH 21/78] treat host effect params as erased generics in codegen This fixes the changes brought to codegen tests when effect params are added to libcore, by not attempting to monomorphize functions that get the host param by being `const fn`. --- clippy_lints/src/derive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index d3311792cfa8b..2bdac1352dce3 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -343,7 +343,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h // If the current self type doesn't implement Copy (due to generic constraints), search to see if // there's a Copy impl for any instance of the adt. if !is_copy(cx, ty) { - if ty_subs.non_erasable_generics().next().is_some() { + if ty_subs.non_erasable_generics(cx.tcx, ty_adt.did()).next().is_some() { let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).map_or(false, |impls| { impls.iter().any(|&id| { matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) From 477a9b88bf31168e747a463b4942a8d64ae5917d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 12 Sep 2023 14:44:38 +0200 Subject: [PATCH 22/78] fix clippy (and MIR printing) handling of ConstValue::Indirect slices --- clippy_utils/src/consts.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 61b944667a4ae..fcb90c63a6fc6 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -671,19 +671,10 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), _ => None, }, - mir::ConstantKind::Val(ConstValue::Slice { data, start, end }, _) => match result.ty().kind() { - ty::Ref(_, tam, _) => match tam.kind() { - ty::Str => String::from_utf8( - data.inner() - .inspect_with_uninit_and_ptr_outside_interpreter(start..end) - .to_owned(), - ) - .ok() - .map(Constant::Str), - _ => None, - }, - _ => None, - }, + mir::ConstantKind::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => { + let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; + String::from_utf8(data.to_owned()).ok().map(Constant::Str) + } mir::ConstantKind::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); match result.ty().kind() { From c29de92d85f22bdc8814e87e4d4ee9f46d3c679e Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 1 Sep 2023 17:05:30 +0000 Subject: [PATCH 23/78] Return a value from find_format_args instead of using a callback --- clippy_lints/src/explicit_write.rs | 90 +++++++++---------- clippy_lints/src/format.rs | 14 ++- clippy_lints/src/format_args.rs | 20 ++--- clippy_lints/src/format_impl.rs | 41 +++++---- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 13 ++- .../src/utils/format_args_collector.rs | 28 ++++-- clippy_lints/src/write.rs | 10 +-- clippy_utils/src/macros.rs | 37 ++++---- 9 files changed, 126 insertions(+), 129 deletions(-) diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 4b9ca8c917e52..b612cc00bf97e 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -57,54 +57,52 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { } else { None } + && let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) { - find_format_args(cx, write_arg, ExpnId::root(), |format_args| { - let calling_macro = - // ordering is important here, since `writeln!` uses `write!` internally - if is_expn_of(write_call.span, "writeln").is_some() { - Some("writeln") - } else if is_expn_of(write_call.span, "write").is_some() { - Some("write") - } else { - None - }; - let prefix = if dest_name == "stderr" { - "e" - } else { - "" - }; + // ordering is important here, since `writeln!` uses `write!` internally + let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() { + Some("writeln") + } else if is_expn_of(write_call.span, "write").is_some() { + Some("write") + } else { + None + }; + let prefix = if dest_name == "stderr" { + "e" + } else { + "" + }; - // We need to remove the last trailing newline from the string because the - // underlying `fmt::write` function doesn't know whether `println!` or `print!` was - // used. - let (used, sugg_mac) = if let Some(macro_name) = calling_macro { - ( - format!("{macro_name}!({dest_name}(), ...)"), - macro_name.replace("write", "print"), - ) - } else { - ( - format!("{dest_name}().write_fmt(...)"), - "print".into(), - ) - }; - let mut applicability = Applicability::MachineApplicable; - let inputs_snippet = snippet_with_applicability( - cx, - format_args_inputs_span(format_args), - "..", - &mut applicability, - ); - span_lint_and_sugg( - cx, - EXPLICIT_WRITE, - expr.span, - &format!("use of `{used}.unwrap()`"), - "try", - format!("{prefix}{sugg_mac}!({inputs_snippet})"), - applicability, - ); - }); + // We need to remove the last trailing newline from the string because the + // underlying `fmt::write` function doesn't know whether `println!` or `print!` was + // used. + let (used, sugg_mac) = if let Some(macro_name) = calling_macro { + ( + format!("{macro_name}!({dest_name}(), ...)"), + macro_name.replace("write", "print"), + ) + } else { + ( + format!("{dest_name}().write_fmt(...)"), + "print".into(), + ) + }; + let mut applicability = Applicability::MachineApplicable; + let inputs_snippet = snippet_with_applicability( + cx, + format_args_inputs_span(&format_args), + "..", + &mut applicability, + ); + span_lint_and_sugg( + cx, + EXPLICIT_WRITE, + expr.span, + &format!("use of `{used}.unwrap()`"), + "try", + format!("{prefix}{sugg_mac}!({inputs_snippet})"), + applicability, + ); } } } diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index f4f8bdc2c44e5..b748d32936792 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -43,14 +43,10 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { - return; - } - - find_format_args(cx, expr, macro_call.expn, |format_args| { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) + && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + { let mut applicability = Applicability::MachineApplicable; let call_site = macro_call.span; @@ -91,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { }, _ => {}, } - }); + } } } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 01c714c414b5a..39abf5c2def56 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -186,15 +186,10 @@ impl FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - if !is_format_macro(cx, macro_call.def_id) { - return; - } - let name = cx.tcx.item_name(macro_call.def_id); - - find_format_args(cx, expr, macro_call.expn, |format_args| { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) + && is_format_macro(cx, macro_call.def_id) + && let Some(format_args) = find_format_args(cx, expr, macro_call.expn) + { for piece in &format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece && let Ok(index) = placeholder.argument.index @@ -206,12 +201,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if placeholder.format_trait != FormatTrait::Display || placeholder.format_options != FormatOptions::default() - || is_aliased(format_args, index) + || is_aliased(&format_args, index) { continue; } if let Ok(arg_hir_expr) = arg_expr { + let name = cx.tcx.item_name(macro_call.def_id); check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr); check_to_string_in_format_args(cx, name, arg_hir_expr); } @@ -219,9 +215,9 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { } if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed); + check_uninlined_args(cx, &format_args, macro_call.span, macro_call.def_id, self.ignore_mixed); } - }); + } } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 76369bccf9e3b..1d2f7cb71303b 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -170,30 +170,29 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, if let Some(outer_macro) = root_macro_call_first_node(cx, expr) && let macro_def_id = outer_macro.def_id && is_format_macro(cx, macro_def_id) + && let Some(format_args) = find_format_args(cx, expr, outer_macro.expn) { - find_format_args(cx, expr, outer_macro.expn, |format_args| { - for piece in &format_args.template { - if let FormatArgsPiece::Placeholder(placeholder) = piece - && let trait_name = match placeholder.format_trait { - FormatTrait::Display => sym::Display, - FormatTrait::Debug => sym::Debug, - FormatTrait::LowerExp => sym!(LowerExp), - FormatTrait::UpperExp => sym!(UpperExp), - FormatTrait::Octal => sym!(Octal), - FormatTrait::Pointer => sym::Pointer, - FormatTrait::Binary => sym!(Binary), - FormatTrait::LowerHex => sym!(LowerHex), - FormatTrait::UpperHex => sym!(UpperHex), - } - && trait_name == impl_trait.name - && let Ok(index) = placeholder.argument.index - && let Some(arg) = format_args.arguments.all_args().get(index) - && let Ok(arg_expr) = find_format_arg_expr(expr, arg) - { - check_format_arg_self(cx, expr.span, arg_expr, impl_trait); + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(placeholder) = piece + && let trait_name = match placeholder.format_trait { + FormatTrait::Display => sym::Display, + FormatTrait::Debug => sym::Debug, + FormatTrait::LowerExp => sym!(LowerExp), + FormatTrait::UpperExp => sym!(UpperExp), + FormatTrait::Octal => sym!(Octal), + FormatTrait::Pointer => sym::Pointer, + FormatTrait::Binary => sym!(Binary), + FormatTrait::LowerHex => sym!(LowerHex), + FormatTrait::UpperHex => sym!(UpperHex), } + && trait_name == impl_trait.name + && let Ok(index) = placeholder.argument.index + && let Some(arg) = format_args.arguments.all_args().get(index) + && let Ok(arg_expr) = find_format_arg_expr(expr, arg) + { + check_format_arg_self(cx, expr.span, arg_expr, impl_trait); } - }); + } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f5035dcfeeb6a..3f6bdb2b62cc8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -609,7 +609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: .collect(), )) }); - store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector)); + store.register_early_pass(|| Box::::default()); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index d3e90e4bba392..40e487bf65058 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -131,13 +131,12 @@ pub(super) fn check<'tcx>( let mut applicability = Applicability::MachineApplicable; - //Special handling for `format!` as arg_root + // Special handling for `format!` as arg_root if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { - if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { - return; - } - find_format_args(cx, arg_root, macro_call.expn, |format_args| { - let span = format_args_inputs_span(format_args); + if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) + && let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn) + { + let span = format_args_inputs_span(&format_args); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, @@ -148,7 +147,7 @@ pub(super) fn check<'tcx>( format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, ); - }); + } return; } diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index 6d3493523e6fc..94a9a7c241bb7 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -1,12 +1,15 @@ -use clippy_utils::macros::collect_ast_format_args; +use clippy_utils::macros::AST_FORMAT_ARGS; use clippy_utils::source::snippet_opt; use itertools::Itertools; -use rustc_ast::{Expr, ExprKind, FormatArgs}; +use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; +use rustc_data_structures::fx::FxHashMap; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::hygiene; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{hygiene, Span}; use std::iter::once; +use std::mem; +use std::rc::Rc; declare_clippy_lint! { /// ### What it does @@ -17,7 +20,12 @@ declare_clippy_lint! { "collects `format_args` AST nodes for use in later lints" } -declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); +#[derive(Default)] +pub struct FormatArgsCollector { + format_args: FxHashMap>, +} + +impl_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); impl EarlyLintPass for FormatArgsCollector { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { @@ -26,9 +34,17 @@ impl EarlyLintPass for FormatArgsCollector { return; } - collect_ast_format_args(expr.span, args); + self.format_args + .insert(expr.span.with_parent(None), Rc::new((**args).clone())); } } + + fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) { + AST_FORMAT_ARGS.with(|ast_format_args| { + let result = ast_format_args.set(mem::take(&mut self.format_args)); + debug_assert!(result.is_ok()); + }); + } } /// Detects if the format string or an argument has its span set by a proc macro to something inside diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index a9957b18a53b3..da083fb14aae1 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -304,7 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { _ => return, } - find_format_args(cx, expr, macro_call.expn, |format_args| { + if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) { // ignore `writeln!(w)` and `write!(v, some_macro!())` if format_args.span.from_expansion() { return; @@ -312,15 +312,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { match diag_name { sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, format_args, ¯o_call, name); + check_newline(cx, &format_args, ¯o_call, name); }, sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, format_args, ¯o_call, name); + check_empty_string(cx, &format_args, ¯o_call, name); }, _ => {}, } - check_literal(cx, format_args, name); + check_literal(cx, &format_args, name); if !self.in_debug_impl { for piece in &format_args.template { @@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for Write { } } } - }); + } } } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 98724fcbe96a3..82508bcdb857a 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -10,8 +10,9 @@ use rustc_lint::LateContext; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; -use std::cell::RefCell; +use std::cell::OnceCell; use std::ops::ControlFlow; +use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -374,30 +375,21 @@ thread_local! { /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an /// assumption that the early pass that populates the map and the later late passes will all be /// running on the same thread. - static AST_FORMAT_ARGS: RefCell> = { + #[doc(hidden)] + pub static AST_FORMAT_ARGS: OnceCell>> = { static CALLED: AtomicBool = AtomicBool::new(false); debug_assert!( !CALLED.swap(true, Ordering::SeqCst), "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread", ); - RefCell::default() + OnceCell::new() }; } -/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by -/// `FormatArgsCollector` -pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) { - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args - .borrow_mut() - .insert(span.with_parent(None), format_args.clone()); - }); -} - -/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a -/// descendant of `expn_id` -pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) { +/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of +/// `expn_id` +pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option> { let format_args_expr = for_each_expr(start, |expr| { let ctxt = expr.span.ctxt(); if ctxt.outer_expn().is_descendant_of(expn_id) { @@ -412,13 +404,14 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, } else { ControlFlow::Continue(Descend::No) } - }); + })?; - if let Some(expr) = format_args_expr { - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow().get(&expr.span.with_parent(None)).map(callback); - }); - } + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args + .get()? + .get(&format_args_expr.span.with_parent(None)) + .map(Rc::clone) + }) } /// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if From da4244e79c49d4a938c02eebb955f9b217001213 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 14 Sep 2023 17:47:36 +0000 Subject: [PATCH 24/78] Ignore `#[doc(hidden)]` functions in clippy doc lints --- clippy_lints/src/doc.rs | 4 ++++ tests/ui/doc_errors.rs | 15 +++++++++++++++ tests/ui/doc_errors.stderr | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 4498e9ccdec25..bf2add6aa64f7 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -479,6 +479,10 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ Some(("fake".into(), "fake".into())) } + if is_doc_hidden(attrs) { + return None; + } + let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let mut doc = String::new(); for fragment in &fragments { diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index d661231c59ea8..9b3783aaf09de 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -101,6 +101,11 @@ impl Struct1 { fn block_comment_leading_asterisks() -> Result<(), ()> { unimplemented!(); } + + #[doc(hidden)] + fn doc_hidden() -> Result<(), ()> { + unimplemented!(); + } } pub trait Trait1 { @@ -111,6 +116,11 @@ pub trait Trait1 { /// # Errors /// A description of the errors goes here. fn trait_method_with_errors_header() -> Result<(), ()>; + + #[doc(hidden)] + fn doc_hidden() -> Result<(), ()> { + unimplemented!(); + } } impl Trait1 for Struct1 { @@ -123,6 +133,11 @@ impl Trait1 for Struct1 { } } +#[doc(hidden)] +pub trait DocHidden { + fn f() -> Result<(), ()>; +} + fn main() -> Result<(), ()> { Ok(()) } diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index 28b2db2440b7a..dc59675b9e5f2 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -38,7 +38,7 @@ LL | pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:108:5 + --> $DIR/doc_errors.rs:113:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 1d49cb6118c346871f85dc3a0e7bb3330b09687b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 11 Sep 2023 09:52:45 +0200 Subject: [PATCH 25/78] move required_consts check to general post-mono-check function --- clippy_lints/src/non_copy_const.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 243192385c256..8846633378798 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -204,7 +204,7 @@ fn is_value_unfrozen_raw<'tcx>( // similar to 2., but with the a frozen variant) (e.g. borrowing // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). - err == ErrorHandled::TooGeneric + matches!(err, ErrorHandled::TooGeneric(..)) }, |val| val.map_or(true, |val| inner(cx, val, ty)), ) @@ -244,8 +244,8 @@ pub fn const_eval_resolve<'tcx>( }; tcx.const_eval_global_id_for_typeck(param_env, cid, span) }, - Ok(None) => Err(ErrorHandled::TooGeneric), - Err(err) => Err(ErrorHandled::Reported(err.into())), + Ok(None) => Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP))), + Err(err) => Err(ErrorHandled::Reported(err.into(), span.unwrap_or(rustc_span::DUMMY_SP))), } } From 3226e4b01089b4e8e8d474a8d498bb0c68e76ed2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 11 Sep 2023 23:09:11 +0200 Subject: [PATCH 26/78] don't point at const usage site for resolution-time errors also share the code that emits the actual error --- tests/ui-toml/suppress_lint_in_const/test.stderr | 2 +- tests/ui/indexing_slicing_index.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr index d14974faffa67..f8ace7995939a 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.stderr +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -4,7 +4,7 @@ error[E0080]: evaluation of `main::{constant#3}` failed LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -note: erroneous constant used +note: erroneous constant encountered --> $DIR/test.rs:37:5 | LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 64facf208034b..1c34875d2b8ab 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -24,7 +24,7 @@ error[E0080]: evaluation of `main::{constant#3}` failed LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -note: erroneous constant used +note: erroneous constant encountered --> $DIR/indexing_slicing_index.rs:48:5 | LL | const { &ARR[idx4()] }; From a2a31a0a2fd356f681c78498719dea8aa2523190 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 14 Sep 2023 21:20:50 +0000 Subject: [PATCH 27/78] Ignore closures for some type lints --- clippy_lints/src/types/mod.rs | 11 ++++++----- tests/ui/redundant_allocation.rs | 5 +++++ tests/ui/vec_box_sized.fixed | 5 +++++ tests/ui/vec_box_sized.rs | 5 +++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 79f9d45d597ef..71a4b3fba1b59 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -315,7 +315,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn( &mut self, cx: &LateContext<'_>, - _: FnKind<'_>, + fn_kind: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, @@ -340,6 +340,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { CheckTyContext { is_in_trait_impl, is_exported, + in_body: matches!(fn_kind, FnKind::Closure), ..CheckTyContext::default() }, ); @@ -427,7 +428,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { cx, ty, CheckTyContext { - is_local: true, + in_body: true, ..CheckTyContext::default() }, ); @@ -481,7 +482,7 @@ impl Types { } match hir_ty.kind { - TyKind::Path(ref qpath) if !context.is_local => { + TyKind::Path(ref qpath) if !context.in_body => { let hir_id = hir_ty.hir_id; let res = cx.qpath_res(qpath, hir_id); if let Some(def_id) = res.opt_def_id() { @@ -581,8 +582,8 @@ impl Types { #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, - /// `true` for types on local variables. - is_local: bool, + /// `true` for types on local variables and in closure signatures. + in_body: bool, /// `true` for types that are part of the public API. is_exported: bool, is_nested_call: bool, diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index b3257c04f8296..e70f8e71fae85 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -159,4 +159,9 @@ mod box_fat_ptr { //~| NOTE: `Box>` is already on the heap, `Rc>>` makes } +// https://github.com/rust-lang/rust-clippy/issues/11417 +fn type_in_closure() { + let _ = |_: &mut Box>| {}; +} + fn main() {} diff --git a/tests/ui/vec_box_sized.fixed b/tests/ui/vec_box_sized.fixed index 4a5ef83856a41..4363d2224afd2 100644 --- a/tests/ui/vec_box_sized.fixed +++ b/tests/ui/vec_box_sized.fixed @@ -49,4 +49,9 @@ mod inner_mod { } } +// https://github.com/rust-lang/rust-clippy/issues/11417 +fn in_closure() { + let _ = |_: Vec>| {}; +} + fn main() {} diff --git a/tests/ui/vec_box_sized.rs b/tests/ui/vec_box_sized.rs index ea020405a30f0..f4e27fe4bd509 100644 --- a/tests/ui/vec_box_sized.rs +++ b/tests/ui/vec_box_sized.rs @@ -49,4 +49,9 @@ mod inner_mod { } } +// https://github.com/rust-lang/rust-clippy/issues/11417 +fn in_closure() { + let _ = |_: Vec>| {}; +} + fn main() {} From 3c0fc15848395ed5ef714c8817df8bd4f1bc430f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 14 Sep 2023 22:17:51 +0000 Subject: [PATCH 28/78] Truncate files when opening in metadata-collector --- .../internal_lints/metadata_collector.rs | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index f49c3fadb0780..c38a3e81b0f72 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -31,7 +31,7 @@ use serde::{Serialize, Serializer}; use std::collections::{BTreeSet, BinaryHeap}; use std::fmt; use std::fmt::Write as _; -use std::fs::{self, OpenOptions}; +use std::fs::{self, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::process::Command; @@ -229,25 +229,10 @@ impl Drop for MetadataCollector { collect_renames(&mut lints); // Outputting json - if Path::new(JSON_OUTPUT_FILE).exists() { - fs::remove_file(JSON_OUTPUT_FILE).unwrap(); - } - let mut file = OpenOptions::new() - .write(true) - .create(true) - .open(JSON_OUTPUT_FILE) - .unwrap(); - writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap(); + fs::write(JSON_OUTPUT_FILE, serde_json::to_string_pretty(&lints).unwrap()).unwrap(); // Outputting markdown - if Path::new(MARKDOWN_OUTPUT_FILE).exists() { - fs::remove_file(MARKDOWN_OUTPUT_FILE).unwrap(); - } - let mut file = OpenOptions::new() - .write(true) - .create(true) - .open(MARKDOWN_OUTPUT_FILE) - .unwrap(); + let mut file = File::create(MARKDOWN_OUTPUT_FILE).unwrap(); writeln!( file, "") { - // I know this is kinda wasteful, we just don't have regex on `clippy_lints` so... this is the best - // we can do AFAIK. - changelog = changelog[..position].to_string(); - } + let changelog = std::fs::read_to_string(CHANGELOG_PATH).unwrap(); + let mut changelog_file = File::create(CHANGELOG_PATH).unwrap(); + let position = changelog + .find("") + .unwrap(); writeln!( changelog_file, - "{changelog}\n{}\n", + "{}\n{}\n", + &changelog[..position], self.configs_to_markdown(ClippyConfiguration::to_markdown_link) ) .unwrap(); From 1d76eede99126dc6534a0d89dbf04121cd990042 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Fri, 15 Sep 2023 12:24:45 +0800 Subject: [PATCH 29/78] trigger [`transmute_null_to_fn`] on chain of casts --- .../src/transmute/transmute_null_to_fn.rs | 30 ++++++++++++------- tests/ui/transmute_null_to_fn.rs | 11 +++++++ tests/ui/transmute_null_to_fn.stderr | 26 +++++++++++++++- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 4944381da24d5..b26365e34ab94 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -28,35 +28,43 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t return false; } - match arg.kind { + let casts_peeled = peel_casts(arg); + match casts_peeled.kind { // Catching: // transmute over constants that resolve to `null`. - ExprKind::Path(ref _qpath) if matches!(constant(cx, cx.typeck_results(), arg), Some(Constant::RawPtr(0))) => { + ExprKind::Path(ref _qpath) + if matches!( + constant(cx, cx.typeck_results(), casts_peeled), + Some(Constant::RawPtr(0)) + ) => + { lint_expr(cx, expr); true }, - - // Catching: - // `std::mem::transmute(0 as *const i32)` - ExprKind::Cast(inner_expr, _cast_ty) if is_integer_literal(inner_expr, 0) => { - lint_expr(cx, expr); - true - }, - // Catching: // `std::mem::transmute(std::ptr::null::())` ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { lint_expr(cx, expr); true }, - _ => { // FIXME: // Also catch transmutations of variables which are known nulls. // To do this, MIR const propagation seems to be the better tool. // Whenever MIR const prop routines are more developed, this will // become available. As of this writing (25/03/19) it is not yet. + if is_integer_literal(casts_peeled, 0) { + lint_expr(cx, expr); + return true; + } false }, } } + +fn peel_casts<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + match &expr.kind { + ExprKind::Cast(inner_expr, _) => peel_casts(inner_expr), + _ => expr, + } +} diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs index 7d780c803ffd5..b07851e864f68 100644 --- a/tests/ui/transmute_null_to_fn.rs +++ b/tests/ui/transmute_null_to_fn.rs @@ -25,6 +25,17 @@ fn transmute_const() { } } +fn issue_11485() { + unsafe { + let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); + //~^ ERROR: transmuting a known null pointer into a function pointer + let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); + //~^ ERROR: transmuting a known null pointer into a function pointer + let _: fn() = std::mem::transmute(ZPTR as *const u8); + //~^ ERROR: transmuting a known null pointer into a function pointer + } +} + fn main() { one_liners(); transmute_const(); diff --git a/tests/ui/transmute_null_to_fn.stderr b/tests/ui/transmute_null_to_fn.stderr index ab0ac0dd480bc..9073080cbf3c6 100644 --- a/tests/ui/transmute_null_to_fn.stderr +++ b/tests/ui/transmute_null_to_fn.stderr @@ -24,5 +24,29 @@ LL | let _: fn() = std::mem::transmute(ZPTR); | = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value -error: aborting due to 3 previous errors +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:30:23 + | +LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:32:23 + | +LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:34:23 + | +LL | let _: fn() = std::mem::transmute(ZPTR as *const u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: aborting due to 6 previous errors From c9b212d5ffa5dcfe36ed41658c71dcbcbf949dd8 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Fri, 15 Sep 2023 14:23:58 +0800 Subject: [PATCH 30/78] fix filter_map_bool_then with a bool reference --- clippy_lints/src/methods/filter_map_bool_then.rs | 5 +++-- tests/ui/filter_map_bool_then.fixed | 5 +++++ tests/ui/filter_map_bool_then.rs | 5 +++++ tests/ui/filter_map_bool_then.stderr | 8 +++++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index fafc970977009..ac5bff6fee0aa 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Binder; +use rustc_middle::ty::{self, Binder}; use rustc_span::{sym, Span}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { @@ -36,6 +36,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) && match_def_path(cx, def_id, &BOOL_THEN) && !is_from_proc_macro(cx, expr) + && let ref_bool = matches!(cx.typeck_results().expr_ty(recv).kind(), ty::Ref(..)) && let Some(param_snippet) = snippet_opt(cx, param.span) && let Some(filter) = snippet_opt(cx, recv.span) && let Some(map) = snippet_opt(cx, then_body.span) @@ -46,7 +47,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & call_span, "usage of `bool::then` in `filter_map`", "use `filter` then `map` instead", - format!("filter(|&{param_snippet}| {filter}).map(|{param_snippet}| {map})"), + format!("filter(|&{param_snippet}| {}{filter}).map(|{param_snippet}| {map})", if ref_bool { "*" } else { "" }), Applicability::MachineApplicable, ); } diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index 6de870a928985..67b984ce0254d 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -55,3 +55,8 @@ fn main() { fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str> { iter.filter_map(|(_, s): (&str, _)| Some(s)).collect() } + +fn issue11503() { + let bools: &[bool] = &[true, false, false, true]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| *b).map(|(i, b)| i).collect(); +} diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index 4108177e3a0e3..b716fd70fa062 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -55,3 +55,8 @@ fn main() { fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str> { iter.filter_map(|(_, s): (&str, _)| Some(s)).collect() } + +fn issue11503() { + let bools: &[bool] = &[true, false, false, true]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); +} diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index 86ef6edf8eeda..1fee93afb36a0 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -37,5 +37,11 @@ error: usage of `bool::then` in `filter_map` LL | v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i == &NonCopy)).map(|i| i)` -error: aborting due to 6 previous errors +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:61:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| *b).map(|(i, b)| i)` + +error: aborting due to 7 previous errors From c81888eab32e09318a9b95fdad22aea27f6933ea Mon Sep 17 00:00:00 2001 From: mojave2 Date: Fri, 15 Sep 2023 15:05:43 +0800 Subject: [PATCH 31/78] fix FP of let_unit_value on async fn args --- clippy_lints/src/unit_types/let_unit_value.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 704d7abd7e55a..e7915953d85b3 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_middle::lint::{in_external_macro, is_from_async_await}; use rustc_middle::ty; use super::LET_UNIT_VALUE; @@ -16,6 +16,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { if let Some(init) = local.init && !local.pat.span.from_expansion() && !in_external_macro(cx.sess(), local.span) + && !is_from_async_await(local.span) && cx.typeck_results().pat_ty(local.pat).is_unit() { if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer)) From 981e96008b58d34e9c52b0aa3b318974c75b9d1d Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:46:15 +0200 Subject: [PATCH 32/78] new lint: `path_ends_with_ext` --- CHANGELOG.md | 2 + book/src/lint_configuration.md | 10 ++++ clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 7 +++ clippy_lints/src/methods/mod.rs | 47 ++++++++++++++++ .../src/methods/path_ends_with_ext.rs | 53 +++++++++++++++++++ clippy_lints/src/utils/conf.rs | 5 ++ .../toml_unknown_key/conf_unknown_key.stderr | 2 + tests/ui/path_ends_with_ext.fixed | 36 +++++++++++++ tests/ui/path_ends_with_ext.rs | 36 +++++++++++++ tests/ui/path_ends_with_ext.stderr | 17 ++++++ 11 files changed, 216 insertions(+) create mode 100644 clippy_lints/src/methods/path_ends_with_ext.rs create mode 100644 tests/ui/path_ends_with_ext.fixed create mode 100644 tests/ui/path_ends_with_ext.rs create mode 100644 tests/ui/path_ends_with_ext.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aca13a6cf642..5e01e8d07bd79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5245,6 +5245,7 @@ Released 2018-09-13 [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite +[`path_ends_with_ext`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters @@ -5575,5 +5576,6 @@ Released 2018-09-13 [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings [`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments [`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates +[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 52c795e04fe95..3aeb6206fca93 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -751,6 +751,16 @@ Which crates to allow absolute paths from * [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths) +## `allowed-dotfiles` +Additional dotfiles (files or directories starting with a dot) to allow + +**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet`) + +--- +**Affected lints:** +* [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext) + + ## `enforce-iter-loop-reborrow` #### Example ``` diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index ecf44e05920ae..4772f3d80371b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -399,6 +399,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::OR_FUN_CALL_INFO, crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, + crate::methods::PATH_ENDS_WITH_EXT_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3f6bdb2b62cc8..8f603d5d98819 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -662,12 +662,19 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const; store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); + let allowed_dotfiles = conf + .allowed_dotfiles + .iter() + .cloned() + .chain(methods::DEFAULT_ALLOWED_DOTFILES.iter().copied().map(ToOwned::to_owned)) + .collect::>(); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, msrv(), allow_expect_in_tests, allow_unwrap_in_tests, + allowed_dotfiles.clone(), )) }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 81223fa8d954f..f468236480684 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -74,6 +74,7 @@ mod option_map_unwrap_or; mod or_fun_call; mod or_then_unwrap; mod path_buf_push_overwrite; +mod path_ends_with_ext; mod range_zip_with_len; mod read_line_without_trim; mod readonly_write_lock; @@ -120,6 +121,8 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty}; use if_chain::if_chain; +pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; use rustc_hir_analysis::hir_ty_to_ty; @@ -3563,11 +3566,51 @@ declare_clippy_lint! { "calls to `.take()` or `.skip()` that are out of bounds" } +declare_clippy_lint! { + /// ### What it does + /// Looks for calls to `Path::ends_with` calls where the argument looks like a file extension. + /// + /// By default, Clippy has a short list of known filenames that start with a dot + /// but aren't necessarily file extensions (e.g. the `.git` folder), which are allowed by default. + /// The `allowed-dotfiles` configuration can be used to allow additional + /// file extensions that Clippy should not lint. + /// + /// ### Why is this bad? + /// This doesn't actually compare file extensions. Rather, `ends_with` compares the given argument + /// to the last **component** of the path and checks if it matches exactly. + /// + /// ### Known issues + /// File extensions are often at most three characters long, so this only lints in those cases + /// in an attempt to avoid false positives. + /// Any extension names longer than that are assumed to likely be real path components and are + /// therefore ignored. + /// + /// ### Example + /// ```rust + /// # use std::path::Path; + /// fn is_markdown(path: &Path) -> bool { + /// path.ends_with(".md") + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::path::Path; + /// fn is_markdown(path: &Path) -> bool { + /// path.extension().is_some_and(|ext| ext == "md") + /// } + /// ``` + #[clippy::version = "1.74.0"] + pub PATH_ENDS_WITH_EXT, + suspicious, + "attempting to compare file extensions using `Path::ends_with`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, + allowed_dotfiles: FxHashSet, } impl Methods { @@ -3577,12 +3620,14 @@ impl Methods { msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, + allowed_dotfiles: FxHashSet, ) -> Self { Self { avoid_breaking_exported_api, msrv, allow_expect_in_tests, allow_unwrap_in_tests, + allowed_dotfiles, } } } @@ -3703,6 +3748,7 @@ impl_lint_pass!(Methods => [ FILTER_MAP_BOOL_THEN, READONLY_WRITE_LOCK, ITER_OUT_OF_BOUNDS, + PATH_ENDS_WITH_EXT, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3978,6 +4024,7 @@ impl Methods { if let ExprKind::MethodCall(.., span) = expr.kind { case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg); } + path_ends_with_ext::check(cx, recv, arg, expr, &self.msrv, &self.allowed_dotfiles); }, ("expect", [_]) => { match method_call(recv) { diff --git a/clippy_lints/src/methods/path_ends_with_ext.rs b/clippy_lints/src/methods/path_ends_with_ext.rs new file mode 100644 index 0000000000000..3347c8c162015 --- /dev/null +++ b/clippy_lints/src/methods/path_ends_with_ext.rs @@ -0,0 +1,53 @@ +use super::PATH_ENDS_WITH_EXT; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs; +use clippy_utils::msrvs::Msrv; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_ast::{LitKind, StrStyle}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::sym; +use std::fmt::Write; + +pub const DEFAULT_ALLOWED_DOTFILES: &[&str] = &[ + "git", "svn", "gem", "npm", "vim", "env", "rnd", "ssh", "vnc", "smb", "nvm", "bin", +]; + +pub(super) fn check( + cx: &LateContext<'_>, + recv: &Expr<'_>, + path: &Expr<'_>, + expr: &Expr<'_>, + msrv: &Msrv, + allowed_dotfiles: &FxHashSet, +) { + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path) + && !path.span.from_expansion() + && let ExprKind::Lit(lit) = path.kind + && let LitKind::Str(path, StrStyle::Cooked) = lit.node + && let Some(path) = path.as_str().strip_prefix('.') + && (1..=3).contains(&path.len()) + && !allowed_dotfiles.contains(path) + && path.chars().all(char::is_alphanumeric) + { + let mut sugg = snippet(cx, recv.span, "..").into_owned(); + if msrv.meets(msrvs::OPTION_IS_SOME_AND) { + let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#); + } else { + let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#); + }; + + span_lint_and_sugg( + cx, + PATH_ENDS_WITH_EXT, + expr.span, + "this looks like a failed attempt at checking for the file extension", + "try", + sugg, + Applicability::MaybeIncorrect + ); + } +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2688947552253..e1280ef38ef71 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -561,6 +561,11 @@ define_Conf! { /// Which crates to allow absolute paths from (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet = rustc_data_structures::fx::FxHashSet::default()), + /// Lint: PATH_ENDS_WITH_EXT. + /// + /// Additional dotfiles (files or directories starting with a dot) to allow + (allowed_dotfiles: rustc_data_structures::fx::FxHashSet = + rustc_data_structures::fx::FxHashSet::default()), /// Lint: EXPLICIT_ITER_LOOP /// /// Whether to recommend using implicit into iter for reborrowed values. diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index b97bb144468b0..4bed5c149f517 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -10,6 +10,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-print-in-tests allow-private-module-inception allow-unwrap-in-tests + allowed-dotfiles allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed @@ -82,6 +83,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-print-in-tests allow-private-module-inception allow-unwrap-in-tests + allowed-dotfiles allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed diff --git a/tests/ui/path_ends_with_ext.fixed b/tests/ui/path_ends_with_ext.fixed new file mode 100644 index 0000000000000..49767e242cee5 --- /dev/null +++ b/tests/ui/path_ends_with_ext.fixed @@ -0,0 +1,36 @@ +#![warn(clippy::path_ends_with_ext)] +use std::path::Path; + +macro_rules! arg { + () => { + ".md" + }; +} + +fn test(path: &Path) { + path.extension().is_some_and(|ext| ext == "md"); + //~^ ERROR: this looks like a failed attempt at checking for the file extension + + // some "extensions" are allowed by default + path.ends_with(".git"); + + // most legitimate "dotfiles" are longer than 3 chars, so we allow them as well + path.ends_with(".bashrc"); + + // argument from expn shouldn't trigger + path.ends_with(arg!()); + + path.ends_with(".."); + path.ends_with("./a"); + path.ends_with("."); + path.ends_with(""); +} + +// is_some_and was stabilized in 1.70, so suggest map_or(false, ..) if under that +#[clippy::msrv = "1.69"] +fn under_msv(path: &Path) -> bool { + path.extension().map_or(false, |ext| ext == "md") + //~^ ERROR: this looks like a failed attempt at checking for the file extension +} + +fn main() {} diff --git a/tests/ui/path_ends_with_ext.rs b/tests/ui/path_ends_with_ext.rs new file mode 100644 index 0000000000000..2dfd046218afa --- /dev/null +++ b/tests/ui/path_ends_with_ext.rs @@ -0,0 +1,36 @@ +#![warn(clippy::path_ends_with_ext)] +use std::path::Path; + +macro_rules! arg { + () => { + ".md" + }; +} + +fn test(path: &Path) { + path.ends_with(".md"); + //~^ ERROR: this looks like a failed attempt at checking for the file extension + + // some "extensions" are allowed by default + path.ends_with(".git"); + + // most legitimate "dotfiles" are longer than 3 chars, so we allow them as well + path.ends_with(".bashrc"); + + // argument from expn shouldn't trigger + path.ends_with(arg!()); + + path.ends_with(".."); + path.ends_with("./a"); + path.ends_with("."); + path.ends_with(""); +} + +// is_some_and was stabilized in 1.70, so suggest map_or(false, ..) if under that +#[clippy::msrv = "1.69"] +fn under_msv(path: &Path) -> bool { + path.ends_with(".md") + //~^ ERROR: this looks like a failed attempt at checking for the file extension +} + +fn main() {} diff --git a/tests/ui/path_ends_with_ext.stderr b/tests/ui/path_ends_with_ext.stderr new file mode 100644 index 0000000000000..a73ab4d08e9c5 --- /dev/null +++ b/tests/ui/path_ends_with_ext.stderr @@ -0,0 +1,17 @@ +error: this looks like a failed attempt at checking for the file extension + --> $DIR/path_ends_with_ext.rs:11:5 + | +LL | path.ends_with(".md"); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `path.extension().is_some_and(|ext| ext == "md")` + | + = note: `-D clippy::path-ends-with-ext` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::path_ends_with_ext)]` + +error: this looks like a failed attempt at checking for the file extension + --> $DIR/path_ends_with_ext.rs:32:5 + | +LL | path.ends_with(".md") + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `path.extension().map_or(false, |ext| ext == "md")` + +error: aborting due to 2 previous errors + From 7426c83d8f5d1688763642503b59d0f0f653a3f5 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Sat, 16 Sep 2023 00:05:57 +0800 Subject: [PATCH 33/78] add a test case --- tests/ui/let_unit.fixed | 2 ++ tests/ui/let_unit.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index 57374bd5fcdad..f98ce9d50a994 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -177,3 +177,5 @@ fn attributes() { async fn issue10433() { let _pending: () = std::future::pending().await; } + +pub async fn issue11502(a: ()) {} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index 09077c60d5019..6d942ca8908c4 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -177,3 +177,5 @@ fn attributes() { async fn issue10433() { let _pending: () = std::future::pending().await; } + +pub async fn issue11502(a: ()) {} From fed036a57c96d91ac632f0bd40a27ef78b5b96c6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 15 Sep 2023 07:06:50 -0400 Subject: [PATCH 34/78] Fix cycle detection in `needless_borrow` --- clippy_lints/src/dereference.rs | 3 --- clippy_utils/src/mir/mod.rs | 40 ++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 8090f821d1ff2..d0a898adff44d 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -12,7 +12,6 @@ use hir::def::DefKind; use hir::MatchSource; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -1172,8 +1171,6 @@ fn referent_used_exactly_once<'tcx>( && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind && !place.is_indirect_first_projection() - // Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710) - && TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none() { let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); if possible_borrowers diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 131f3c0aa3946..f04467dc19d37 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -1,7 +1,8 @@ use rustc_hir::{Expr, HirId}; +use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ - traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, + traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, }; use rustc_middle::ty::TyCtxt; @@ -79,8 +80,32 @@ impl<'a, 'tcx> Visitor<'tcx> for V<'a> { } } +/// Checks if the block is part of a cycle +pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { + let mut seen = BitSet::new_empty(body.basic_blocks.len()); + let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2); + + seen.insert(block); + let mut next = block; + loop { + for succ in body.basic_blocks[next].terminator().successors() { + if seen.insert(succ) { + to_visit.push(succ); + } else if succ == block { + return true; + } + } + + if let Some(x) = to_visit.pop() { + next = x; + } else { + return false; + } + } +} + /// Convenience wrapper around `visit_local_usage`. -pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option { +pub fn used_exactly_once(mir: &Body<'_>, local: rustc_middle::mir::Local) -> Option { visit_local_usage( &[local], mir, @@ -91,11 +116,14 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle: ) .map(|mut vec| { let LocalUsage { local_use_locs, .. } = vec.remove(0); - local_use_locs + let mut locations = local_use_locs .into_iter() - .filter(|location| !is_local_assignment(mir, local, *location)) - .count() - == 1 + .filter(|&location| !is_local_assignment(mir, local, location)); + if let Some(location) = locations.next() { + locations.next().is_none() && !block_in_cycle(mir, location.block) + } else { + false + } }) } From 860e800fa08734ff9d15a67115f8220e5fa2202e Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 16 Sep 2023 14:14:51 +0200 Subject: [PATCH 35/78] [`filter_map_bool_then`]: peel as many refs as needed --- clippy_lints/src/methods/filter_map_bool_then.rs | 15 +++++++++++---- tests/ui/filter_map_bool_then.fixed | 8 ++++++++ tests/ui/filter_map_bool_then.rs | 8 ++++++++ tests/ui/filter_map_bool_then.stderr | 14 +++++++++++++- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index ac5bff6fee0aa..dd4aa2766630e 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -2,13 +2,13 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::BOOL_THEN; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_copy; +use clippy_utils::ty::{is_copy, peel_mid_ty_refs_is_mutable}; use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder}; +use rustc_middle::ty::Binder; use rustc_span::{sym, Span}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { @@ -36,7 +36,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) && match_def_path(cx, def_id, &BOOL_THEN) && !is_from_proc_macro(cx, expr) - && let ref_bool = matches!(cx.typeck_results().expr_ty(recv).kind(), ty::Ref(..)) + // Peel all refs (e.g. `&&&&mut &&&bool` -> `bool`) and get its count so we can suggest the exact + // amount of derefs to get to the bool in the filter. + // `peel_mid_ty_refs` alone doesn't handle mutable reference, so we use `_is_mutable` + // instead which counts them too and just ignore the resulting mutability + && let (_, needed_derefs, _) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) && let Some(param_snippet) = snippet_opt(cx, param.span) && let Some(filter) = snippet_opt(cx, recv.span) && let Some(map) = snippet_opt(cx, then_body.span) @@ -47,7 +51,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & call_span, "usage of `bool::then` in `filter_map`", "use `filter` then `map` instead", - format!("filter(|&{param_snippet}| {}{filter}).map(|{param_snippet}| {map})", if ref_bool { "*" } else { "" }), + format!( + "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", + derefs="*".repeat(needed_derefs) + ), Applicability::MachineApplicable, ); } diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index 67b984ce0254d..d1c946585f577 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -59,4 +59,12 @@ fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str fn issue11503() { let bools: &[bool] = &[true, false, false, true]; let _: Vec = bools.iter().enumerate().filter(|&(i, b)| *b).map(|(i, b)| i).collect(); + + // Need to insert multiple derefs if there is more than one layer of references + let bools: &[&&bool] = &[&&true, &&false, &&false, &&true]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| ***b).map(|(i, b)| i).collect(); + + // Should also suggest derefs when going through a mutable reference + let bools: &[&mut bool] = &[&mut true]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| **b).map(|(i, b)| i).collect(); } diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index b716fd70fa062..2d0ed282013d0 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -59,4 +59,12 @@ fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str fn issue11503() { let bools: &[bool] = &[true, false, false, true]; let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + + // Need to insert multiple derefs if there is more than one layer of references + let bools: &[&&bool] = &[&&true, &&false, &&false, &&true]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + + // Should also suggest derefs when going through a mutable reference + let bools: &[&mut bool] = &[&mut true]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); } diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index 1fee93afb36a0..174786ff32ef6 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -43,5 +43,17 @@ error: usage of `bool::then` in `filter_map` LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| *b).map(|(i, b)| i)` -error: aborting due to 7 previous errors +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:65:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ***b).map(|(i, b)| i)` + +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:69:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| **b).map(|(i, b)| i)` + +error: aborting due to 9 previous errors From 2ec6f3b1ed22c3fa0b20eda5cf335c54f81b4462 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 16 Sep 2023 15:13:44 +0200 Subject: [PATCH 36/78] also count derefs through custom `Deref` impls --- clippy_lints/src/methods/filter_map_bool_then.rs | 13 +++++++------ tests/ui/filter_map_bool_then.fixed | 11 +++++++++++ tests/ui/filter_map_bool_then.rs | 11 +++++++++++ tests/ui/filter_map_bool_then.stderr | 8 +++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index dd4aa2766630e..33657254965c1 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -2,12 +2,13 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::BOOL_THEN; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{is_copy, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::is_copy; use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::Binder; use rustc_span::{sym, Span}; @@ -36,11 +37,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) && match_def_path(cx, def_id, &BOOL_THEN) && !is_from_proc_macro(cx, expr) - // Peel all refs (e.g. `&&&&mut &&&bool` -> `bool`) and get its count so we can suggest the exact - // amount of derefs to get to the bool in the filter. - // `peel_mid_ty_refs` alone doesn't handle mutable reference, so we use `_is_mutable` - // instead which counts them too and just ignore the resulting mutability - && let (_, needed_derefs, _) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) + // Count the number of derefs needed to get to the bool because we need those in the suggestion + && let needed_derefs = cx.typeck_results().expr_adjustments(recv) + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count() && let Some(param_snippet) = snippet_opt(cx, param.span) && let Some(filter) = snippet_opt(cx, recv.span) && let Some(map) = snippet_opt(cx, then_body.span) diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index d1c946585f577..6a1b81fdbcbe8 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -67,4 +67,15 @@ fn issue11503() { // Should also suggest derefs when going through a mutable reference let bools: &[&mut bool] = &[&mut true]; let _: Vec = bools.iter().enumerate().filter(|&(i, b)| **b).map(|(i, b)| i).collect(); + + // Should also suggest derefs when going through a custom deref + struct DerefToBool; + impl std::ops::Deref for DerefToBool { + type Target = bool; + fn deref(&self) -> &Self::Target { + &true + } + } + let bools: &[&&DerefToBool] = &[&&DerefToBool]; + let _: Vec = bools.iter().enumerate().filter(|&(i, b)| ****b).map(|(i, b)| i).collect(); } diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index 2d0ed282013d0..a41e88f8805de 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -67,4 +67,15 @@ fn issue11503() { // Should also suggest derefs when going through a mutable reference let bools: &[&mut bool] = &[&mut true]; let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + + // Should also suggest derefs when going through a custom deref + struct DerefToBool; + impl std::ops::Deref for DerefToBool { + type Target = bool; + fn deref(&self) -> &Self::Target { + &true + } + } + let bools: &[&&DerefToBool] = &[&&DerefToBool]; + let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); } diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index 174786ff32ef6..fab6987913a10 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -55,5 +55,11 @@ error: usage of `bool::then` in `filter_map` LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| **b).map(|(i, b)| i)` -error: aborting due to 9 previous errors +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:80:50 + | +LL | let _: Vec = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ****b).map(|(i, b)| i)` + +error: aborting due to 10 previous errors From 67f0ba4af8be42d361adc8d047121fe6c820c0d4 Mon Sep 17 00:00:00 2001 From: Chen Chen <0109chenchen@gmail.com> Date: Sat, 16 Sep 2023 22:12:40 +0800 Subject: [PATCH 37/78] Update clippy_lints/src/matches/redundant_guards.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix typo Co-authored-by: Alejandra González --- clippy_lints/src/matches/redundant_guards.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index d51d7edc8e353..d470c05f35110 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -123,7 +123,7 @@ fn get_pat_binding<'tcx>( if matches!(bind_annot.0, rustc_ast::ByRef::Yes) { let _ = byref_ident.insert(ident); } - // the second call of `replce()` returns a `Some(span)`, meaning a multi-binding pattern + // the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern if span.replace(pat.span).is_some() { multiple_bindings = true; return false; From af2a8478ba3228ede92ff799b17ac2408fc6347f Mon Sep 17 00:00:00 2001 From: mojave2 Date: Sat, 16 Sep 2023 23:17:47 +0800 Subject: [PATCH 38/78] fix cast_lossless with macro call --- clippy_lints/src/casts/cast_lossless.rs | 9 ++++++++- tests/ui/cast_lossless_integer.fixed | 11 +++++++++++ tests/ui/cast_lossless_integer.rs | 11 +++++++++++ tests/ui/cast_lossless_integer.stderr | 14 +++++++++++++- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index cf07e050ccce9..2f3ffea669344 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -25,7 +25,14 @@ pub(super) fn check( // The suggestion is to use a function call, so if the original expression // has parens on the outside, they are no longer needed. let mut applicability = Applicability::MachineApplicable; - let opt = snippet_opt(cx, cast_op.span); + let opt = snippet_opt( + cx, + if cast_op.span.from_expansion() { + cast_op.span.source_callsite() + } else { + cast_op.span + }, + ); let sugg = opt.as_ref().map_or_else( || { applicability = Applicability::HasPlaceholders; diff --git a/tests/ui/cast_lossless_integer.fixed b/tests/ui/cast_lossless_integer.fixed index 6547107f50060..5e7e545e764a5 100644 --- a/tests/ui/cast_lossless_integer.fixed +++ b/tests/ui/cast_lossless_integer.fixed @@ -49,3 +49,14 @@ mod cast_lossless_in_impl { enum Test { A = u32::MAX as i64 + 1, } + +fn issue11458() { + macro_rules! sign_cast { + ($var: ident, $src: ty, $dest: ty) => { + <$dest>::from_ne_bytes(($var as $src).to_ne_bytes()) + }; + } + let x = 10_u128; + let _ = i32::from(sign_cast!(x, u8, i8)); + let _ = i32::from(sign_cast!(x, u8, i8) + 1); +} diff --git a/tests/ui/cast_lossless_integer.rs b/tests/ui/cast_lossless_integer.rs index 79af9a83ca286..0d69ddbd586a5 100644 --- a/tests/ui/cast_lossless_integer.rs +++ b/tests/ui/cast_lossless_integer.rs @@ -49,3 +49,14 @@ mod cast_lossless_in_impl { enum Test { A = u32::MAX as i64 + 1, } + +fn issue11458() { + macro_rules! sign_cast { + ($var: ident, $src: ty, $dest: ty) => { + <$dest>::from_ne_bytes(($var as $src).to_ne_bytes()) + }; + } + let x = 10_u128; + let _ = sign_cast!(x, u8, i8) as i32; + let _ = (sign_cast!(x, u8, i8) + 1) as i32; +} diff --git a/tests/ui/cast_lossless_integer.stderr b/tests/ui/cast_lossless_integer.stderr index da75cb195ebae..f9f111a7c20f1 100644 --- a/tests/ui/cast_lossless_integer.stderr +++ b/tests/ui/cast_lossless_integer.stderr @@ -115,5 +115,17 @@ error: casting `u8` to `u16` may become silently lossy if you later change the t LL | let _ = (1u8 + 1u8) as u16; | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` -error: aborting due to 19 previous errors +error: casting `i8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:60:13 + | +LL | let _ = sign_cast!(x, u8, i8) as i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8))` + +error: casting `i8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:61:13 + | +LL | let _ = (sign_cast!(x, u8, i8) + 1) as i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8) + 1)` + +error: aborting due to 21 previous errors From 3cad6237166dc71e2b75a9e60d5058f2876e1959 Mon Sep 17 00:00:00 2001 From: Chen Chen <0109chenchen@gmail.com> Date: Sun, 17 Sep 2023 02:42:12 +0800 Subject: [PATCH 39/78] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20cast=5Flossless.rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Timo <30553356+y21@users.noreply.github.com> --- clippy_lints/src/casts/cast_lossless.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 2f3ffea669344..c586b572be9a1 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -25,14 +25,7 @@ pub(super) fn check( // The suggestion is to use a function call, so if the original expression // has parens on the outside, they are no longer needed. let mut applicability = Applicability::MachineApplicable; - let opt = snippet_opt( - cx, - if cast_op.span.from_expansion() { - cast_op.span.source_callsite() - } else { - cast_op.span - }, - ); + let opt = snippet_opt(cx, cast_op.span.source_callsite()); let sugg = opt.as_ref().map_or_else( || { applicability = Applicability::HasPlaceholders; From 5b790ff9e0effa9f742ce8a7b896a35c767fb131 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Sun, 17 Sep 2023 12:54:00 +0800 Subject: [PATCH 40/78] fix FP with needless_raw_string_hashes --- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/raw_strings.rs | 5 +++-- tests/ui/needless_raw_string_hashes.fixed | 3 +++ tests/ui/needless_raw_string_hashes.rs | 3 +++ tests/ui/needless_raw_string_hashes.stderr | 26 +++++++++++++++++++++- 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index a88f2b51c827e..0546807bac4fb 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -616,7 +616,7 @@ fn check_should_panic_reason(cx: &LateContext<'_>, attr: &Attribute) { attr.span, "#[should_panic] attribute without a reason", "consider specifying the expected panic", - r#"#[should_panic(expected = /* panic message */)]"#.into(), + "#[should_panic(expected = /* panic message */)]".into(), Applicability::HasPlaceholders, ); } diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index a4fe501f79cdc..3db9f7541e588 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -96,8 +96,9 @@ impl EarlyLintPass for RawStrings { ); }, ); - - return; + if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { + return; + } } let req = { diff --git a/tests/ui/needless_raw_string_hashes.fixed b/tests/ui/needless_raw_string_hashes.fixed index e980adeeff4c1..c99c2f46532a3 100644 --- a/tests/ui/needless_raw_string_hashes.fixed +++ b/tests/ui/needless_raw_string_hashes.fixed @@ -21,4 +21,7 @@ fn main() { multiline string "; + + r"rust"; + r"hello world"; } diff --git a/tests/ui/needless_raw_string_hashes.rs b/tests/ui/needless_raw_string_hashes.rs index 6113c5f25ae64..dcc2af69f4e9c 100644 --- a/tests/ui/needless_raw_string_hashes.rs +++ b/tests/ui/needless_raw_string_hashes.rs @@ -21,4 +21,7 @@ fn main() { multiline string "#; + + r###"rust"###; + r#"hello world"#; } diff --git a/tests/ui/needless_raw_string_hashes.stderr b/tests/ui/needless_raw_string_hashes.stderr index 5a8e3d04543af..4399c6555c2c0 100644 --- a/tests/ui/needless_raw_string_hashes.stderr +++ b/tests/ui/needless_raw_string_hashes.stderr @@ -163,5 +163,29 @@ LL | string LL ~ "; | -error: aborting due to 13 previous errors +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:25:5 + | +LL | r###"rust"###; + | ^^^^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - r###"rust"###; +LL + r"rust"; + | + +error: unnecessary hashes around raw string literal + --> $DIR/needless_raw_string_hashes.rs:26:5 + | +LL | r#"hello world"#; + | ^^^^^^^^^^^^^^^^ + | +help: remove all the hashes around the literal + | +LL - r#"hello world"#; +LL + r"hello world"; + | + +error: aborting due to 15 previous errors From 3665a4102bce2cd6b09a7aea1b405cc640e076d8 Mon Sep 17 00:00:00 2001 From: mojave2 Date: Sun, 17 Sep 2023 19:44:25 +0800 Subject: [PATCH 41/78] fix ICE by `u64::try_from()` --- clippy_lints/src/casts/cast_possible_truncation.rs | 2 +- tests/ui/cast.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 84b99ad5c243d..f99a51e2b88c5 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -44,7 +44,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b .unwrap_or(u64::max_value()) .min(apply_reductions(cx, nbits, left, signed)), BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) - .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))), + .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())), _ => nbits, }, ExprKind::MethodCall(method, left, [right], _) => { diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index d0a092093f3e0..1ca18170f8a0e 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -361,3 +361,7 @@ fn avoid_subtract_overflow(q: u32) { //~^ ERROR: casting `u32` to `u8` may truncate the value c as usize; } + +fn issue11426() { + (&42u8 >> 0xa9008fb6c9d81e42_0e25730562a601c8_u128) as usize; +} From 558ae4c6a8a5a81c5b0cb3c7828e622201ba33ee Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 17 Sep 2023 15:34:32 +0200 Subject: [PATCH 42/78] [`redundant_guards`]: lint if the pattern is on the LHS --- clippy_lints/src/matches/redundant_guards.rs | 9 +++- tests/ui/redundant_guards.fixed | 9 ++++ tests/ui/redundant_guards.rs | 9 ++++ tests/ui/redundant_guards.stderr | 56 ++++++++++++++++---- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index d470c05f35110..0efeeacc9d97c 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -70,10 +70,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { ); } // `Some(x) if x == Some(2)` + // `Some(x) if Some(2) == x` else if let Guard::If(if_expr) = guard && let ExprKind::Binary(bin_op, local, pat) = if_expr.kind && matches!(bin_op.node, BinOpKind::Eq) - && expr_can_be_pat(cx, pat) // Ensure they have the same type. If they don't, we'd need deref coercion which isn't // possible (currently) in a pattern. In some cases, you can use something like // `as_deref` or similar but in general, we shouldn't lint this as it'd create an @@ -81,7 +81,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { // // This isn't necessary in the other two checks, as they must be a pattern already. && cx.typeck_results().expr_ty(local) == cx.typeck_results().expr_ty(pat) - && let Some(binding) = get_pat_binding(cx, local, outer_arm) + // Since we want to lint on both `x == Some(2)` and `Some(2) == x`, we might have to "swap" + // `local` and `pat`, depending on which side they are. + && let Some((binding, pat)) = get_pat_binding(cx, local, outer_arm) + .map(|binding| (binding, pat)) + .or_else(|| get_pat_binding(cx, pat, outer_arm).map(|binding| (binding, local))) + && expr_can_be_pat(cx, pat) { let pat_span = match (pat.kind, binding.byref_ident) { (ExprKind::AddrOf(BorrowKind::Ref, _, expr), Some(_)) => expr.span, diff --git a/tests/ui/redundant_guards.fixed b/tests/ui/redundant_guards.fixed index 20fcc1254a66d..f23116a7e1c5c 100644 --- a/tests/ui/redundant_guards.fixed +++ b/tests/ui/redundant_guards.fixed @@ -43,6 +43,7 @@ fn main() { }, Some(Some(1)) => .., Some(Some(2)) => .., + Some(Some(2)) => .., // Don't lint, since x is used in the body Some(x) if let Some(1) = x => { x; @@ -56,11 +57,13 @@ fn main() { Some(x) if matches!(y, 1 if true) => .., Some(x) if let 1 = y => .., Some(x) if y == 2 => .., + Some(x) if 2 == y => .., _ => todo!(), }; let a = A(1); match a { _ if a.0 == 1 => {}, + _ if 1 == a.0 => {}, _ => todo!(), } let b = B { e: Some(A(0)) }; @@ -119,6 +122,7 @@ fn h(v: Option) { fn f(s: Option) { match s { Some(x) if x == "a" => {}, + Some(x) if "a" == x => {}, _ => {}, } } @@ -140,6 +144,7 @@ static CONST_S: S = S { a: 1 }; fn g(opt_s: Option) { match opt_s { Some(x) if x == CONST_S => {}, + Some(x) if CONST_S == x => {}, _ => {}, } } @@ -157,6 +162,7 @@ mod issue11465 { fn issue11465() { let c = Some(1); match c { + Some(1) => {}, Some(1) => {}, Some(2) => {}, Some(3) => {}, @@ -166,6 +172,7 @@ mod issue11465 { let enum_a = A::Foo([98, 97, 114]); match enum_a { A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if b"foo" == arr => {}, A::Foo(ref arr) if let b"bar" = arr => {}, A::Foo(ref arr) if matches!(arr, b"baz") => {}, _ => {}, @@ -177,6 +184,8 @@ mod issue11465 { }; match struct_b { B { ref b, .. } if b == "bar" => {}, + B { ref b, .. } if "bar" == b => {}, + B { c: 1, .. } => {}, B { c: 1, .. } => {}, B { c: 1, .. } => {}, B { c: 1, .. } => {}, diff --git a/tests/ui/redundant_guards.rs b/tests/ui/redundant_guards.rs index e63cd29e8afe9..c0206b4cec75f 100644 --- a/tests/ui/redundant_guards.rs +++ b/tests/ui/redundant_guards.rs @@ -43,6 +43,7 @@ fn main() { }, Some(x) if let Some(1) = x => .., Some(x) if x == Some(2) => .., + Some(x) if Some(2) == x => .., // Don't lint, since x is used in the body Some(x) if let Some(1) = x => { x; @@ -56,11 +57,13 @@ fn main() { Some(x) if matches!(y, 1 if true) => .., Some(x) if let 1 = y => .., Some(x) if y == 2 => .., + Some(x) if 2 == y => .., _ => todo!(), }; let a = A(1); match a { _ if a.0 == 1 => {}, + _ if 1 == a.0 => {}, _ => todo!(), } let b = B { e: Some(A(0)) }; @@ -119,6 +122,7 @@ fn h(v: Option) { fn f(s: Option) { match s { Some(x) if x == "a" => {}, + Some(x) if "a" == x => {}, _ => {}, } } @@ -140,6 +144,7 @@ static CONST_S: S = S { a: 1 }; fn g(opt_s: Option) { match opt_s { Some(x) if x == CONST_S => {}, + Some(x) if CONST_S == x => {}, _ => {}, } } @@ -158,6 +163,7 @@ mod issue11465 { let c = Some(1); match c { Some(ref x) if x == &1 => {}, + Some(ref x) if &1 == x => {}, Some(ref x) if let &2 = x => {}, Some(ref x) if matches!(x, &3) => {}, _ => {}, @@ -166,6 +172,7 @@ mod issue11465 { let enum_a = A::Foo([98, 97, 114]); match enum_a { A::Foo(ref arr) if arr == b"foo" => {}, + A::Foo(ref arr) if b"foo" == arr => {}, A::Foo(ref arr) if let b"bar" = arr => {}, A::Foo(ref arr) if matches!(arr, b"baz") => {}, _ => {}, @@ -177,7 +184,9 @@ mod issue11465 { }; match struct_b { B { ref b, .. } if b == "bar" => {}, + B { ref b, .. } if "bar" == b => {}, B { ref c, .. } if c == &1 => {}, + B { ref c, .. } if &1 == c => {}, B { ref c, .. } if let &1 = c => {}, B { ref c, .. } if matches!(c, &1) => {}, _ => {}, diff --git a/tests/ui/redundant_guards.stderr b/tests/ui/redundant_guards.stderr index f208e556f2e90..b8d7834e358ca 100644 --- a/tests/ui/redundant_guards.stderr +++ b/tests/ui/redundant_guards.stderr @@ -60,7 +60,19 @@ LL + Some(Some(2)) => .., | error: redundant guard - --> $DIR/redundant_guards.rs:68:20 + --> $DIR/redundant_guards.rs:46:20 + | +LL | Some(x) if Some(2) == x => .., + | ^^^^^^^^^^^^ + | +help: try + | +LL - Some(x) if Some(2) == x => .., +LL + Some(Some(2)) => .., + | + +error: redundant guard + --> $DIR/redundant_guards.rs:71:20 | LL | B { e } if matches!(e, Some(A(2))) => .., | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +84,7 @@ LL + B { e: Some(A(2)) } => .., | error: redundant guard - --> $DIR/redundant_guards.rs:105:20 + --> $DIR/redundant_guards.rs:108:20 | LL | E::A(y) if y == "not from an or pattern" => {}, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +96,7 @@ LL + E::A("not from an or pattern") => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:112:14 + --> $DIR/redundant_guards.rs:115:14 | LL | x if matches!(x, Some(0)) => .., | ^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +108,7 @@ LL + Some(0) => .., | error: redundant guard - --> $DIR/redundant_guards.rs:160:28 + --> $DIR/redundant_guards.rs:165:28 | LL | Some(ref x) if x == &1 => {}, | ^^^^^^^ @@ -108,7 +120,19 @@ LL + Some(1) => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:161:28 + --> $DIR/redundant_guards.rs:166:28 + | +LL | Some(ref x) if &1 == x => {}, + | ^^^^^^^ + | +help: try + | +LL - Some(ref x) if &1 == x => {}, +LL + Some(1) => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:167:28 | LL | Some(ref x) if let &2 = x => {}, | ^^^^^^^^^^ @@ -120,7 +144,7 @@ LL + Some(2) => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:162:28 + --> $DIR/redundant_guards.rs:168:28 | LL | Some(ref x) if matches!(x, &3) => {}, | ^^^^^^^^^^^^^^^ @@ -132,7 +156,7 @@ LL + Some(3) => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:180:32 + --> $DIR/redundant_guards.rs:188:32 | LL | B { ref c, .. } if c == &1 => {}, | ^^^^^^^ @@ -144,7 +168,19 @@ LL + B { c: 1, .. } => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:181:32 + --> $DIR/redundant_guards.rs:189:32 + | +LL | B { ref c, .. } if &1 == c => {}, + | ^^^^^^^ + | +help: try + | +LL - B { ref c, .. } if &1 == c => {}, +LL + B { c: 1, .. } => {}, + | + +error: redundant guard + --> $DIR/redundant_guards.rs:190:32 | LL | B { ref c, .. } if let &1 = c => {}, | ^^^^^^^^^^ @@ -156,7 +192,7 @@ LL + B { c: 1, .. } => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:182:32 + --> $DIR/redundant_guards.rs:191:32 | LL | B { ref c, .. } if matches!(c, &1) => {}, | ^^^^^^^^^^^^^^^ @@ -167,5 +203,5 @@ LL - B { ref c, .. } if matches!(c, &1) => {}, LL + B { c: 1, .. } => {}, | -error: aborting due to 14 previous errors +error: aborting due to 17 previous errors From 79247d95f7ec00019cc26db20a07d4f1c1f335aa Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 15 Sep 2023 07:07:34 -0400 Subject: [PATCH 43/78] Split part of `needless_borrow` into `needless_borrows_for_generic_args` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/dereference.rs | 371 +--------------- clippy_lints/src/lib.rs | 8 +- .../src/needless_borrows_for_generic_args.rs | 410 ++++++++++++++++++ tests/ui/needless_borrow.fixed | 290 ------------- tests/ui/needless_borrow.rs | 290 ------------- tests/ui/needless_borrow.stderr | 90 +--- .../needless_borrows_for_generic_args.fixed | 287 ++++++++++++ tests/ui/needless_borrows_for_generic_args.rs | 287 ++++++++++++ .../needless_borrows_for_generic_args.stderr | 77 ++++ tests/ui/regex.rs | 3 +- tests/ui/regex.stderr | 48 +- tests/ui/unnecessary_to_owned.fixed | 2 +- tests/ui/unnecessary_to_owned.rs | 2 +- 15 files changed, 1106 insertions(+), 1061 deletions(-) create mode 100644 clippy_lints/src/needless_borrows_for_generic_args.rs create mode 100644 tests/ui/needless_borrows_for_generic_args.fixed create mode 100644 tests/ui/needless_borrows_for_generic_args.rs create mode 100644 tests/ui/needless_borrows_for_generic_args.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aca13a6cf642..3717b15ac5c2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5171,6 +5171,7 @@ Released 2018-09-13 [`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference +[`needless_borrows_for_generic_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrows_for_generic_args [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index ecf44e05920ae..c4d5bee389ed8 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -479,6 +479,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_bool::NEEDLESS_BOOL_INFO, crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO, crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, + crate::needless_borrows_for_generic_args::NEEDLESS_BORROWS_FOR_GENERIC_ARGS_INFO, crate::needless_continue::NEEDLESS_CONTINUE_INFO, crate::needless_else::NEEDLESS_ELSE_INFO, crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index d0a898adff44d..aa80d60a4b1a1 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,40 +1,24 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; -use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, }; - -use hir::def::DefKind; -use hir::MatchSource; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; -use rustc_hir::def::Res; -use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, - Path, QPath, TyKind, UnOp, + self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, + Pat, PatKind, Path, QPath, TyKind, UnOp, }; -use rustc_index::bit_set::BitSet; -use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{ - self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamEnv, ParamTy, ProjectionPredicate, Ty, - TyCtxt, TypeVisitableExt, TypeckResults, -}; +use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{Obligation, ObligationCause}; -use std::collections::VecDeque; declare_clippy_lint! { /// ### What it does @@ -182,24 +166,6 @@ pub struct Dereferencing<'tcx> { /// /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap>, - - /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by - /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead - /// be moved. - possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - - // `IntoIterator` for arrays requires Rust 1.53. - msrv: Msrv, -} - -impl<'tcx> Dereferencing<'tcx> { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { - msrv, - ..Dereferencing::default() - } - } } #[derive(Debug)] @@ -354,52 +320,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { )); }, (Some(use_cx), RefOp::AddrOf(mutability)) => { - let defined_ty = use_cx.node.defined_ty(cx); - - // Check needless_borrow for generic arguments. - if !use_cx.is_ty_unified - && let Some(DefinedTy::Mir(ty)) = defined_ty - && let ty::Param(ty) = *ty.value.skip_binder().kind() - && let Some((hir_id, fn_id, i)) = match use_cx.node { - ExprUseNode::MethodArg(_, _, 0) => None, - ExprUseNode::MethodArg(hir_id, None, i) => { - typeck.type_dependent_def_id(hir_id).map(|id| (hir_id, id, i)) - }, - ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i) - if !path_has_args(p) => match typeck.qpath_res(p, hir_id) { - Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => { - Some((hir_id, id, i)) - }, - _ => None, - }, - _ => None, - } && let count = needless_borrow_generic_arg_count( - cx, - &mut self.possible_borrowers, - fn_id, - typeck.node_args(hir_id), - i, - ty, - expr, - &self.msrv, - ) && count != 0 - { - self.state = Some(( - State::DerefedBorrow(DerefedBorrow { - count: count - 1, - msg: "the borrowed expression implements the required traits", - stability: TyCoercionStability::None, - for_field_access: None, - }), - StateData { - span: expr.span, - hir_id: expr.hir_id, - adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), - }, - )); - return; - } - // Find the number of times the borrow is auto-derefed. let mut iter = use_cx.adjustments.iter(); let mut deref_count = 0usize; @@ -418,7 +338,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { }; }; - let stability = defined_ty.map_or(TyCoercionStability::None, |ty| { + let stability = use_cx.node.defined_ty(cx).map_or(TyCoercionStability::None, |ty| { TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return()) }); let can_auto_borrow = match use_cx.node { @@ -699,12 +619,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { - if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { - local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) - }) { - self.possible_borrowers.pop(); - } - if Some(body.id()) == self.current_body { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; @@ -728,8 +642,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { self.current_body = None; } } - - extract_msrv_attr!(LateContext); } fn try_parse_ref_op<'tcx>( @@ -787,13 +699,6 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } } -fn path_has_args(p: &QPath<'_>) -> bool { - match *p { - QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(), - _ => false, - } -} - fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { if let Some(parent) = get_parent_expr(cx, e) && parent.span.ctxt() == e.span.ctxt() @@ -980,272 +885,6 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { v.0 } -/// Checks for the number of borrow expressions which can be removed from the given expression -/// where the expression is used as an argument to a function expecting a generic type. -/// -/// The following constraints will be checked: -/// * The borrowed expression meets all the generic type's constraints. -/// * The generic type appears only once in the functions signature. -/// * The borrowed value will not be moved if it is used later in the function. -#[expect(clippy::too_many_arguments)] -fn needless_borrow_generic_arg_count<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - fn_id: DefId, - callee_args: &'tcx List>, - arg_index: usize, - param_ty: ParamTy, - mut expr: &Expr<'tcx>, - msrv: &Msrv, -) -> usize { - let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); - let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); - - let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder(); - let predicates = cx.tcx.param_env(fn_id).caller_bounds(); - let projection_predicates = predicates - .iter() - .filter_map(|predicate| { - if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() { - Some(projection_predicate) - } else { - None - } - }) - .collect::>(); - - let mut trait_with_ref_mut_self_method = false; - - // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return. - if predicates - .iter() - .filter_map(|predicate| { - if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) - { - Some(trait_predicate.trait_ref.def_id) - } else { - None - } - }) - .inspect(|trait_def_id| { - trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id); - }) - .all(|trait_def_id| { - Some(trait_def_id) == destruct_trait_def_id - || Some(trait_def_id) == sized_trait_def_id - || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) - }) - { - return 0; - } - - // See: - // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 - // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 - if projection_predicates - .iter() - .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate)) - { - return 0; - } - - // `args_with_referent_ty` can be constructed outside of `check_referent` because the same - // elements are modified each time `check_referent` is called. - let mut args_with_referent_ty = callee_args.to_vec(); - - let mut check_reference_and_referent = |reference, referent| { - let referent_ty = cx.typeck_results().expr_ty(referent); - - if !is_copy(cx, referent_ty) - && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) - || !referent_used_exactly_once(cx, possible_borrowers, reference)) - { - return false; - } - - // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 - if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) { - return false; - } - - if !replace_types( - cx, - param_ty, - referent_ty, - fn_sig, - arg_index, - &projection_predicates, - &mut args_with_referent_ty, - ) { - return false; - } - - predicates.iter().all(|predicate| { - if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) - && let ty::Param(param_ty) = trait_predicate.self_ty().kind() - && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack() - && ty.is_array() - && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) - { - return false; - } - - let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); - let infcx = cx.tcx.infer_ctxt().build(); - infcx.predicate_must_hold_modulo_regions(&obligation) - }) - }; - - let mut count = 0; - while let ExprKind::AddrOf(_, _, referent) = expr.kind { - if !check_reference_and_referent(expr, referent) { - break; - } - expr = referent; - count += 1; - } - count -} - -fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { - cx.tcx - .associated_items(trait_def_id) - .in_definition_order() - .any(|assoc_item| { - if assoc_item.fn_has_self_parameter { - let self_ty = cx - .tcx - .fn_sig(assoc_item.def_id) - .instantiate_identity() - .skip_binder() - .inputs()[0]; - matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) - } else { - false - } - }) -} - -fn is_mixed_projection_predicate<'tcx>( - cx: &LateContext<'tcx>, - callee_def_id: DefId, - projection_predicate: &ProjectionPredicate<'tcx>, -) -> bool { - let generics = cx.tcx.generics_of(callee_def_id); - // The predicate requires the projected type to equal a type parameter from the parent context. - if let Some(term_ty) = projection_predicate.term.ty() - && let ty::Param(term_param_ty) = term_ty.kind() - && (term_param_ty.index as usize) < generics.parent_count - { - // The inner-most self type is a type parameter from the current function. - let mut projection_ty = projection_predicate.projection_ty; - loop { - match projection_ty.self_ty().kind() { - ty::Alias(ty::Projection, inner_projection_ty) => { - projection_ty = *inner_projection_ty; - } - ty::Param(param_ty) => { - return (param_ty.index as usize) >= generics.parent_count; - } - _ => { - return false; - } - } - } - } else { - false - } -} - -fn referent_used_exactly_once<'tcx>( - cx: &LateContext<'tcx>, - possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, - reference: &Expr<'tcx>, -) -> bool { - if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) - && let Some(local) = expr_local(cx.tcx, reference) - && let [location] = *local_assignments(mir, local).as_slice() - && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) - && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind - && !place.is_indirect_first_projection() - { - let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); - if possible_borrowers - .last() - .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) - { - possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); - } - let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; - // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is - // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of - // itself. See the comment in that method for an explanation as to why. - possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) - && used_exactly_once(mir, place.local).unwrap_or(false) - } else { - false - } -} - -// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting -// projected type that is a type parameter. Returns `false` if replacing the types would have an -// effect on the function signature beyond substituting `new_ty` for `param_ty`. -// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 -fn replace_types<'tcx>( - cx: &LateContext<'tcx>, - param_ty: ParamTy, - new_ty: Ty<'tcx>, - fn_sig: FnSig<'tcx>, - arg_index: usize, - projection_predicates: &[ProjectionPredicate<'tcx>], - args: &mut [ty::GenericArg<'tcx>], -) -> bool { - let mut replaced = BitSet::new_empty(args.len()); - - let mut deque = VecDeque::with_capacity(args.len()); - deque.push_back((param_ty, new_ty)); - - while let Some((param_ty, new_ty)) = deque.pop_front() { - // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in. - if !fn_sig - .inputs_and_output - .iter() - .enumerate() - .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx))) - { - return false; - } - - args[param_ty.index as usize] = ty::GenericArg::from(new_ty); - - // The `replaced.insert(...)` check provides some protection against infinite loops. - if replaced.insert(param_ty.index) { - for projection_predicate in projection_predicates { - if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx) - && let Some(term_ty) = projection_predicate.term.ty() - && let ty::Param(term_param_ty) = term_ty.kind() - { - let projection = cx.tcx.mk_ty_from_kind(ty::Alias( - ty::Projection, - projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty), - )); - - if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) - && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) - { - deque.push_back((*term_param_ty, projected_ty)); - } - } - } - } - } - - true -} - fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool { if let ty::Adt(adt, _) = *ty.kind() { adt.is_struct() && adt.all_fields().any(|f| f.name == name) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3f6bdb2b62cc8..614ddcda88f17 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -228,6 +228,7 @@ mod mutex_atomic; mod needless_arbitrary_self_type; mod needless_bool; mod needless_borrowed_ref; +mod needless_borrows_for_generic_args; mod needless_continue; mod needless_else; mod needless_for_each; @@ -880,7 +881,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); - store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv()))); + store.register_late_pass(|_| Box::>::default()); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); let future_size_threshold = conf.future_size_threshold; @@ -1104,6 +1105,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)); store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)); store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)); + store.register_late_pass(move |_| { + Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( + msrv(), + )) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs new file mode 100644 index 0000000000000..d55c77a92b158 --- /dev/null +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -0,0 +1,410 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_copy; +use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath}; +use rustc_index::bit_set::BitSet; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::{Rvalue, StatementKind}; +use rustc_middle::ty::{ + self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, List, ParamTy, ProjectionPredicate, Ty, +}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::sym; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{Obligation, ObligationCause}; +use std::collections::VecDeque; + +declare_clippy_lint! { + /// ### What it does + /// Checks for borrow operations (`&`) that used as a generic argument to a + /// function when the borrowed value could be used. + /// + /// ### Why is this bad? + /// Suggests that the receiver of the expression borrows + /// the expression. + /// + /// ### Known problems + /// The lint cannot tell when the implementation of a trait + /// for `&T` and `T` do different things. Removing a borrow + /// in such a case can change the semantics of the code. + /// + /// ### Example + /// ```rust + /// fn f(_: impl AsRef) {} + /// + /// let x = "foo"; + /// f(&x); + /// ``` + /// + /// Use instead: + /// ```rust + /// fn f(_: impl AsRef) {} + /// + /// let x = "foo"; + /// f(x); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub NEEDLESS_BORROWS_FOR_GENERIC_ARGS, + style, + "taking a reference that is going to be automatically dereferenced" +} + +pub struct NeedlessBorrowsForGenericArgs<'tcx> { + /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by + /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead + /// be moved. + possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + + // `IntoIterator` for arrays requires Rust 1.53. + msrv: Msrv, +} +impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [NEEDLESS_BORROWS_FOR_GENERIC_ARGS]); + +impl NeedlessBorrowsForGenericArgs<'_> { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { + possible_borrowers: Vec::new(), + msrv, + } + } +} + +impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if matches!(expr.kind, ExprKind::AddrOf(..)) + && !expr.span.from_expansion() + && let Some(use_cx) = expr_use_ctxt(cx, expr) + && !use_cx.is_ty_unified + && let Some(DefinedTy::Mir(ty)) = use_cx.node.defined_ty(cx) + && let ty::Param(ty) = *ty.value.skip_binder().kind() + && let Some((hir_id, fn_id, i)) = match use_cx.node { + ExprUseNode::MethodArg(_, _, 0) => None, + ExprUseNode::MethodArg(hir_id, None, i) => { + cx.typeck_results().type_dependent_def_id(hir_id).map(|id| (hir_id, id, i)) + }, + ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i) + if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) { + Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => { + Some((hir_id, id, i)) + }, + _ => None, + }, + _ => None, + } && let count = needless_borrow_count( + cx, + &mut self.possible_borrowers, + fn_id, + cx.typeck_results().node_args(hir_id), + i, + ty, + expr, + &self.msrv, + ) && count != 0 + { + span_lint_and_then( + cx, + NEEDLESS_BORROWS_FOR_GENERIC_ARGS, + expr.span, + "the borrowed expression implements the required traits", + |diag| { + let mut app = Applicability::MachineApplicable; + let snip_span = peel_n_hir_expr_refs(expr, count).0.span; + let snip = snippet_with_context(cx, snip_span, expr.span.ctxt(), "..", &mut app).0; + diag.span_suggestion(expr.span, "change this to", snip.into_owned(), app); + } + ); + } + } + + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { + local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) + }) { + self.possible_borrowers.pop(); + } + } + + extract_msrv_attr!(LateContext); +} + +fn path_has_args(p: &QPath<'_>) -> bool { + match *p { + QPath::Resolved(_, Path { segments: [.., s], .. }) | QPath::TypeRelative(_, s) => s.args.is_some(), + _ => false, + } +} + +/// Checks for the number of borrow expressions which can be removed from the given expression +/// where the expression is used as an argument to a function expecting a generic type. +/// +/// The following constraints will be checked: +/// * The borrowed expression meets all the generic type's constraints. +/// * The generic type appears only once in the functions signature. +/// * The borrowed value will not be moved if it is used later in the function. +#[expect(clippy::too_many_arguments)] +fn needless_borrow_count<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + fn_id: DefId, + callee_args: &'tcx List>, + arg_index: usize, + param_ty: ParamTy, + mut expr: &Expr<'tcx>, + msrv: &Msrv, +) -> usize { + let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); + let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); + + let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder(); + let predicates = cx.tcx.param_env(fn_id).caller_bounds(); + let projection_predicates = predicates + .iter() + .filter_map(|predicate| { + if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() { + Some(projection_predicate) + } else { + None + } + }) + .collect::>(); + + let mut trait_with_ref_mut_self_method = false; + + // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return. + if predicates + .iter() + .filter_map(|predicate| { + if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) + { + Some(trait_predicate.trait_ref.def_id) + } else { + None + } + }) + .inspect(|trait_def_id| { + trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id); + }) + .all(|trait_def_id| { + Some(trait_def_id) == destruct_trait_def_id + || Some(trait_def_id) == sized_trait_def_id + || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) + }) + { + return 0; + } + + // See: + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 + if projection_predicates + .iter() + .any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate)) + { + return 0; + } + + // `args_with_referent_ty` can be constructed outside of `check_referent` because the same + // elements are modified each time `check_referent` is called. + let mut args_with_referent_ty = callee_args.to_vec(); + + let mut check_reference_and_referent = |reference, referent| { + let referent_ty = cx.typeck_results().expr_ty(referent); + + if !is_copy(cx, referent_ty) + && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) + || !referent_used_exactly_once(cx, possible_borrowers, reference)) + { + return false; + } + + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + return false; + } + + if !replace_types( + cx, + param_ty, + referent_ty, + fn_sig, + arg_index, + &projection_predicates, + &mut args_with_referent_ty, + ) { + return false; + } + + predicates.iter().all(|predicate| { + if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) + && let ty::Param(param_ty) = trait_predicate.self_ty().kind() + && let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack() + && ty.is_array() + && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) + { + return false; + } + + let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); + let infcx = cx.tcx.infer_ctxt().build(); + infcx.predicate_must_hold_modulo_regions(&obligation) + }) + }; + + let mut count = 0; + while let ExprKind::AddrOf(_, _, referent) = expr.kind { + if !check_reference_and_referent(expr, referent) { + break; + } + expr = referent; + count += 1; + } + count +} + +fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { + cx.tcx + .associated_items(trait_def_id) + .in_definition_order() + .any(|assoc_item| { + if assoc_item.fn_has_self_parameter { + let self_ty = cx + .tcx + .fn_sig(assoc_item.def_id) + .instantiate_identity() + .skip_binder() + .inputs()[0]; + matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) + } else { + false + } + }) +} + +fn is_mixed_projection_predicate<'tcx>( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + projection_predicate: &ProjectionPredicate<'tcx>, +) -> bool { + let generics = cx.tcx.generics_of(callee_def_id); + // The predicate requires the projected type to equal a type parameter from the parent context. + if let Some(term_ty) = projection_predicate.term.ty() + && let ty::Param(term_param_ty) = term_ty.kind() + && (term_param_ty.index as usize) < generics.parent_count + { + // The inner-most self type is a type parameter from the current function. + let mut projection_ty = projection_predicate.projection_ty; + loop { + match projection_ty.self_ty().kind() { + ty::Alias(ty::Projection, inner_projection_ty) => { + projection_ty = *inner_projection_ty; + } + ty::Param(param_ty) => { + return (param_ty.index as usize) >= generics.parent_count; + } + _ => { + return false; + } + } + } + } else { + false + } +} + +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id) + && let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let block_data = &mir.basic_blocks[location.block] + && let Some(statement) = block_data.statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind + && !place.is_indirect_first_projection() + { + let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); + if possible_borrowers + .last() + .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) + { + possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); + } + let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + && used_exactly_once(mir, place.local).unwrap_or(false) + } else { + false + } +} + +// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting +// projected type that is a type parameter. Returns `false` if replacing the types would have an +// effect on the function signature beyond substituting `new_ty` for `param_ty`. +// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 +fn replace_types<'tcx>( + cx: &LateContext<'tcx>, + param_ty: ParamTy, + new_ty: Ty<'tcx>, + fn_sig: FnSig<'tcx>, + arg_index: usize, + projection_predicates: &[ProjectionPredicate<'tcx>], + args: &mut [ty::GenericArg<'tcx>], +) -> bool { + let mut replaced = BitSet::new_empty(args.len()); + + let mut deque = VecDeque::with_capacity(args.len()); + deque.push_back((param_ty, new_ty)); + + while let Some((param_ty, new_ty)) = deque.pop_front() { + // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in. + if !fn_sig + .inputs_and_output + .iter() + .enumerate() + .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx))) + { + return false; + } + + args[param_ty.index as usize] = ty::GenericArg::from(new_ty); + + // The `replaced.insert(...)` check provides some protection against infinite loops. + if replaced.insert(param_ty.index) { + for projection_predicate in projection_predicates { + if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx) + && let Some(term_ty) = projection_predicate.term.ty() + && let ty::Param(term_param_ty) = term_ty.kind() + { + let projection = cx.tcx.mk_ty_from_kind(ty::Alias( + ty::Projection, + projection_predicate.projection_ty.with_self_ty(cx.tcx, new_ty), + )); + + if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) + && args[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) + { + deque.push_back((*term_param_ty, projected_ty)); + } + } + } + } + } + + true +} diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 0a52b25229d76..c2c5f765abfff 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -131,21 +131,6 @@ fn main() { 0 } } - - let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); - let _ = std::path::Path::new(".").join("."); - deref_target_is_x(X); - multiple_constraints([[""]]); - multiple_constraints_normalizes_to_same(X, X); - let _ = Some("").unwrap_or(""); - let _ = std::fs::write("x", "".to_string()); - - only_sized(&""); // Don't lint. `Sized` is only bound - let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound - let _ = Box::new(&""); // Don't lint. Type parameter appears in return type - ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter - refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't - multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments } #[allow(clippy::needless_borrowed_reference)] @@ -201,103 +186,6 @@ mod issue9160 { } } -#[derive(Clone, Copy)] -struct X; - -impl std::ops::Deref for X { - type Target = X; - fn deref(&self) -> &Self::Target { - self - } -} - -fn deref_target_is_x(_: T) -where - T: std::ops::Deref, -{ -} - -fn multiple_constraints(_: T) -where - T: IntoIterator + IntoIterator, - U: IntoIterator, - V: AsRef, - X: IntoIterator, - Y: AsRef, -{ -} - -fn multiple_constraints_normalizes_to_same(_: T, _: V) -where - T: std::ops::Deref, - U: std::ops::Deref, -{ -} - -fn only_sized(_: T) {} - -fn ref_as_ref_path(_: &'static T) -where - &'static T: AsRef, -{ -} - -trait RefsOnly { - type Referent; -} - -impl RefsOnly for &T { - type Referent = T; -} - -fn refs_only(_: T) -where - T: RefsOnly, -{ -} - -fn multiple_constraints_normalizes_to_different(_: T, _: U) -where - T: IntoIterator, - U: IntoIterator, - V: AsRef, -{ -} - -// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -mod copyable_iterator { - #[derive(Clone, Copy)] - struct Iter; - impl Iterator for Iter { - type Item = (); - fn next(&mut self) -> Option { - None - } - } - fn takes_iter(_: impl Iterator) {} - fn dont_warn(mut x: Iter) { - takes_iter(&mut x); - } - #[allow(unused_mut)] - fn warn(mut x: &mut Iter) { - takes_iter(x) - } -} - -#[clippy::msrv = "1.52.0"] -mod under_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - } -} - -#[clippy::msrv = "1.53.0"] -mod meets_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); - } -} - fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -326,184 +214,6 @@ fn issue9383() { } } -fn closure_test() { - let env = "env".to_owned(); - let arg = "arg".to_owned(); - let f = |arg| { - let loc = "loc".to_owned(); - let _ = std::fs::write("x", &env); // Don't lint. In environment - let _ = std::fs::write("x", arg); - let _ = std::fs::write("x", loc); - }; - let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` - f(arg); -} - -mod significant_drop { - #[derive(Debug)] - struct X; - - #[derive(Debug)] - struct Y; - - impl Drop for Y { - fn drop(&mut self) {} - } - - fn foo(x: X, y: Y) { - debug(x); - debug(&y); // Don't lint. Has significant drop - } - - fn debug(_: impl std::fmt::Debug) {} -} - -mod used_exactly_once { - fn foo(x: String) { - use_x(x); - } - fn use_x(_: impl AsRef) {} -} - -mod used_more_than_once { - fn foo(x: String) { - use_x(&x); - use_x_again(&x); - } - fn use_x(_: impl AsRef) {} - fn use_x_again(_: impl AsRef) {} -} - -// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -mod issue_9111 { - struct A; - - impl Extend for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - impl<'a> Extend<&'a u8> for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - fn main() { - let mut a = A; - a.extend(&[]); // vs a.extend([]); - } -} - -mod issue_9710 { - fn main() { - let string = String::new(); - for _i in 0..10 { - f(&string); - } - } - - fn f>(_: T) {} -} - -mod issue_9739 { - fn foo(_it: impl IntoIterator) {} - - fn main() { - foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9739_method_variant { - struct S; - - impl S { - fn foo(&self, _it: impl IntoIterator) {} - } - - fn main() { - S.foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9782 { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - // 100 - foo::<[u8; 100]>(a); - foo(a); - - // 16 - foo::<&[u8]>(&a); - foo(a.as_slice()); - - // 8 - foo::<&[u8; 100]>(&a); - foo(a); - } -} - -mod issue_9782_type_relative_variant { - struct S; - - impl S { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S::foo::<&[u8; 100]>(&a); - } -} - -mod issue_9782_method_variant { - struct S; - - impl S { - fn foo>(&self, t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S.foo::<&[u8; 100]>(&a); - } -} - -mod issue_10535 { - static SOME_STATIC: String = String::new(); - - static UNIT: () = compute(&SOME_STATIC); - - pub const fn compute(_: T) - where - T: Copy, - { - } -} - mod issue_10253 { struct S; trait X { diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 34a95d184635d..0cd6e41b8a47a 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -131,21 +131,6 @@ fn main() { 0 } } - - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - let _ = std::path::Path::new(".").join(&&"."); - deref_target_is_x(&X); - multiple_constraints(&[[""]]); - multiple_constraints_normalizes_to_same(&X, X); - let _ = Some("").unwrap_or(&""); - let _ = std::fs::write("x", &"".to_string()); - - only_sized(&""); // Don't lint. `Sized` is only bound - let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound - let _ = Box::new(&""); // Don't lint. Type parameter appears in return type - ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter - refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't - multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments } #[allow(clippy::needless_borrowed_reference)] @@ -201,103 +186,6 @@ mod issue9160 { } } -#[derive(Clone, Copy)] -struct X; - -impl std::ops::Deref for X { - type Target = X; - fn deref(&self) -> &Self::Target { - self - } -} - -fn deref_target_is_x(_: T) -where - T: std::ops::Deref, -{ -} - -fn multiple_constraints(_: T) -where - T: IntoIterator + IntoIterator, - U: IntoIterator, - V: AsRef, - X: IntoIterator, - Y: AsRef, -{ -} - -fn multiple_constraints_normalizes_to_same(_: T, _: V) -where - T: std::ops::Deref, - U: std::ops::Deref, -{ -} - -fn only_sized(_: T) {} - -fn ref_as_ref_path(_: &'static T) -where - &'static T: AsRef, -{ -} - -trait RefsOnly { - type Referent; -} - -impl RefsOnly for &T { - type Referent = T; -} - -fn refs_only(_: T) -where - T: RefsOnly, -{ -} - -fn multiple_constraints_normalizes_to_different(_: T, _: U) -where - T: IntoIterator, - U: IntoIterator, - V: AsRef, -{ -} - -// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -mod copyable_iterator { - #[derive(Clone, Copy)] - struct Iter; - impl Iterator for Iter { - type Item = (); - fn next(&mut self) -> Option { - None - } - } - fn takes_iter(_: impl Iterator) {} - fn dont_warn(mut x: Iter) { - takes_iter(&mut x); - } - #[allow(unused_mut)] - fn warn(mut x: &mut Iter) { - takes_iter(&mut x) - } -} - -#[clippy::msrv = "1.52.0"] -mod under_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - } -} - -#[clippy::msrv = "1.53.0"] -mod meets_msrv { - fn foo() { - let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - } -} - fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -326,184 +214,6 @@ fn issue9383() { } } -fn closure_test() { - let env = "env".to_owned(); - let arg = "arg".to_owned(); - let f = |arg| { - let loc = "loc".to_owned(); - let _ = std::fs::write("x", &env); // Don't lint. In environment - let _ = std::fs::write("x", &arg); - let _ = std::fs::write("x", &loc); - }; - let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` - f(arg); -} - -mod significant_drop { - #[derive(Debug)] - struct X; - - #[derive(Debug)] - struct Y; - - impl Drop for Y { - fn drop(&mut self) {} - } - - fn foo(x: X, y: Y) { - debug(&x); - debug(&y); // Don't lint. Has significant drop - } - - fn debug(_: impl std::fmt::Debug) {} -} - -mod used_exactly_once { - fn foo(x: String) { - use_x(&x); - } - fn use_x(_: impl AsRef) {} -} - -mod used_more_than_once { - fn foo(x: String) { - use_x(&x); - use_x_again(&x); - } - fn use_x(_: impl AsRef) {} - fn use_x_again(_: impl AsRef) {} -} - -// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -mod issue_9111 { - struct A; - - impl Extend for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - impl<'a> Extend<&'a u8> for A { - fn extend>(&mut self, _: T) { - unimplemented!() - } - } - - fn main() { - let mut a = A; - a.extend(&[]); // vs a.extend([]); - } -} - -mod issue_9710 { - fn main() { - let string = String::new(); - for _i in 0..10 { - f(&string); - } - } - - fn f>(_: T) {} -} - -mod issue_9739 { - fn foo(_it: impl IntoIterator) {} - - fn main() { - foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9739_method_variant { - struct S; - - impl S { - fn foo(&self, _it: impl IntoIterator) {} - } - - fn main() { - S.foo(if std::env::var_os("HI").is_some() { - &[0] - } else { - &[] as &[u32] - }); - } -} - -mod issue_9782 { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - // 100 - foo::<[u8; 100]>(a); - foo(a); - - // 16 - foo::<&[u8]>(&a); - foo(a.as_slice()); - - // 8 - foo::<&[u8; 100]>(&a); - foo(&a); - } -} - -mod issue_9782_type_relative_variant { - struct S; - - impl S { - fn foo>(t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S::foo::<&[u8; 100]>(&a); - } -} - -mod issue_9782_method_variant { - struct S; - - impl S { - fn foo>(&self, t: T) { - println!("{}", std::mem::size_of::()); - let _t: &[u8] = t.as_ref(); - } - } - - fn main() { - let a: [u8; 100] = [0u8; 100]; - - S.foo::<&[u8; 100]>(&a); - } -} - -mod issue_10535 { - static SOME_STATIC: String = String::new(); - - static UNIT: () = compute(&SOME_STATIC); - - pub const fn compute(_: T) - where - T: Copy, - { - } -} - mod issue_10253 { struct S; trait X { diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 8e27014d53c3e..e91b78b0a1520 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -121,101 +121,17 @@ error: this expression creates a reference which is immediately dereferenced by LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:135:51 - | -LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:136:44 - | -LL | let _ = std::path::Path::new(".").join(&&"."); - | ^^^^^ help: change this to: `"."` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:137:23 - | -LL | deref_target_is_x(&X); - | ^^ help: change this to: `X` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:138:26 - | -LL | multiple_constraints(&[[""]]); - | ^^^^^^^ help: change this to: `[[""]]` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:139:45 - | -LL | multiple_constraints_normalizes_to_same(&X, X); - | ^^ help: change this to: `X` - -error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:140:32 - | -LL | let _ = Some("").unwrap_or(&""); - | ^^^ help: change this to: `""` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:141:33 - | -LL | let _ = std::fs::write("x", &"".to_string()); - | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` - error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:190:13 + --> $DIR/needless_borrow.rs:175:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:199:13 + --> $DIR/needless_borrow.rs:184:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:283:20 - | -LL | takes_iter(&mut x) - | ^^^^^^ help: change this to: `x` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:297:55 - | -LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); - | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:335:37 - | -LL | let _ = std::fs::write("x", &arg); - | ^^^^ help: change this to: `arg` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:336:37 - | -LL | let _ = std::fs::write("x", &loc); - | ^^^^ help: change this to: `loc` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:354:15 - | -LL | debug(&x); - | ^^ help: change this to: `x` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:363:15 - | -LL | use_x(&x); - | ^^ help: change this to: `x` - -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:457:13 - | -LL | foo(&a); - | ^^ help: change this to: `a` - -error: aborting due to 36 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/needless_borrows_for_generic_args.fixed b/tests/ui/needless_borrows_for_generic_args.fixed new file mode 100644 index 0000000000000..2a335516f51c3 --- /dev/null +++ b/tests/ui/needless_borrows_for_generic_args.fixed @@ -0,0 +1,287 @@ +#![warn(clippy::needless_borrows_for_generic_args)] +#![allow( + clippy::unnecessary_to_owned, + clippy::unnecessary_literal_unwrap, + clippy::needless_borrow +)] + +use core::ops::Deref; +use std::any::Any; +use std::ffi::OsStr; +use std::fmt::{Debug, Display}; +use std::path::Path; +use std::process::Command; + +fn main() { + let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap(); + let _ = Path::new(".").join("."); + let _ = Any::type_id(&""); // Don't lint. `Any` is only bound + let _ = Box::new(&""); // Don't lint. Type parameter appears in return type + let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", "".to_string()); + + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn deref_target_is_x>(_: T) {} + + deref_target_is_x(X); + } + { + fn multiple_constraints(_: T) + where + T: IntoIterator + IntoIterator, + U: IntoIterator, + V: AsRef, + X: IntoIterator, + Y: AsRef, + { + } + + multiple_constraints([[""]]); + } + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn multiple_constraints_normalizes_to_same(_: T, _: V) + where + T: Deref, + U: Deref, + { + } + + multiple_constraints_normalizes_to_same(X, X); + } + { + fn only_sized(_: T) {} + only_sized(&""); // Don't lint. `Sized` is only bound + } + { + fn ref_as_ref_path(_: &'static T) + where + &'static T: AsRef, + { + } + + ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter + } + { + trait RefsOnly { + type Referent; + } + + impl RefsOnly for &T { + type Referent = T; + } + + fn refs_only(_: T) + where + T: RefsOnly, + { + } + + refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't + } + { + fn multiple_constraints_normalizes_to_different(_: T, _: U) + where + T: IntoIterator, + U: IntoIterator, + V: AsRef, + { + } + multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments + } + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + { + #[derive(Clone, Copy)] + struct Iter; + impl Iterator for Iter { + type Item = (); + fn next(&mut self) -> Option { + None + } + } + fn takes_iter(_: impl Iterator) {} + fn dont_warn(mut x: Iter) { + takes_iter(&mut x); + } + #[allow(unused_mut)] + fn warn(mut x: &mut Iter) { + takes_iter(x) + } + } + #[clippy::msrv = "1.52.0"] + { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + }; + #[clippy::msrv = "1.53.0"] + { + let _ = Command::new("ls").args(["-a", "-l"]).status().unwrap(); + }; + { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", arg); + let _ = std::fs::write("x", loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + let x = X; + f(&x); // Don't lint. Has significant drop + } + { + fn f(_: impl AsRef) {} + + let x = String::new(); + f(x); + } + { + fn f(_: impl AsRef) {} + fn f2(_: impl AsRef) {} + + let x = String::new(); + f(&x); + f2(&x); + } + // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 + // issue 9111 + { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } + // issue 9710 + { + fn f(_: impl AsRef) {} + + let x = String::new(); + for _ in 0..10 { + f(&x); + } + } + // issue 9739 + { + fn foo(_it: impl IntoIterator) {} + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + // issue 9782 + { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(a); + } + { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S::foo::<&[u8; 100]>(&a); + } + { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S.foo::<&[u8; 100]>(&a); + } + // issue 10535 + { + static SOME_STATIC: String = String::new(); + + static UNIT: () = compute(&SOME_STATIC); + + pub const fn compute(_: T) + where + T: Copy, + { + } + } +} diff --git a/tests/ui/needless_borrows_for_generic_args.rs b/tests/ui/needless_borrows_for_generic_args.rs new file mode 100644 index 0000000000000..f0567f486acca --- /dev/null +++ b/tests/ui/needless_borrows_for_generic_args.rs @@ -0,0 +1,287 @@ +#![warn(clippy::needless_borrows_for_generic_args)] +#![allow( + clippy::unnecessary_to_owned, + clippy::unnecessary_literal_unwrap, + clippy::needless_borrow +)] + +use core::ops::Deref; +use std::any::Any; +use std::ffi::OsStr; +use std::fmt::{Debug, Display}; +use std::path::Path; +use std::process::Command; + +fn main() { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + let _ = Path::new(".").join(&&"."); + let _ = Any::type_id(&""); // Don't lint. `Any` is only bound + let _ = Box::new(&""); // Don't lint. Type parameter appears in return type + let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", &"".to_string()); + + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn deref_target_is_x>(_: T) {} + + deref_target_is_x(&X); + } + { + fn multiple_constraints(_: T) + where + T: IntoIterator + IntoIterator, + U: IntoIterator, + V: AsRef, + X: IntoIterator, + Y: AsRef, + { + } + + multiple_constraints(&[[""]]); + } + { + #[derive(Clone, Copy)] + struct X; + + impl Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } + } + + fn multiple_constraints_normalizes_to_same(_: T, _: V) + where + T: Deref, + U: Deref, + { + } + + multiple_constraints_normalizes_to_same(&X, X); + } + { + fn only_sized(_: T) {} + only_sized(&""); // Don't lint. `Sized` is only bound + } + { + fn ref_as_ref_path(_: &'static T) + where + &'static T: AsRef, + { + } + + ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter + } + { + trait RefsOnly { + type Referent; + } + + impl RefsOnly for &T { + type Referent = T; + } + + fn refs_only(_: T) + where + T: RefsOnly, + { + } + + refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't + } + { + fn multiple_constraints_normalizes_to_different(_: T, _: U) + where + T: IntoIterator, + U: IntoIterator, + V: AsRef, + { + } + multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments + } + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + { + #[derive(Clone, Copy)] + struct Iter; + impl Iterator for Iter { + type Item = (); + fn next(&mut self) -> Option { + None + } + } + fn takes_iter(_: impl Iterator) {} + fn dont_warn(mut x: Iter) { + takes_iter(&mut x); + } + #[allow(unused_mut)] + fn warn(mut x: &mut Iter) { + takes_iter(&mut x) + } + } + #[clippy::msrv = "1.52.0"] + { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + }; + #[clippy::msrv = "1.53.0"] + { + let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + }; + { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); + } + { + #[derive(Debug)] + struct X; + + impl Drop for X { + fn drop(&mut self) {} + } + + fn f(_: impl Debug) {} + + let x = X; + f(&x); // Don't lint. Has significant drop + } + { + fn f(_: impl AsRef) {} + + let x = String::new(); + f(&x); + } + { + fn f(_: impl AsRef) {} + fn f2(_: impl AsRef) {} + + let x = String::new(); + f(&x); + f2(&x); + } + // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 + // issue 9111 + { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } + // issue 9710 + { + fn f(_: impl AsRef) {} + + let x = String::new(); + for _ in 0..10 { + f(&x); + } + } + // issue 9739 + { + fn foo(_it: impl IntoIterator) {} + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } + // issue 9782 + { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(&a); + } + { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S::foo::<&[u8; 100]>(&a); + } + { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + let a: [u8; 100] = [0u8; 100]; + S.foo::<&[u8; 100]>(&a); + } + // issue 10535 + { + static SOME_STATIC: String = String::new(); + + static UNIT: () = compute(&SOME_STATIC); + + pub const fn compute(_: T) + where + T: Copy, + { + } + } +} diff --git a/tests/ui/needless_borrows_for_generic_args.stderr b/tests/ui/needless_borrows_for_generic_args.stderr new file mode 100644 index 0000000000000..e2cde2c59a6e0 --- /dev/null +++ b/tests/ui/needless_borrows_for_generic_args.stderr @@ -0,0 +1,77 @@ +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:16:37 + | +LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` + | + = note: `-D clippy::needless-borrows-for-generic-args` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_borrows_for_generic_args)]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:17:33 + | +LL | let _ = Path::new(".").join(&&"."); + | ^^^^^ help: change this to: `"."` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:21:33 + | +LL | let _ = std::fs::write("x", &"".to_string()); + | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:36:27 + | +LL | deref_target_is_x(&X); + | ^^ help: change this to: `X` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:49:30 + | +LL | multiple_constraints(&[[""]]); + | ^^^^^^^ help: change this to: `[[""]]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:69:49 + | +LL | multiple_constraints_normalizes_to_same(&X, X); + | ^^ help: change this to: `X` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:127:24 + | +LL | takes_iter(&mut x) + | ^^^^^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:136:41 + | +LL | let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:144:41 + | +LL | let _ = std::fs::write("x", &arg); + | ^^^^ help: change this to: `arg` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:145:41 + | +LL | let _ = std::fs::write("x", &loc); + | ^^^^ help: change this to: `loc` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:167:11 + | +LL | f(&x); + | ^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrows_for_generic_args.rs:247:13 + | +LL | foo(&a); + | ^^ help: change this to: `a` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index 5259d9ce04bbf..094d9574ae987 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -2,7 +2,8 @@ unused, clippy::needless_raw_strings, clippy::needless_raw_string_hashes, - clippy::needless_borrow + clippy::needless_borrow, + clippy::needless_borrows_for_generic_args )] #![warn(clippy::invalid_regex, clippy::trivial_regex)] diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index 91f90157e6847..6d98d691d6f0a 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -1,5 +1,5 @@ error: trivial regex - --> $DIR/regex.rs:18:45 + --> $DIR/regex.rs:19:45 | LL | let pipe_in_wrong_position = Regex::new("|"); | ^^^ @@ -9,7 +9,7 @@ LL | let pipe_in_wrong_position = Regex::new("|"); = help: to override `-D warnings` add `#[allow(clippy::trivial_regex)]` error: trivial regex - --> $DIR/regex.rs:20:60 + --> $DIR/regex.rs:21:60 | LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); | ^^^ @@ -17,7 +17,7 @@ LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); = help: the regex is unlikely to be useful as it is error: regex syntax error: invalid character class range, the start must be <= the end - --> $DIR/regex.rs:22:42 + --> $DIR/regex.rs:23:42 | LL | let wrong_char_ranice = Regex::new("[z-a]"); | ^^^ @@ -26,7 +26,7 @@ LL | let wrong_char_ranice = Regex::new("[z-a]"); = help: to override `-D warnings` add `#[allow(clippy::invalid_regex)]` error: regex syntax error: invalid character class range, the start must be <= the end - --> $DIR/regex.rs:25:37 + --> $DIR/regex.rs:26:37 | LL | let some_unicode = Regex::new("[é-è]"); | ^^^ @@ -35,13 +35,13 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:28:33 + --> $DIR/regex.rs:29:33 | LL | let some_regex = Regex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ error: trivial regex - --> $DIR/regex.rs:30:53 + --> $DIR/regex.rs:31:53 | LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | ^^^ @@ -52,7 +52,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:32:41 + --> $DIR/regex.rs:33:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:33:56 + --> $DIR/regex.rs:34:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -70,7 +70,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:45:37 + --> $DIR/regex.rs:46:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -79,7 +79,7 @@ error: regex parse error: ( ^ error: unclosed group - --> $DIR/regex.rs:46:39 + --> $DIR/regex.rs:47:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ error: regex parse error: \b\c ^^ error: unrecognized escape sequence - --> $DIR/regex.rs:53:42 + --> $DIR/regex.rs:54:42 | LL | let escaped_string_span = Regex::new("\\b\\c"); | ^^^^^^^^ @@ -96,19 +96,19 @@ LL | let escaped_string_span = Regex::new("\\b\\c"); = help: consider using a raw string literal: `r".."` error: regex syntax error: duplicate flag - --> $DIR/regex.rs:55:34 + --> $DIR/regex.rs:56:34 | LL | let aux_span = Regex::new("(?ixi)"); | ^ ^ error: regex syntax error: pattern can match invalid UTF-8 - --> $DIR/regex.rs:61:53 + --> $DIR/regex.rs:62:53 | LL | let invalid_utf8_should_lint = Regex::new("(?-u)."); | ^ error: trivial regex - --> $DIR/regex.rs:66:33 + --> $DIR/regex.rs:67:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -116,7 +116,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:69:48 + --> $DIR/regex.rs:70:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -124,7 +124,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:72:42 + --> $DIR/regex.rs:73:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -132,7 +132,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:75:40 + --> $DIR/regex.rs:76:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -140,7 +140,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:78:39 + --> $DIR/regex.rs:79:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -148,7 +148,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:81:39 + --> $DIR/regex.rs:82:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:84:40 + --> $DIR/regex.rs:85:40 | LL | let trivial_backslash = Regex::new("a\\.b"); | ^^^^^^^ @@ -164,7 +164,7 @@ LL | let trivial_backslash = Regex::new("a\\.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:88:36 + --> $DIR/regex.rs:89:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -172,7 +172,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:91:36 + --> $DIR/regex.rs:92:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -180,7 +180,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:94:36 + --> $DIR/regex.rs:95:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -188,7 +188,7 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:97:44 + --> $DIR/regex.rs:98:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 7b662ca92d2ae..67faabc53cb5e 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::needless_borrow, clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] use std::borrow::Cow; diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index d79778a6a2ed4..99f9136427d4d 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -1,4 +1,4 @@ -#![allow(clippy::needless_borrow, clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] use std::borrow::Cow; From 32d3387c80b59ad85756aa66237aa6d2a0eca884 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 17 Sep 2023 14:33:06 +0000 Subject: [PATCH 44/78] used_underscore_bindings: respect lint levels on the binding definition --- clippy_lints/src/misc.rs | 104 +++++++++++------------- clippy_utils/src/lib.rs | 27 ++++++ tests/ui/used_underscore_binding.rs | 28 ++++++- tests/ui/used_underscore_binding.stderr | 47 +++++++++-- 4 files changed, 140 insertions(+), 66 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 303f012569087..f8e76a5833e50 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,24 +1,23 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_context}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{ + any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, in_constant, is_integer_literal, + is_lint_allowed, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq, +}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - self as hir, def, BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, Stmt, - StmtKind, TyKind, + BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, Ty, + TyKind, }; -use rustc_lint::{LateContext, LateLintPass}; +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::def_id::LocalDefId; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::{ExpnKind, Span}; - -use clippy_utils::sugg::Sugg; -use clippy_utils::{ - get_parent_expr, in_constant, is_integer_literal, is_lint_allowed, is_no_std_crate, iter_input_pats, - last_path_segment, SpanlessEq, -}; +use rustc_span::source_map::Span; use crate::ref_patterns::REF_PATTERNS; @@ -257,46 +256,56 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { self.check_cast(cx, expr.span, e, ty); return; } - if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) { - // Don't lint things expanded by #[derive(...)], etc or `await` desugaring + if in_external_macro(cx.sess(), expr.span) + || expr.span.desugaring_kind().is_some() + || any_parent_is_automatically_derived(cx.tcx, expr.hir_id) + { return; } - let sym; - let binding = match expr.kind { - ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => { - let binding = last_path_segment(qpath).ident.as_str(); - if binding.starts_with('_') && - !binding.starts_with("__") && - binding != "_result" && // FIXME: #944 - is_used(cx, expr) && - // don't lint if the declaration is in a macro - non_macro_local(cx, cx.qpath_res(qpath, expr.hir_id)) + let (definition_hir_id, ident) = match expr.kind { + ExprKind::Path(ref qpath) => { + if let QPath::Resolved(None, path) = qpath + && let Res::Local(id) = path.res + && is_used(cx, expr) { - Some(binding) + (id, last_path_segment(qpath).ident) } else { - None + return; } }, - ExprKind::Field(_, ident) => { - sym = ident.name; - let name = sym.as_str(); - if name.starts_with('_') && !name.starts_with("__") { - Some(name) + ExprKind::Field(recv, ident) => { + if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def() + && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name) + && let Some(local_did) = field.did.as_local() + && let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(local_did) + && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data() + { + (hir_id, ident) } else { - None + return; } }, - _ => None, + _ => return, }; - if let Some(binding) = binding { - span_lint( + + let name = ident.name.as_str(); + if name.starts_with('_') + && !name.starts_with("__") + && let definition_span = cx.tcx.hir().span(definition_hir_id) + && !definition_span.from_expansion() + && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) + { + span_lint_and_then( cx, USED_UNDERSCORE_BINDING, expr.span, &format!( - "used binding `{binding}` which is prefixed with an underscore. A leading \ + "used binding `{name}` which is prefixed with an underscore. A leading \ underscore signals that a binding will not be used" ), + |diag| { + diag.span_note(definition_span, format!("`{name}` is defined here")); + } ); } } @@ -312,29 +321,8 @@ fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } -/// Tests whether an expression is in a macro expansion (e.g., something -/// generated by `#[derive(...)]` or the like). -fn in_attributes_expansion(expr: &Expr<'_>) -> bool { - use rustc_span::hygiene::MacroKind; - if expr.span.from_expansion() { - let data = expr.span.ctxt().outer_expn_data(); - matches!(data.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, _)) - } else { - false - } -} - -/// Tests whether `res` is a variable defined outside a macro. -fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool { - if let def::Res::Local(id) = res { - !cx.tcx.hir().span(id).from_expansion() - } else { - false - } -} - impl LintPass { - fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { + fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) { if_chain! { if let TyKind::Ptr(ref mut_ty) = ty.kind; if is_integer_literal(e, 0); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 4ef3ec19647e9..eb5baa98f62f3 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1785,6 +1785,33 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc None } +/// Returns `true` if the lint is `#[allow]`ed or `#[expect]`ed at any of the `ids`, fulfilling all +/// of the expectations in `ids` +/// +/// This should only be used when the lint would otherwise be emitted, for a way to check if a lint +/// is allowed early to skip work see [`is_lint_allowed`] +/// +/// To emit at a lint at a different context than the one current see +/// [`span_lint_hir`](diagnostics::span_lint_hir) or +/// [`span_lint_hir_and_then`](diagnostics::span_lint_hir_and_then) +pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator) -> bool { + let mut suppress_lint = false; + + for id in ids { + let (level, _) = cx.tcx.lint_level_at_node(lint, id); + if let Some(expectation) = level.get_expectation_id() { + cx.fulfill_expectation(expectation); + } + + match level { + Level::Allow | Level::Expect(_) => suppress_lint = true, + Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {}, + } + } + + suppress_lint +} + /// Returns `true` if the lint is allowed in the current context. This is useful for /// skipping long running code when it's unnecessary /// diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index c672eff1c2710..a8f404b1400c9 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -1,6 +1,5 @@ //@aux-build:proc_macro_derive.rs -#![feature(rustc_private)] -#![warn(clippy::all)] +#![feature(rustc_private, lint_reasons)] #![warn(clippy::used_underscore_binding)] #![allow(clippy::disallowed_names, clippy::eq_op, clippy::uninlined_format_args)] @@ -107,6 +106,31 @@ async fn await_desugaring() { .await } +struct PhantomField { + _marker: std::marker::PhantomData, +} + +impl std::fmt::Debug for PhantomField { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("PhantomField").field("_marker", &self._marker).finish() + } +} + +struct AllowedField { + #[allow(clippy::used_underscore_binding)] + _allowed: usize, +} + +struct ExpectedField { + #[expect(clippy::used_underscore_binding)] + _expected: usize, +} + +fn lint_levels(allowed: AllowedField, expected: ExpectedField) { + let _ = allowed._allowed; + let _ = expected._expected; +} + fn main() { let foo = 0u32; // tests of unused_underscore lint diff --git a/tests/ui/used_underscore_binding.stderr b/tests/ui/used_underscore_binding.stderr index 289519b172eeb..78d8279810c1f 100644 --- a/tests/ui/used_underscore_binding.stderr +++ b/tests/ui/used_underscore_binding.stderr @@ -1,41 +1,76 @@ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:24:5 + --> $DIR/used_underscore_binding.rs:23:5 | LL | _foo + 1 | ^^^^ | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:22:22 + | +LL | fn prefix_underscore(_foo: u32) -> u32 { + | ^^^^ = note: `-D clippy::used-underscore-binding` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::used_underscore_binding)]` error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:29:20 + --> $DIR/used_underscore_binding.rs:28:20 | LL | println!("{}", _foo); | ^^^^ + | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:27:24 + | +LL | fn in_macro_or_desugar(_foo: u32) { + | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:30:16 + --> $DIR/used_underscore_binding.rs:29:16 | LL | assert_eq!(_foo, _foo); | ^^^^ + | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:27:24 + | +LL | fn in_macro_or_desugar(_foo: u32) { + | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:30:22 + --> $DIR/used_underscore_binding.rs:29:22 | LL | assert_eq!(_foo, _foo); | ^^^^ + | +note: `_foo` is defined here + --> $DIR/used_underscore_binding.rs:27:24 + | +LL | fn in_macro_or_desugar(_foo: u32) { + | ^^^^ error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:43:5 + --> $DIR/used_underscore_binding.rs:42:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ + | +note: `_underscore_field` is defined here + --> $DIR/used_underscore_binding.rs:36:5 + | +LL | _underscore_field: u32, + | ^^^^^^^^^^^^^^^^^^^^^^ error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:104:16 + --> $DIR/used_underscore_binding.rs:103:16 | LL | uses_i(_i); | ^^ + | +note: `_i` is defined here + --> $DIR/used_underscore_binding.rs:102:13 + | +LL | let _i = 5; + | ^^ error: aborting due to 6 previous errors From 1c9f3bef8bb80dc8706e4595a262fe8825ccf74a Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Sun, 17 Sep 2023 17:50:45 -0400 Subject: [PATCH 45/78] Add redundant_as_str lint This lint checks for `as_str` on a `String` immediately followed by `as_bytes` or `is_empty` as those methods are available on `String` too. This could possibly also be extended to `&[u8]` in the future. --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/mod.rs | 29 +++++++++++++++++ clippy_lints/src/methods/redundant_as_str.rs | 34 ++++++++++++++++++++ tests/ui/redundant_as_str.fixed | 6 ++++ tests/ui/redundant_as_str.rs | 6 ++++ tests/ui/redundant_as_str.stderr | 17 ++++++++++ 7 files changed, 94 insertions(+) create mode 100644 clippy_lints/src/methods/redundant_as_str.rs create mode 100644 tests/ui/redundant_as_str.fixed create mode 100644 tests/ui/redundant_as_str.rs create mode 100644 tests/ui/redundant_as_str.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index a2484203e9e89..8c9ab1e2402ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5281,6 +5281,7 @@ Released 2018-09-13 [`readonly_write_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#readonly_write_lock [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation +[`redundant_as_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_as_str [`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block [`redundant_at_rest_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_at_rest_pattern [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b1ffc34781246..aa2ca0e11494b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -403,6 +403,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, + crate::methods::REDUNDANT_AS_STR_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, crate::methods::SEARCH_IS_SOME_INFO, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f468236480684..0b0a90bac0cb3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -78,6 +78,7 @@ mod path_ends_with_ext; mod range_zip_with_len; mod read_line_without_trim; mod readonly_write_lock; +mod redundant_as_str; mod repeat_once; mod search_is_some; mod seek_from_current; @@ -3605,6 +3606,32 @@ declare_clippy_lint! { "attempting to compare file extensions using `Path::ends_with`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `as_str()` on a `String`` chained with a method available on the `String` itself. + /// + /// ### Why is this bad? + /// The `as_str()` conversion is pointless and can be removed for simplicity and cleanliness. + /// + /// ### Example + /// ```rust + /// # #![allow(unused)] + /// let owned_string = "This is a string".to_owned(); + /// owned_string.as_str().as_bytes() + /// ``` + /// + /// Use instead: + /// ```rust + /// # #![allow(unused)] + /// let owned_string = "This is a string".to_owned(); + /// owned_string.as_bytes() + /// ``` + #[clippy::version = "1.74.0"] + pub REDUNDANT_AS_STR, + complexity, + "`as_str` used to call a method on `str` that is also available on `String`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3749,6 +3776,7 @@ impl_lint_pass!(Methods => [ READONLY_WRITE_LOCK, ITER_OUT_OF_BOUNDS, PATH_ENDS_WITH_EXT, + REDUNDANT_AS_STR, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3975,6 +4003,7 @@ impl Methods { ("as_deref" | "as_deref_mut", []) => { needless_option_as_deref::check(cx, expr, recv, name); }, + ("as_bytes" | "is_empty", []) => if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), diff --git a/clippy_lints/src/methods/redundant_as_str.rs b/clippy_lints/src/methods/redundant_as_str.rs new file mode 100644 index 0000000000000..98cd6afc2b79f --- /dev/null +++ b/clippy_lints/src/methods/redundant_as_str.rs @@ -0,0 +1,34 @@ +use super::REDUNDANT_AS_STR; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::query::Key; +use rustc_span::Span; + +pub(super) fn check( + cx: &LateContext<'_>, + _expr: &Expr<'_>, + recv: &Expr<'_>, + as_str_span: Span, + other_method_span: Span, +) { + if cx + .tcx + .lang_items() + .string() + .is_some_and(|id| Some(id) == cx.typeck_results().expr_ty(recv).ty_adt_id()) + { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + REDUNDANT_AS_STR, + as_str_span.to(other_method_span), + "this `as_str` is redundant and can be removed as the method immediately following exists on `String` too", + "try", + snippet_with_applicability(cx, other_method_span, "..", &mut applicability).into_owned(), + applicability, + ); + } +} diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed new file mode 100644 index 0000000000000..f339787e68943 --- /dev/null +++ b/tests/ui/redundant_as_str.fixed @@ -0,0 +1,6 @@ +#![warn(clippy::redundant_as_str)] + +fn main() { + let _redundant = "Hello, world!".to_owned().as_bytes(); + let _redundant = "Hello, world!".to_owned().is_empty(); +} diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs new file mode 100644 index 0000000000000..66074df83a865 --- /dev/null +++ b/tests/ui/redundant_as_str.rs @@ -0,0 +1,6 @@ +#![warn(clippy::redundant_as_str)] + +fn main() { + let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); + let _redundant = "Hello, world!".to_owned().as_str().is_empty(); +} diff --git a/tests/ui/redundant_as_str.stderr b/tests/ui/redundant_as_str.stderr new file mode 100644 index 0000000000000..32b5232385dda --- /dev/null +++ b/tests/ui/redundant_as_str.stderr @@ -0,0 +1,17 @@ +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:4:49 + | +LL | let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); + | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` + | + = note: `-D clippy::redundant-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` + +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:5:49 + | +LL | let _redundant = "Hello, world!".to_owned().as_str().is_empty(); + | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` + +error: aborting due to 2 previous errors + From 01056c5aae63c546c3d988a0b46e502a3a2a7baf Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Sun, 17 Sep 2023 18:10:21 -0400 Subject: [PATCH 46/78] Fix missing semicolon in redundant_as_str docstring example --- clippy_lints/src/methods/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 0b0a90bac0cb3..bd07e6cbf2e2c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3617,14 +3617,14 @@ declare_clippy_lint! { /// ```rust /// # #![allow(unused)] /// let owned_string = "This is a string".to_owned(); - /// owned_string.as_str().as_bytes() + /// owned_string.as_str().as_bytes(); /// ``` /// /// Use instead: /// ```rust /// # #![allow(unused)] /// let owned_string = "This is a string".to_owned(); - /// owned_string.as_bytes() + /// owned_string.as_bytes(); /// ``` #[clippy::version = "1.74.0"] pub REDUNDANT_AS_STR, From b06b915dc04a577cfe599d7e44137124ba12c877 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 18 Sep 2023 12:10:35 +0000 Subject: [PATCH 47/78] Move zero_ptr to the casts module --- clippy_lints/src/casts/mod.rs | 26 +++++++++ clippy_lints/src/casts/zero_ptr.rs | 39 ++++++++++++++ clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/misc.rs | 84 +++--------------------------- 5 files changed, 75 insertions(+), 78 deletions(-) create mode 100644 clippy_lints/src/casts/zero_ptr.rs diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 88ffbb55486bd..b00130ffd76db 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -20,6 +20,7 @@ mod ptr_as_ptr; mod ptr_cast_constness; mod unnecessary_cast; mod utils; +mod zero_ptr; use clippy_utils::is_hir_ty_cfg_dependant; use clippy_utils::msrvs::{self, Msrv}; @@ -665,6 +666,29 @@ declare_clippy_lint! { "casting a known floating-point NaN into an integer" } +declare_clippy_lint! { + /// ### What it does + /// Catch casts from `0` to some pointer type + /// + /// ### Why is this bad? + /// This generally means `null` and is better expressed as + /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}. + /// + /// ### Example + /// ```rust + /// let a = 0 as *const u32; + /// ``` + /// + /// Use instead: + /// ```rust + /// let a = std::ptr::null::(); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub ZERO_PTR, + style, + "using `0 as *{const, mut} T`" +} + pub struct Casts { msrv: Msrv, } @@ -699,6 +723,7 @@ impl_lint_pass!(Casts => [ CAST_SLICE_FROM_RAW_PARTS, AS_PTR_CAST_MUT, CAST_NAN_TO_INT, + ZERO_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -729,6 +754,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); + zero_ptr::check(cx, expr, cast_expr, cast_to_hir); if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span); diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs new file mode 100644 index 0000000000000..5071af5ecb986 --- /dev/null +++ b/clippy_lints/src/casts/zero_ptr.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{in_constant, is_integer_literal, std_or_core}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability, Ty, TyKind}; +use rustc_lint::LateContext; + +use super::ZERO_PTR; + +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { + if let TyKind::Ptr(ref mut_ty) = to.kind + && is_integer_literal(from, 0) + && !in_constant(cx, from.hir_id) + && let Some(std_or_core) = std_or_core(cx) + { + let (msg, sugg_fn) = match mut_ty.mutbl { + Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), + Mutability::Not => ("`0 as *const _` detected", "ptr::null"), + }; + + let sugg = if let TyKind::Infer = mut_ty.ty.kind { + format!("{std_or_core}::{sugg_fn}()") + } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { + format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") + } else { + return; + }; + + span_lint_and_sugg( + cx, + ZERO_PTR, + expr.span, + msg, + "try", + sugg, + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b1ffc34781246..d45f4013816e9 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -97,6 +97,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::UNNECESSARY_CAST_INFO, + crate::casts::ZERO_PTR_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, @@ -442,7 +443,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, crate::misc::TOPLEVEL_REF_ARG_INFO, crate::misc::USED_UNDERSCORE_BINDING_INFO, - crate::misc::ZERO_PTR_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, crate::misc_early::DOUBLE_NEG_INFO, crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dcc6ee68e1b46..fa009c8b2d6c8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -637,7 +637,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); store.register_late_pass(|_| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); - store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|_| Box::new(mut_mut::MutMut)); store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index f8e76a5833e50..9c8b47fb30327 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,21 +1,20 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::source::{snippet, snippet_opt, snippet_with_context}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, in_constant, is_integer_literal, - is_lint_allowed, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq, + any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, is_lint_allowed, iter_input_pats, + last_path_segment, SpanlessEq, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, Ty, - TyKind, + BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, 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_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -55,6 +54,7 @@ declare_clippy_lint! { style, "an entire binding declared as `ref`, in a function argument or a `let` statement" } + declare_clippy_lint! { /// ### What it does /// Checks for the use of bindings with a single leading @@ -102,51 +102,13 @@ declare_clippy_lint! { "using a short circuit boolean condition as a statement" } -declare_clippy_lint! { - /// ### What it does - /// Catch casts from `0` to some pointer type - /// - /// ### Why is this bad? - /// This generally means `null` and is better expressed as - /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}. - /// - /// ### Example - /// ```rust - /// let a = 0 as *const u32; - /// ``` - /// - /// Use instead: - /// ```rust - /// let a = std::ptr::null::(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub ZERO_PTR, - style, - "using `0 as *{const, mut} T`" -} - -pub struct LintPass { - std_or_core: &'static str, -} -impl Default for LintPass { - fn default() -> Self { - Self { std_or_core: "std" } - } -} -impl_lint_pass!(LintPass => [ +declare_lint_pass!(LintPass => [ TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, SHORT_CIRCUIT_STATEMENT, - ZERO_PTR, ]); impl<'tcx> LateLintPass<'tcx> for LintPass { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if is_no_std_crate(cx) { - self.std_or_core = "core"; - } - } - fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -252,10 +214,6 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Cast(e, ty) = expr.kind { - self.check_cast(cx, expr.span, e, ty); - return; - } if in_external_macro(cx.sess(), expr.span) || expr.span.desugaring_kind().is_some() || any_parent_is_automatically_derived(cx.tcx, expr.hir_id) @@ -320,29 +278,3 @@ fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => is_used(cx, parent), }) } - -impl LintPass { - fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &Ty<'_>) { - if_chain! { - if let TyKind::Ptr(ref mut_ty) = ty.kind; - if is_integer_literal(e, 0); - if !in_constant(cx, e.hir_id); - then { - let (msg, sugg_fn) = match mut_ty.mutbl { - Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), - Mutability::Not => ("`0 as *const _` detected", "ptr::null"), - }; - - let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { - (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MachineApplicable) - } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { - (format!("{}::{sugg_fn}::<{mut_ty_snip}>()", self.std_or_core), Applicability::MachineApplicable) - } else { - // `MaybeIncorrect` as type inference may not work with the suggested code - (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MaybeIncorrect) - }; - span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); - } - } - } -} From 32519528dfe8743d006e337c8105868761922d03 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 18 Sep 2023 12:42:37 +0000 Subject: [PATCH 48/78] Update remark CI deps --- .github/workflows/remark.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 7d25b6a2b79e7..30bd476332f75 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -21,7 +21,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: '14.x' + node-version: '18.x' - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm @@ -29,19 +29,19 @@ jobs: - name: Install mdbook run: | mkdir mdbook - curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.28/mdbook-v0.4.28-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.34/mdbook-v0.4.34-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH # Run - name: Check *.md files - run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null + run: ./node_modules/.bin/remark -u lint -f . - name: Linkcheck book run: | rustup toolchain install nightly --component rust-docs curl https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh -o linkcheck.sh sh linkcheck.sh clippy --path ./book - + - name: Build mdbook run: mdbook build book From b8b420cc79dc18423a9bf59ee3397a398e9aac2a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 18 Sep 2023 15:47:24 +0200 Subject: [PATCH 49/78] Improve code readability by moving the retrieval of closures inside async functions right besides other closures handling. Add doc comment explaining what `MutablyUsedVariablesCtxt::prev_move_to_closure` is about. --- clippy_lints/src/needless_pass_by_ref_mut.rs | 57 +++++++------------- 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index bea44ed92ef38..36ca61c0b44d3 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,13 +1,13 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; +use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_fn, walk_qpath, FnKind, Visitor}; +use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor}; use rustc_hir::{ - Body, BodyId, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, - PatKind, QPath, + Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; @@ -22,6 +22,8 @@ use rustc_span::symbol::kw; use rustc_span::Span; use rustc_target::spec::abi::Abi; +use core::ops::ControlFlow; + declare_clippy_lint! { /// ### What it does /// Check if a `&mut` function argument is actually used mutably. @@ -189,17 +191,19 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // We retrieve all the closures declared in the async function because they will // not be found by `euv::Delegate`. - let mut closures_retriever = ClosuresRetriever { - cx, - closures: FxHashSet::default(), - }; - walk_fn(&mut closures_retriever, kind, decl, body.id(), fn_def_id); - check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures_retriever.closures); + let mut closures: FxHashSet = FxHashSet::default(); + for_each_expr_with_closures(cx, body, |expr| { + if let ExprKind::Closure(closure) = expr.kind { + closures.insert(closure.def_id); + } + ControlFlow::<()>::Continue(()) + }); + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); while !ctx.async_closures.is_empty() { - let closures = ctx.async_closures.clone(); + let async_closures = ctx.async_closures.clone(); ctx.async_closures.clear(); - check_closures(&mut ctx, cx, &infcx, &mut checked_closures, closures); + check_closures(&mut ctx, cx, &infcx, &mut checked_closures, async_closures); } } ctx @@ -264,6 +268,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { struct MutablyUsedVariablesCtxt<'tcx> { mutably_used_vars: HirIdSet, prev_bind: Option, + /// In async functions, the inner AST is composed of multiple layers until we reach the code + /// defined by the user. Because of that, some variables are marked as mutably borrowed even + /// though they're not. This field lists the `HirId` that should not be considered as mutable + /// use of a variable. prev_move_to_closure: HirIdSet, aliases: HirIdMap, async_closures: FxHashSet, @@ -459,30 +467,3 @@ impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> { } } } - -struct ClosuresRetriever<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - closures: FxHashSet, -} - -impl<'a, 'tcx> Visitor<'tcx> for ClosuresRetriever<'a, 'tcx> { - type NestedFilter = OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } - - fn visit_fn( - &mut self, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'tcx>, - body_id: BodyId, - _span: Span, - fn_def_id: LocalDefId, - ) { - if matches!(kind, FnKind::Closure) { - self.closures.insert(fn_def_id); - } - walk_fn(self, kind, decl, body_id, fn_def_id); - } -} From f2ab16eac139522ac5a9c6606c81e5967aec6d79 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:08:02 -0400 Subject: [PATCH 50/78] Add not triggering examples to redundant_as_str test --- tests/ui/redundant_as_str.fixed | 3 +++ tests/ui/redundant_as_str.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed index f339787e68943..c8e44d1c39e97 100644 --- a/tests/ui/redundant_as_str.fixed +++ b/tests/ui/redundant_as_str.fixed @@ -3,4 +3,7 @@ fn main() { let _redundant = "Hello, world!".to_owned().as_bytes(); let _redundant = "Hello, world!".to_owned().is_empty(); + + let _ok = "Hello, world!".to_owned().as_bytes(); + let _ok = "Hello, world!".to_owned().is_empty(); } diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index 66074df83a865..e9aa84419130c 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -3,4 +3,7 @@ fn main() { let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); let _redundant = "Hello, world!".to_owned().as_str().is_empty(); + + let _ok = "Hello, world!".to_owned().as_bytes(); + let _ok = "Hello, world!".to_owned().is_empty(); } From 00ca47b97d59ab3384c9786e5b38f0262f413384 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:32:22 -0400 Subject: [PATCH 51/78] Add more examples and comments to redundant_as_str test --- tests/ui/redundant_as_str.fixed | 9 --------- tests/ui/redundant_as_str.rs | 19 +++++++++++++++---- tests/ui/redundant_as_str.stderr | 19 +++++-------------- 3 files changed, 20 insertions(+), 27 deletions(-) delete mode 100644 tests/ui/redundant_as_str.fixed diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed deleted file mode 100644 index c8e44d1c39e97..0000000000000 --- a/tests/ui/redundant_as_str.fixed +++ /dev/null @@ -1,9 +0,0 @@ -#![warn(clippy::redundant_as_str)] - -fn main() { - let _redundant = "Hello, world!".to_owned().as_bytes(); - let _redundant = "Hello, world!".to_owned().is_empty(); - - let _ok = "Hello, world!".to_owned().as_bytes(); - let _ok = "Hello, world!".to_owned().is_empty(); -} diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index e9aa84419130c..35b6bdb3c5e85 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -1,9 +1,20 @@ #![warn(clippy::redundant_as_str)] fn main() { - let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); - let _redundant = "Hello, world!".to_owned().as_str().is_empty(); + let string = "Hello, world!".to_owned(); - let _ok = "Hello, world!".to_owned().as_bytes(); - let _ok = "Hello, world!".to_owned().is_empty(); + // These methods are redundant and the `as_str` can be removed. + let _redundant = string.as_str().as_bytes(); + let _redundant = string.as_str().is_empty(); + + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method. + let _not_redundant = string.as_str().escape_unicode(); + let _not_redundant = string.as_str().trim(); + let _not_redundant = string.as_str().split_whitespace(); + + // These methods don't use `as_str` and are applied on a `str` directly. + let borrowed_str = "Hello, world"! + let _no_as_str = borrowed_str.as_bytes(); + let _no_as_str = borrowed_str.is_empty(); } diff --git a/tests/ui/redundant_as_str.stderr b/tests/ui/redundant_as_str.stderr index 32b5232385dda..e7da34650f2cf 100644 --- a/tests/ui/redundant_as_str.stderr +++ b/tests/ui/redundant_as_str.stderr @@ -1,17 +1,8 @@ -error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> $DIR/redundant_as_str.rs:4:49 +error: expected one of `.`, `;`, `?`, `else`, or an operator, found `!` + --> $DIR/redundant_as_str.rs:17:35 | -LL | let _redundant = "Hello, world!".to_owned().as_str().as_bytes(); - | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` - | - = note: `-D clippy::redundant-as-str` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` - -error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> $DIR/redundant_as_str.rs:5:49 - | -LL | let _redundant = "Hello, world!".to_owned().as_str().is_empty(); - | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` +LL | let borrowed_str = "Hello, world"! + | ^ expected one of `.`, `;`, `?`, `else`, or an operator -error: aborting due to 2 previous errors +error: aborting due to previous error From 367ba9cd003e271dc4d7e354b1212fe3c60d3329 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:36:10 -0400 Subject: [PATCH 52/78] Add not redundant examples for redundant_as_str --- tests/ui/redundant_as_str.fixed | 24 ++++++++++++++++++++++++ tests/ui/redundant_as_str.rs | 10 +++++++--- tests/ui/redundant_as_str.stderr | 19 ++++++++++++++----- 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 tests/ui/redundant_as_str.fixed diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed new file mode 100644 index 0000000000000..a95ab2d25a2c6 --- /dev/null +++ b/tests/ui/redundant_as_str.fixed @@ -0,0 +1,24 @@ +#![warn(clippy::redundant_as_str)] + +fn main() { + let string = "Hello, world!".to_owned(); + + // These methods are redundant and the `as_str` can be removed. + let _redundant = string.as_bytes(); + let _redundant = string.is_empty(); + + // These methods don't use `as_str` when they are redundant. + let _no_as_str = string.as_bytes(); + let _no_as_str = string.is_empty(); + + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method. + let _not_redundant = string.as_str().escape_unicode(); + let _not_redundant = string.as_str().trim(); + let _not_redundant = string.as_str().split_whitespace(); + + // These methods don't use `as_str` and are applied on a `str` directly. + let borrowed_str = "Hello, world!"; + let _is_str = borrowed_str.as_bytes(); + let _is_str = borrowed_str.is_empty(); +} diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index 35b6bdb3c5e85..d5ccfe2effeff 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -7,6 +7,10 @@ fn main() { let _redundant = string.as_str().as_bytes(); let _redundant = string.as_str().is_empty(); + // These methods don't use `as_str` when they are redundant. + let _no_as_str = string.as_bytes(); + let _no_as_str = string.is_empty(); + // These methods are not redundant, and are equivelant to // doing dereferencing the string and applying the method. let _not_redundant = string.as_str().escape_unicode(); @@ -14,7 +18,7 @@ fn main() { let _not_redundant = string.as_str().split_whitespace(); // These methods don't use `as_str` and are applied on a `str` directly. - let borrowed_str = "Hello, world"! - let _no_as_str = borrowed_str.as_bytes(); - let _no_as_str = borrowed_str.is_empty(); + let borrowed_str = "Hello, world!"; + let _is_str = borrowed_str.as_bytes(); + let _is_str = borrowed_str.is_empty(); } diff --git a/tests/ui/redundant_as_str.stderr b/tests/ui/redundant_as_str.stderr index e7da34650f2cf..0ea42a94a81ed 100644 --- a/tests/ui/redundant_as_str.stderr +++ b/tests/ui/redundant_as_str.stderr @@ -1,8 +1,17 @@ -error: expected one of `.`, `;`, `?`, `else`, or an operator, found `!` - --> $DIR/redundant_as_str.rs:17:35 +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:7:29 | -LL | let borrowed_str = "Hello, world"! - | ^ expected one of `.`, `;`, `?`, `else`, or an operator +LL | let _redundant = string.as_str().as_bytes(); + | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` + | + = note: `-D clippy::redundant-as-str` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` + +error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too + --> $DIR/redundant_as_str.rs:8:29 + | +LL | let _redundant = string.as_str().is_empty(); + | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` -error: aborting due to previous error +error: aborting due to 2 previous errors From 522485343466bf81b9dedcb340d24f5287669c28 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:37:27 -0400 Subject: [PATCH 53/78] Remove periods from end of sentences in redundant_as_str comments --- tests/ui/redundant_as_str.fixed | 8 ++++---- tests/ui/redundant_as_str.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed index a95ab2d25a2c6..c4962e7e00002 100644 --- a/tests/ui/redundant_as_str.fixed +++ b/tests/ui/redundant_as_str.fixed @@ -3,21 +3,21 @@ fn main() { let string = "Hello, world!".to_owned(); - // These methods are redundant and the `as_str` can be removed. + // These methods are redundant and the `as_str` can be removed let _redundant = string.as_bytes(); let _redundant = string.is_empty(); - // These methods don't use `as_str` when they are redundant. + // These methods don't use `as_str` when they are redundant let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); // These methods are not redundant, and are equivelant to - // doing dereferencing the string and applying the method. + // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); let _not_redundant = string.as_str().split_whitespace(); - // These methods don't use `as_str` and are applied on a `str` directly. + // These methods don't use `as_str` and are applied on a `str` directly let borrowed_str = "Hello, world!"; let _is_str = borrowed_str.as_bytes(); let _is_str = borrowed_str.is_empty(); diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index d5ccfe2effeff..7357d7f0eeaf0 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -3,21 +3,21 @@ fn main() { let string = "Hello, world!".to_owned(); - // These methods are redundant and the `as_str` can be removed. + // These methods are redundant and the `as_str` can be removed let _redundant = string.as_str().as_bytes(); let _redundant = string.as_str().is_empty(); - // These methods don't use `as_str` when they are redundant. + // These methods don't use `as_str` when they are redundant let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); // These methods are not redundant, and are equivelant to - // doing dereferencing the string and applying the method. + // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); let _not_redundant = string.as_str().split_whitespace(); - // These methods don't use `as_str` and are applied on a `str` directly. + // These methods don't use `as_str` and are applied on a `str` directly let borrowed_str = "Hello, world!"; let _is_str = borrowed_str.as_bytes(); let _is_str = borrowed_str.is_empty(); From 17d174d113fda8a21680a103d062d6fd589b4dc7 Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:39:09 -0400 Subject: [PATCH 54/78] Reformat redundant_as_str ui test --- tests/ui/redundant_as_str.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index 7357d7f0eeaf0..33adb60999641 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -1,9 +1,9 @@ #![warn(clippy::redundant_as_str)] fn main() { - let string = "Hello, world!".to_owned(); + let string = "Hello, world!".to_owned(); - // These methods are redundant and the `as_str` can be removed + // These methods are redundant and the `as_str` can be removed let _redundant = string.as_str().as_bytes(); let _redundant = string.as_str().is_empty(); @@ -11,14 +11,14 @@ fn main() { let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); - // These methods are not redundant, and are equivelant to - // doing dereferencing the string and applying the method + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); let _not_redundant = string.as_str().split_whitespace(); - // These methods don't use `as_str` and are applied on a `str` directly - let borrowed_str = "Hello, world!"; + // These methods don't use `as_str` and are applied on a `str` directly + let borrowed_str = "Hello, world!"; let _is_str = borrowed_str.as_bytes(); let _is_str = borrowed_str.is_empty(); } From d9d25e98fca4085cfe062700f6ca465abc20a52d Mon Sep 17 00:00:00 2001 From: Dev381 <49997896+Dev380@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:46:00 -0400 Subject: [PATCH 55/78] Fix redundant_as_str .fixed file not being consistent --- tests/ui/redundant_as_str.fixed | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed index c4962e7e00002..a38523a7c79e7 100644 --- a/tests/ui/redundant_as_str.fixed +++ b/tests/ui/redundant_as_str.fixed @@ -1,9 +1,9 @@ #![warn(clippy::redundant_as_str)] fn main() { - let string = "Hello, world!".to_owned(); + let string = "Hello, world!".to_owned(); - // These methods are redundant and the `as_str` can be removed + // These methods are redundant and the `as_str` can be removed let _redundant = string.as_bytes(); let _redundant = string.is_empty(); @@ -11,14 +11,14 @@ fn main() { let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); - // These methods are not redundant, and are equivelant to - // doing dereferencing the string and applying the method + // These methods are not redundant, and are equivelant to + // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); let _not_redundant = string.as_str().split_whitespace(); - // These methods don't use `as_str` and are applied on a `str` directly - let borrowed_str = "Hello, world!"; + // These methods don't use `as_str` and are applied on a `str` directly + let borrowed_str = "Hello, world!"; let _is_str = borrowed_str.as_bytes(); let _is_str = borrowed_str.is_empty(); } From b30cefc775e92892f8d49a649bb09510bbbb79af Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 Sep 2023 09:36:22 +0200 Subject: [PATCH 56/78] move ConstValue into mir this way we have mir::ConstValue and ty::ValTree as reasonably parallel --- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/ty.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index fcb90c63a6fc6..c88fce22f7fb6 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -656,7 +656,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'tcx>) -> Option> { - use rustc_middle::mir::interpret::ConstValue; + use rustc_middle::mir::ConstValue; match result { mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index f0b4ede35fbc5..9e25d97f5a6bc 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,7 @@ use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; -use rustc_middle::mir::interpret::{ConstValue, Scalar}; +use rustc_middle::mir::{ConstValue, interpret::Scalar}; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ From 1b3e5dd0fcae6056e8ab435b795eb4d73f69230d Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 17 Jul 2023 00:30:24 -0400 Subject: [PATCH 57/78] Change default configuration of `undocumented_unsafe_blocks` This patch sets the two configuration options for `undocumented_unsafe_blocks` to `true` by default: these are `accept-comment-above-statement` and `accept-comment-above-attributes`. Having these values `false` by default prevents what many users would consider clean code, e.g. placing the `// SAFETY:` comment above a single-line functino call, rather than directly next to the argument. changelog: [`undocumented_unsafe_blocks`]: set `accept-comment-above-statement` and `accept-comment-above-attributes` to `true` by default. --- book/src/lint_configuration.md | 4 +- clippy_lints/src/utils/conf.rs | 4 +- .../undocumented_unsafe_blocks/clippy.toml | 2 - .../default/clippy.toml | 2 + .../disabled/clippy.toml | 3 + ...undocumented_unsafe_blocks.default.stderr} | 74 +-- ...ndocumented_unsafe_blocks.disabled.stderr} | 132 +++-- .../undocumented_unsafe_blocks.rs | 30 +- tests/ui/undocumented_unsafe_blocks.rs | 534 ------------------ 9 files changed, 153 insertions(+), 632 deletions(-) delete mode 100644 tests/ui-toml/undocumented_unsafe_blocks/clippy.toml create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml create mode 100644 tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml rename tests/ui-toml/undocumented_unsafe_blocks/{undocumented_unsafe_blocks.stderr => undocumented_unsafe_blocks.default.stderr} (83%) rename tests/{ui/undocumented_unsafe_blocks.stderr => ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr} (74%) delete mode 100644 tests/ui/undocumented_unsafe_blocks.rs diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 3aeb6206fca93..b980083f1f52a 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -703,7 +703,7 @@ Minimum chars an ident can have, anything below or equal to this will be linted. ## `accept-comment-above-statement` Whether to accept a safety comment to be placed above the statement containing the `unsafe` block -**Default Value:** `false` (`bool`) +**Default Value:** `true` (`bool`) --- **Affected lints:** @@ -713,7 +713,7 @@ Whether to accept a safety comment to be placed above the statement containing t ## `accept-comment-above-attributes` Whether to accept a safety comment to be placed above the attributes for the `unsafe` block -**Default Value:** `false` (`bool`) +**Default Value:** `true` (`bool`) --- **Affected lints:** diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e1280ef38ef71..75c3c7a958a21 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -542,11 +542,11 @@ define_Conf! { /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. /// /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block - (accept_comment_above_statement: bool = false), + (accept_comment_above_statement: bool = true), /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. /// /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block - (accept_comment_above_attributes: bool = false), + (accept_comment_above_attributes: bool = true), /// Lint: UNNECESSARY_RAW_STRING_HASHES. /// /// Whether to allow `r#""#` when `r""` can be used diff --git a/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml b/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml deleted file mode 100644 index e6dbb3d37841b..0000000000000 --- a/tests/ui-toml/undocumented_unsafe_blocks/clippy.toml +++ /dev/null @@ -1,2 +0,0 @@ -accept-comment-above-statement = true -accept-comment-above-attributes = true diff --git a/tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml b/tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml new file mode 100644 index 0000000000000..3b205d536f2ed --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/default/clippy.toml @@ -0,0 +1,2 @@ +# default configuration has `accept-comment-above-statement` and +# `accept-comment-above-attributes` true diff --git a/tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml b/tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml new file mode 100644 index 0000000000000..57ecb902d6545 --- /dev/null +++ b/tests/ui-toml/undocumented_unsafe_blocks/disabled/clippy.toml @@ -0,0 +1,3 @@ +# test with these options disabled +accept-comment-above-statement = false +accept-comment-above-attributes = false diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr similarity index 83% rename from tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr rename to tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr index 183c07fe786ce..15edf2a7dae47 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.default.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:263:19 + --> $DIR/undocumented_unsafe_blocks.rs:266:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {} = help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]` error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:267:5 + --> $DIR/undocumented_unsafe_blocks.rs:270:5 | LL | unsafe {} | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:271:14 + --> $DIR/undocumented_unsafe_blocks.rs:274:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:271:29 + --> $DIR/undocumented_unsafe_blocks.rs:274:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:271:48 + --> $DIR/undocumented_unsafe_blocks.rs:274:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:275:18 + --> $DIR/undocumented_unsafe_blocks.rs:278:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:275:37 + --> $DIR/undocumented_unsafe_blocks.rs:278:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:279:14 + --> $DIR/undocumented_unsafe_blocks.rs:282:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:284:19 + --> $DIR/undocumented_unsafe_blocks.rs:287:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:290:14 + --> $DIR/undocumented_unsafe_blocks.rs:293:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -81,7 +81,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:294:14 + --> $DIR/undocumented_unsafe_blocks.rs:297:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:298:13 + --> $DIR/undocumented_unsafe_blocks.rs:301:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -97,7 +97,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:308:8 + --> $DIR/undocumented_unsafe_blocks.rs:311:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -105,7 +105,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:314:13 + --> $DIR/undocumented_unsafe_blocks.rs:317:13 | LL | unsafe {} | ^^^^^^^^^ @@ -117,7 +117,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:322:5 + --> $DIR/undocumented_unsafe_blocks.rs:325:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:326:5 + --> $DIR/undocumented_unsafe_blocks.rs:329:5 | LL | unsafe { | ^^^^^^^^ @@ -133,7 +133,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:336:5 + --> $DIR/undocumented_unsafe_blocks.rs:339:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -141,7 +141,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:340:20 + --> $DIR/undocumented_unsafe_blocks.rs:343:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:347:5 + --> $DIR/undocumented_unsafe_blocks.rs:350:5 | LL | unsafe impl A for () {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL | unsafe impl A for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:354:9 + --> $DIR/undocumented_unsafe_blocks.rs:357:9 | LL | unsafe impl B for (u32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:375:13 + --> $DIR/undocumented_unsafe_blocks.rs:378:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:400:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:408:5 + --> $DIR/undocumented_unsafe_blocks.rs:411:5 | LL | unsafe impl T for (i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:400:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | no_safety_comment!(u32); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:414:5 + --> $DIR/undocumented_unsafe_blocks.rs:417:5 | LL | unsafe impl T for (bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:460:5 + --> $DIR/undocumented_unsafe_blocks.rs:463:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:464:19 + --> $DIR/undocumented_unsafe_blocks.rs:467:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:468:5 + --> $DIR/undocumented_unsafe_blocks.rs:471:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: constant item has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:472:5 + --> $DIR/undocumented_unsafe_blocks.rs:475:5 | LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:471:5 + --> $DIR/undocumented_unsafe_blocks.rs:474:5 | LL | // SAFETY: | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | // SAFETY: = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:473:5 + --> $DIR/undocumented_unsafe_blocks.rs:476:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:480:5 + --> $DIR/undocumented_unsafe_blocks.rs:483:5 | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:489:1 + --> $DIR/undocumented_unsafe_blocks.rs:492:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {} = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:502:5 + --> $DIR/undocumented_unsafe_blocks.rs:505:5 | LL | / let _ = { LL | | if unsafe { true } { @@ -291,13 +291,13 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:501:5 + --> $DIR/undocumented_unsafe_blocks.rs:504:5 | LL | // SAFETY: this is more than one level away, so it should warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:503:12 + --> $DIR/undocumented_unsafe_blocks.rs:506:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -305,7 +305,7 @@ LL | if unsafe { true } { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:506:23 + --> $DIR/undocumented_unsafe_blocks.rs:509:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr similarity index 74% rename from tests/ui/undocumented_unsafe_blocks.stderr rename to tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index 77f6aea2e0d07..cc9530f79b67c 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:262:19 + --> $DIR/undocumented_unsafe_blocks.rs:266:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -9,7 +9,7 @@ LL | /* Safety: */ unsafe {} = help: to override `-D warnings` add `#[allow(clippy::undocumented_unsafe_blocks)]` error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:266:5 + --> $DIR/undocumented_unsafe_blocks.rs:270:5 | LL | unsafe {} | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:14 + --> $DIR/undocumented_unsafe_blocks.rs:274:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:29 + --> $DIR/undocumented_unsafe_blocks.rs:274:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:270:48 + --> $DIR/undocumented_unsafe_blocks.rs:274:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:18 + --> $DIR/undocumented_unsafe_blocks.rs:278:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:274:37 + --> $DIR/undocumented_unsafe_blocks.rs:278:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -57,7 +57,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:278:14 + --> $DIR/undocumented_unsafe_blocks.rs:282:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:283:19 + --> $DIR/undocumented_unsafe_blocks.rs:287:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -73,7 +73,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:289:14 + --> $DIR/undocumented_unsafe_blocks.rs:293:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -81,7 +81,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:293:14 + --> $DIR/undocumented_unsafe_blocks.rs:297:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:297:13 + --> $DIR/undocumented_unsafe_blocks.rs:301:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -97,7 +97,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:307:8 + --> $DIR/undocumented_unsafe_blocks.rs:311:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -105,7 +105,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:313:13 + --> $DIR/undocumented_unsafe_blocks.rs:317:13 | LL | unsafe {} | ^^^^^^^^^ @@ -117,7 +117,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:321:5 + --> $DIR/undocumented_unsafe_blocks.rs:325:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -125,7 +125,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:325:5 + --> $DIR/undocumented_unsafe_blocks.rs:329:5 | LL | unsafe { | ^^^^^^^^ @@ -133,7 +133,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:335:5 + --> $DIR/undocumented_unsafe_blocks.rs:339:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -141,7 +141,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:339:20 + --> $DIR/undocumented_unsafe_blocks.rs:343:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:346:5 + --> $DIR/undocumented_unsafe_blocks.rs:350:5 | LL | unsafe impl A for () {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL | unsafe impl A for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:353:9 + --> $DIR/undocumented_unsafe_blocks.rs:357:9 | LL | unsafe impl B for (u32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:374:13 + --> $DIR/undocumented_unsafe_blocks.rs:378:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:399:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -189,7 +189,7 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:407:5 + --> $DIR/undocumented_unsafe_blocks.rs:411:5 | LL | unsafe impl T for (i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +197,7 @@ LL | unsafe impl T for (i32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:399:13 + --> $DIR/undocumented_unsafe_blocks.rs:403:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,7 +209,7 @@ LL | no_safety_comment!(u32); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:413:5 + --> $DIR/undocumented_unsafe_blocks.rs:417:5 | LL | unsafe impl T for (bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL | unsafe impl T for (bool) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:459:5 + --> $DIR/undocumented_unsafe_blocks.rs:463:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -225,7 +225,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:463:19 + --> $DIR/undocumented_unsafe_blocks.rs:467:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:467:5 + --> $DIR/undocumented_unsafe_blocks.rs:471:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,13 +241,13 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: constant item has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:471:5 + --> $DIR/undocumented_unsafe_blocks.rs:475:5 | LL | const BIG_NUMBER: i32 = 1000000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:470:5 + --> $DIR/undocumented_unsafe_blocks.rs:474:5 | LL | // SAFETY: | ^^^^^^^^^^ @@ -255,7 +255,7 @@ LL | // SAFETY: = help: to override `-D warnings` add `#[allow(clippy::unnecessary_safety_comment)]` error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:472:5 + --> $DIR/undocumented_unsafe_blocks.rs:476:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -263,7 +263,7 @@ LL | unsafe impl Interference for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:479:5 + --> $DIR/undocumented_unsafe_blocks.rs:483:5 | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -271,7 +271,7 @@ LL | unsafe impl ImplInFn for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:488:1 + --> $DIR/undocumented_unsafe_blocks.rs:492:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,7 +279,7 @@ LL | unsafe impl CrateRoot for () {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:498:9 + --> $DIR/undocumented_unsafe_blocks.rs:502:9 | LL | unsafe {}; | ^^^^^^^^^ @@ -287,7 +287,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:501:5 + --> $DIR/undocumented_unsafe_blocks.rs:505:5 | LL | / let _ = { LL | | if unsafe { true } { @@ -299,13 +299,13 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:500:5 + --> $DIR/undocumented_unsafe_blocks.rs:504:5 | LL | // SAFETY: this is more than one level away, so it should warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:502:12 + --> $DIR/undocumented_unsafe_blocks.rs:506:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL | if unsafe { true } { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:505:23 + --> $DIR/undocumented_unsafe_blocks.rs:509:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ @@ -321,7 +321,7 @@ LL | let bar = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:523:9 + --> $DIR/undocumented_unsafe_blocks.rs:527:9 | LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -329,7 +329,7 @@ LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:527:9 + --> $DIR/undocumented_unsafe_blocks.rs:531:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -337,12 +337,60 @@ LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:531:9 + --> $DIR/undocumented_unsafe_blocks.rs:535:9 + | +LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:541:5 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:545:5 + | +LL | unsafe { + | ^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:552:9 + | +LL | unsafe { a_function_with_a_very_long_name_to_break_the_line() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:557:9 + | +LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:563:9 | LL | unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 39 previous errors +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:568:5 + | +LL | unsafe {} + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 45 previous errors diff --git a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index b28e1b7d1802d..a278139876064 100644 --- a/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -1,4 +1,7 @@ //@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs +//@revisions: default disabled +//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/default +//@[disabled] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/undocumented_unsafe_blocks/disabled #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] #![allow(deref_nullptr, clippy::let_unit_value, clippy::missing_safety_doc)] @@ -491,7 +494,7 @@ unsafe impl CrateRoot for () {} // SAFETY: ok unsafe impl CrateRoot for (i32) {} -fn issue_9142() { +fn nested_block_separation_issue_9142() { // SAFETY: ok let _ = // we need this comment to avoid rustfmt putting @@ -518,49 +521,50 @@ pub const unsafe fn a_const_function_with_a_very_long_name_to_break_the_line() - 2 } -fn issue_10832() { - // Safety: A safety comment +fn separate_line_from_let_issue_10832() { + // SAFETY: fail ONLY if `accept-comment-above-statement = false` let _some_variable_with_a_very_long_name_to_break_the_line = unsafe { a_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Another safety comment + // SAFETY: fail ONLY if `accept-comment-above-statement = false` const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Yet another safety comment + // SAFETY: fail ONLY if `accept-comment-above-statement = false` static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; } -fn issue_8679() { - // SAFETY: +fn above_expr_attribute_issue_8679() { + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] unsafe {} - // SAFETY: + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[expect(unsafe_code, reason = "totally safe")] unsafe { *std::ptr::null::() }; - // Safety: A safety comment + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] let _some_variable_with_a_very_long_name_to_break_the_line = unsafe { a_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Another safety comment + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; - // Safety: Yet another safety comment + // SAFETY: fail ONLY if `accept-comment-above-attribute = false` #[allow(unsafe_code)] - static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = + #[allow(non_upper_case_globals)] + static _some_static_with_a_very_long_name_to_break_the_line: u32 = unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; // SAFETY: #[allow(unsafe_code)] - // This also works I guess + // This shouldn't work either unsafe {} } diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs deleted file mode 100644 index f4e7f1943ae6d..0000000000000 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ /dev/null @@ -1,534 +0,0 @@ -//@aux-build:proc_macro_unsafe.rs - -#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] -#![allow(clippy::let_unit_value, clippy::missing_safety_doc)] - -extern crate proc_macro_unsafe; - -// Valid comments - -fn nested_local() { - let _ = { - let _ = { - // SAFETY: - let _ = unsafe {}; - }; - }; -} - -fn deep_nest() { - let _ = { - let _ = { - // SAFETY: - let _ = unsafe {}; - - // Safety: - unsafe {}; - - let _ = { - let _ = { - let _ = { - let _ = { - let _ = { - // Safety: - let _ = unsafe {}; - - // SAFETY: - unsafe {}; - }; - }; - }; - - // Safety: - unsafe {}; - }; - }; - }; - - // Safety: - unsafe {}; - }; - - // SAFETY: - unsafe {}; -} - -fn local_tuple_expression() { - // Safety: - let _ = (42, unsafe {}); -} - -fn line_comment() { - // Safety: - unsafe {} -} - -fn line_comment_newlines() { - // SAFETY: - - unsafe {} -} - -fn line_comment_empty() { - // Safety: - // - // - // - unsafe {} -} - -fn line_comment_with_extras() { - // This is a description - // Safety: - unsafe {} -} - -fn block_comment() { - /* Safety: */ - unsafe {} -} - -fn block_comment_newlines() { - /* SAFETY: */ - - unsafe {} -} - -fn block_comment_with_extras() { - /* This is a description - * SAFETY: - */ - unsafe {} -} - -fn block_comment_terminator_same_line() { - /* This is a description - * Safety: */ - unsafe {} -} - -fn buried_safety() { - // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation - // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in - // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est - // laborum. Safety: - // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi - // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio - // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl - // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. - unsafe {} -} - -fn safety_with_prepended_text() { - // This is a test. safety: - unsafe {} -} - -fn local_line_comment() { - // Safety: - let _ = unsafe {}; -} - -fn local_block_comment() { - /* SAFETY: */ - let _ = unsafe {}; -} - -fn comment_array() { - // Safety: - let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; -} - -fn comment_tuple() { - // sAFETY: - let _ = (42, unsafe {}, "test", unsafe {}); -} - -fn comment_unary() { - // SAFETY: - let _ = *unsafe { &42 }; -} - -#[allow(clippy::match_single_binding)] -fn comment_match() { - // SAFETY: - let _ = match unsafe {} { - _ => {}, - }; -} - -fn comment_addr_of() { - // Safety: - let _ = &unsafe {}; -} - -fn comment_repeat() { - // Safety: - let _ = [unsafe {}; 5]; -} - -fn comment_macro_call() { - macro_rules! t { - ($b:expr) => { - $b - }; - } - - t!( - // SAFETY: - unsafe {} - ); -} - -fn comment_macro_def() { - macro_rules! t { - () => { - // Safety: - unsafe {} - }; - } - - t!(); -} - -fn non_ascii_comment() { - // ॐ᧻໒ SaFeTy: ௵∰ - unsafe {}; -} - -fn local_commented_block() { - let _ = - // safety: - unsafe {}; -} - -fn local_nest() { - // safety: - let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})]; -} - -fn in_fn_call(x: *const u32) { - fn f(x: u32) {} - - // Safety: reason - f(unsafe { *x }); -} - -fn multi_in_fn_call(x: *const u32) { - fn f(x: u32, y: u32) {} - - // Safety: reason - f(unsafe { *x }, unsafe { *x }); -} - -fn in_multiline_fn_call(x: *const u32) { - fn f(x: u32, y: u32) {} - - f( - // Safety: reason - unsafe { *x }, - 0, - ); -} - -fn in_macro_call(x: *const u32) { - // Safety: reason - println!("{}", unsafe { *x }); -} - -fn in_multiline_macro_call(x: *const u32) { - println!( - "{}", - // Safety: reason - unsafe { *x }, - ); -} - -fn from_proc_macro() { - proc_macro_unsafe::unsafe_block!(token); -} - -fn in_closure(x: *const u32) { - // Safety: reason - let _ = || unsafe { *x }; -} - -// Invalid comments - -#[rustfmt::skip] -fn inline_block_comment() { - /* Safety: */ unsafe {} -} - -fn no_comment() { - unsafe {} -} - -fn no_comment_array() { - let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; -} - -fn no_comment_tuple() { - let _ = (42, unsafe {}, "test", unsafe {}); -} - -fn no_comment_unary() { - let _ = *unsafe { &42 }; -} - -#[allow(clippy::match_single_binding)] -fn no_comment_match() { - let _ = match unsafe {} { - _ => {}, - }; -} - -fn no_comment_addr_of() { - let _ = &unsafe {}; -} - -fn no_comment_repeat() { - let _ = [unsafe {}; 5]; -} - -fn local_no_comment() { - let _ = unsafe {}; -} - -fn no_comment_macro_call() { - macro_rules! t { - ($b:expr) => { - $b - }; - } - - t!(unsafe {}); -} - -fn no_comment_macro_def() { - macro_rules! t { - () => { - unsafe {} - }; - } - - t!(); -} - -fn trailing_comment() { - unsafe {} // SAFETY: -} - -fn internal_comment() { - unsafe { - // SAFETY: - } -} - -fn interference() { - // SAFETY - - let _ = 42; - - unsafe {}; -} - -pub fn print_binary_tree() { - println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); -} - -mod unsafe_impl_smoke_test { - unsafe trait A {} - - // error: no safety comment - unsafe impl A for () {} - - // Safety: ok - unsafe impl A for (i32) {} - - mod sub_mod { - // error: - unsafe impl B for (u32) {} - unsafe trait B {} - } - - #[rustfmt::skip] - mod sub_mod2 { - // - // SAFETY: ok - // - - unsafe impl B for (u32) {} - unsafe trait B {} - } -} - -mod unsafe_impl_from_macro { - unsafe trait T {} - - // error - macro_rules! no_safety_comment { - ($t:ty) => { - unsafe impl T for $t {} - }; - } - - // ok - no_safety_comment!(()); - - // ok - macro_rules! with_safety_comment { - ($t:ty) => { - // SAFETY: - unsafe impl T for $t {} - }; - } - - // ok - with_safety_comment!((i32)); -} - -mod unsafe_impl_macro_and_not_macro { - unsafe trait T {} - - // error - macro_rules! no_safety_comment { - ($t:ty) => { - unsafe impl T for $t {} - }; - } - - // ok - no_safety_comment!(()); - - // error - unsafe impl T for (i32) {} - - // ok - no_safety_comment!(u32); - - // error - unsafe impl T for (bool) {} -} - -#[rustfmt::skip] -mod unsafe_impl_valid_comment { - unsafe trait SaFety {} - // SaFety: - unsafe impl SaFety for () {} - - unsafe trait MultiLineComment {} - // The following impl is safe - // ... - // Safety: reason - unsafe impl MultiLineComment for () {} - - unsafe trait NoAscii {} - // 安全 SAFETY: 以下のコードは安全です - unsafe impl NoAscii for () {} - - unsafe trait InlineAndPrecedingComment {} - // SAFETY: - /* comment */ unsafe impl InlineAndPrecedingComment for () {} - - unsafe trait BuriedSafety {} - // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation - // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in - // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est - // laborum. Safety: - // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi - // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio - // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl - // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. - unsafe impl BuriedSafety for () {} - - unsafe trait MultiLineBlockComment {} - /* This is a description - * Safety: */ - unsafe impl MultiLineBlockComment for () {} -} - -#[rustfmt::skip] -mod unsafe_impl_invalid_comment { - unsafe trait NoComment {} - - unsafe impl NoComment for () {} - - unsafe trait InlineComment {} - - /* SAFETY: */ unsafe impl InlineComment for () {} - - unsafe trait TrailingComment {} - - unsafe impl TrailingComment for () {} // SAFETY: - - unsafe trait Interference {} - // SAFETY: - const BIG_NUMBER: i32 = 1000000; - unsafe impl Interference for () {} -} - -unsafe trait ImplInFn {} - -fn impl_in_fn() { - // error - unsafe impl ImplInFn for () {} - - // SAFETY: ok - unsafe impl ImplInFn for (i32) {} -} - -unsafe trait CrateRoot {} - -// error -unsafe impl CrateRoot for () {} - -// SAFETY: ok -unsafe impl CrateRoot for (i32) {} - -fn issue_9142() { - // SAFETY: ok - let _ = - // we need this comment to avoid rustfmt putting - // it all on one line - unsafe {}; - - // SAFETY: this is more than one level away, so it should warn - let _ = { - if unsafe { true } { - todo!(); - } else { - let bar = unsafe {}; - todo!(); - bar - } - }; -} - -pub unsafe fn a_function_with_a_very_long_name_to_break_the_line() -> u32 { - 1 -} - -pub const unsafe fn a_const_function_with_a_very_long_name_to_break_the_line() -> u32 { - 2 -} - -fn issue_10832() { - // Safety: A safety comment. But it will warn anyways - let _some_variable_with_a_very_long_name_to_break_the_line = - unsafe { a_function_with_a_very_long_name_to_break_the_line() }; - - // Safety: Another safety comment. But it will warn anyways - const _SOME_CONST_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = - unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; - - // Safety: Yet another safety comment. But it will warn anyways - static _SOME_STATIC_WITH_A_VERY_LONG_NAME_TO_BREAK_THE_LINE: u32 = - unsafe { a_const_function_with_a_very_long_name_to_break_the_line() }; -} - -fn main() {} From 01c25a8eb63f8da0453d1aedca782f477342ef2d Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 20 Sep 2023 13:41:20 +0000 Subject: [PATCH 58/78] Remove most usage of `hir_ty_to_ty` --- .../src/default_union_representation.rs | 22 ++++++++++--------- clippy_lints/src/error_impl_error.rs | 8 +++---- clippy_lints/src/large_const_arrays.rs | 5 ++--- clippy_lints/src/loops/utils.rs | 3 +-- clippy_lints/src/matches/needless_match.rs | 17 ++++++++------ clippy_lints/src/methods/mod.rs | 13 ++++++----- clippy_lints/src/missing_const_for_fn.rs | 12 +++++----- clippy_lints/src/no_effect.rs | 11 ++++------ clippy_lints/src/non_canonical_impls.rs | 14 +++--------- clippy_lints/src/non_copy_const.rs | 15 ++++++------- clippy_lints/src/ptr.rs | 21 ++++++------------ .../src/utils/internal_lints/invalid_paths.rs | 18 +++++---------- .../utils/internal_lints/msrv_attr_impl.rs | 15 +++++-------- tests/ui/no_effect_return.rs | 1 + tests/ui/no_effect_return.stderr | 8 +++---- 15 files changed, 78 insertions(+), 105 deletions(-) diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index bbce6e1dd8f2b..63ec819502088 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{self as hir, HirId, Item, ItemKind}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, FieldDef, GenericArg, List}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -52,7 +52,10 @@ declare_lint_pass!(DefaultUnionRepresentation => [DEFAULT_UNION_REPRESENTATION]) impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if is_union_with_two_non_zst_fields(cx, item) && !has_c_repr_attr(cx, item.hir_id()) { + if !item.span.from_expansion() + && is_union_with_two_non_zst_fields(cx, item) + && !has_c_repr_attr(cx, item.hir_id()) + { span_lint_and_help( cx, DEFAULT_UNION_REPRESENTATION, @@ -73,18 +76,17 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { /// if there is only one field left after ignoring ZST fields then the offset /// of that field does not matter either.) fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if let ItemKind::Union(data, _) = &item.kind { - data.fields().iter().filter(|f| !is_zst(cx, f.ty)).count() >= 2 + if let ItemKind::Union(..) = &item.kind + && let ty::Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() + { + adt_def.all_fields().filter(|f| !is_zst(cx, f, args)).count() >= 2 } else { false } } -fn is_zst(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) -> bool { - if hir_ty.span.from_expansion() { - return false; - } - let ty = hir_ty_to_ty(cx.tcx, hir_ty); +fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: &'tcx List>) -> bool { + let ty = field.ty(cx.tcx, args); if let Ok(layout) = cx.layout_of(ty) { layout.is_zst() } else { diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 379af9b2234c2..f24577c738229 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -3,7 +3,6 @@ use clippy_utils::path_res; use clippy_utils::ty::implements_trait; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{Item, ItemKind}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Visibility; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -42,9 +41,10 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { }; match item.kind { - ItemKind::TyAlias(ty, _) if implements_trait(cx, hir_ty_to_ty(cx.tcx, ty), error_def_id, &[]) - && item.ident.name == sym::Error - && is_visible_outside_module(cx, item.owner_id.def_id) => + ItemKind::TyAlias(..) if item.ident.name == sym::Error + && is_visible_outside_module(cx, item.owner_id.def_id) + && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && implements_trait(cx, ty, error_def_id, &[]) => { span_lint( cx, diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 9b26c3573e18f..a4f3d49834531 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; @@ -50,12 +49,12 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { if !item.span.from_expansion(); - if let ItemKind::Const(hir_ty, generics, _) = &item.kind; + if let ItemKind::Const(_, generics, _) = &item.kind; // Since static items may not have generics, skip generic const items. // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it // doesn't account for empty where-clauses that only consist of keyword `where` IINM. if generics.params.is_empty() && !generics.has_where_clause_predicates; - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); if let ty::Array(element_type, cst) = ty.kind(); if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx); diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 6edca2d55f649..0a2bd89eb3cd3 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -5,7 +5,6 @@ use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -150,7 +149,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if l.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = l.pat.kind; then { - let ty = l.ty.map(|ty| hir_ty_to_ty(self.cx.tcx, ty)); + let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat)); self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| { InitializeVisitorState::Initialized { diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index c4f6852aedc3d..44dc29c36a6ba 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -8,8 +8,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, Guard, ItemKind, Node, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -141,11 +140,15 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` - Node::Item(..) => { - if let Some(fn_decl) = p_node.fn_decl() { - if let FnRetTy::Return(ret_ty) = fn_decl.output { - return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr)); - } + Node::Item(item) => { + if let ItemKind::Fn(..) = item.kind { + let output = cx + .tcx + .fn_sig(item.owner_id) + .instantiate_identity() + .output() + .skip_binder(); + return same_type_and_consts(output, cx.typeck_results().expr_ty(expr)); } }, // check the parent expr for this whole block `{ match match_expr {..} }` diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bd07e6cbf2e2c..e7fcef9e9de27 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -126,7 +126,6 @@ pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; @@ -3926,18 +3925,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if_chain! { if let TraitItemKind::Fn(ref sig, _) = item.kind; if sig.decl.implicit_self.has_implicit_self(); - if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); - + if let Some(first_arg_hir_ty) = sig.decl.inputs.first(); + if let Some(&first_arg_ty) = cx.tcx.fn_sig(item.owner_id) + .instantiate_identity() + .inputs() + .skip_binder() + .first(); then { - let first_arg_span = first_arg_ty.span; - let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()).self_ty(); wrong_self_convention::check( cx, item.ident.name.as_str(), self_ty, first_arg_ty, - first_arg_span, + first_arg_hir_ty.span, false, true, ); diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 3b7eccad79df8..f598a65d2e488 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -7,7 +7,6 @@ use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind}; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -124,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { FnKind::Method(_, sig, ..) => { if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) - || method_accepts_droppable(cx, sig.decl.inputs) + || method_accepts_droppable(cx, def_id) { return; } @@ -165,12 +164,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { /// Returns true if any of the method parameters is a type that implements `Drop`. The method /// can't be made const then, because `drop` can't be const-evaluated. -fn method_accepts_droppable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool { +fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + // If any of the params are droppable, return true - param_tys.iter().any(|hir_ty| { - let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty); - has_drop(cx, ty_ty) - }) + sig.inputs().iter().any(|&ty| has_drop(cx, ty)) } // We don't have to lint on something that's already `const` diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 5f2a324b05fb9..aee184252fbfd 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -5,10 +5,8 @@ use clippy_utils::{get_parent_node, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, FnRetTy, ItemKind, Node, PatKind, Stmt, StmtKind, - UnsafeSource, + is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, ItemKind, Node, PatKind, Stmt, StmtKind, UnsafeSource, }; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -99,14 +97,13 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { |diag| { for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { if let Node::Item(item) = parent.1 - && let ItemKind::Fn(sig, ..) = item.kind - && let FnRetTy::Return(ret_ty) = sig.decl.output + && let ItemKind::Fn(..) = item.kind && let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) && let [.., final_stmt] = block.stmts && final_stmt.hir_id == stmt.hir_id { let expr_ty = cx.typeck_results().expr_ty(expr); - let mut ret_ty = hir_ty_to_ty(cx.tcx, ret_ty); + let mut ret_ty = cx.tcx.fn_sig(item.owner_id).instantiate_identity().output().skip_binder(); // Remove `impl Future` to get `T` if cx.tcx.ty_is_opaque_future(ret_ty) && @@ -115,7 +112,7 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { ret_ty = true_ret_ty; } - if ret_ty == expr_ty { + if !ret_ty.is_unit() && ret_ty == expr_ty { diag.span_suggestion( stmt.span.shrink_to_lo(), "did you mean to return it?", diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index c390e6913ffb0..20b4b4f03ed47 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -4,8 +4,7 @@ use clippy_utils::ty::implements_trait; use clippy_utils::{get_parent_node, is_res_lang_ctor, last_path_segment, match_def_path, path_res, std_or_core}; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, ItemKind, LangItem, Node, UnOp}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, LangItem, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::EarlyBinder; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -123,9 +122,6 @@ impl LateLintPass<'_> for NonCanonicalImpls { if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) { return; } - let ItemKind::Impl(imp) = item.kind else { - return; - }; let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else { return; }; @@ -181,12 +177,8 @@ impl LateLintPass<'_> for NonCanonicalImpls { if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id) && impl_item.ident.name == sym::partial_cmp - && let Some(ord_def_id) = cx - .tcx - .diagnostic_items(trait_impl.def_id.krate) - .name_to_id - .get(&sym::Ord) - && implements_trait(cx, hir_ty_to_ty(cx.tcx, imp.self_ty), *ord_def_id, &[]) + && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) { // If the `cmp` call likely needs to be fully qualified in the suggestion // (like `std::cmp::Ord::cmp`). It's unfortunate we must put this here but we can't diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 243192385c256..e106a959165ef 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -13,7 +13,6 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, }; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; use rustc_middle::ty::adjustment::Adjust; @@ -297,8 +296,8 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(hir_ty, _generics, body_id) = it.kind { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if let ItemKind::Const(.., body_id) = it.kind { + let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) { lint(cx, Source::Item { item: it.span }); } @@ -306,8 +305,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(hir_ty, body_id_opt) = &trait_item.kind { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind { + let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity(); // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound. @@ -333,7 +332,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(hir_ty, body_id) = &impl_item.kind { + if let ImplItemKind::Const(_, body_id) = &impl_item.kind { let item_def_id = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir().expect_item(item_def_id); @@ -366,7 +365,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { // we should use here as a frozen variant is a potential to be frozen // similar to unknown layouts. // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); if is_unfrozen(cx, normalized); if is_value_unfrozen_poly(cx, *body_id, normalized); @@ -381,7 +380,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } }, ItemKind::Impl(Impl { of_trait: None, .. }) => { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity(); // Normalize assoc types originated from generic params. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index bf031ac84549d..7dabdcd58ec2c 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -16,7 +16,6 @@ use rustc_hir::{ ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety, }; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_lint::{LateContext, LateLintPass}; @@ -172,13 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { for arg in check_fn_args( cx, - cx.tcx - .fn_sig(item.owner_id) - .instantiate_identity() - .skip_binder() - .inputs(), + cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder(), sig.decl.inputs, - &sig.decl.output, &[], ) .filter(|arg| arg.mutability() == Mutability::Not) @@ -237,7 +231,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { let decl = sig.decl; let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder(); - let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, &decl.output, body.params) + let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params) .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) .collect(); let results = check_ptr_arg_usage(cx, body, &lint_args); @@ -443,12 +437,13 @@ impl<'tcx> DerefTy<'tcx> { #[expect(clippy::too_many_lines)] fn check_fn_args<'cx, 'tcx: 'cx>( cx: &'cx LateContext<'tcx>, - tys: &'tcx [Ty<'tcx>], + fn_sig: ty::FnSig<'tcx>, hir_tys: &'tcx [hir::Ty<'tcx>], - ret_ty: &'tcx FnRetTy<'tcx>, params: &'tcx [Param<'tcx>], ) -> impl Iterator> + 'cx { - tys.iter() + fn_sig + .inputs() + .iter() .zip(hir_tys.iter()) .enumerate() .filter_map(move |(i, (ty, hir_ty))| { @@ -494,9 +489,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }) { if !lifetime.is_anonymous() - && let FnRetTy::Return(ret_ty) = ret_ty - && let ret_ty = hir_ty_to_ty(cx.tcx, ret_ty) - && ret_ty + && fn_sig.output() .walk() .filter_map(|arg| { arg.as_region().and_then(|lifetime| { diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 4ed985f54d0e2..250772238853b 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -5,10 +5,9 @@ use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::Item; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, FloatTy}; +use rustc_middle::ty::FloatTy; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; @@ -34,25 +33,20 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); if_chain! { if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, _, body_id) = item.kind; - let ty = hir_ty_to_ty(cx.tcx, ty); - if let ty::Array(el_ty, _) = &ty.kind(); - if let ty::Ref(_, el_ty, _) = &el_ty.kind(); - if el_ty.is_str(); + if let hir::ItemKind::Const(.., body_id) = item.kind; let body = cx.tcx.hir().body(body_id); let typeck_results = cx.tcx.typeck_body(body_id); if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - let path: Vec<&str> = path + if let Some(path) = path .iter() .map(|x| { if let Constant::Str(s) = x { - s.as_str() + Some(s.as_str()) } else { - // We checked the type of the constant above - unreachable!() + None } }) - .collect(); + .collect::>>(); if !check_path(cx, &path[..]); then { span_lint(cx, INVALID_PATHS, item.span, "invalid path"); diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index bf835f89cfc7f..86b77a77f1730 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -5,9 +5,8 @@ use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{self, GenericArgKind}; +use rustc_middle::ty::{self, EarlyBinder, GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -25,16 +24,14 @@ impl LateLintPass<'_> for MsrvAttrImpl { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { if_chain! { if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(lint_pass_trait_ref), - self_ty, + of_trait: Some(_), items, .. }) = &item.kind; - if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); - let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); - let self_ty = hir_ty_to_ty(cx.tcx, self_ty); - if let ty::Adt(self_ty_def, _) = self_ty.kind(); + if let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity); + let is_late_pass = match_def_path(cx, trait_ref.def_id, &paths::LATE_LINT_PASS); + if is_late_pass || match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS); + if let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind(); if self_ty_def.is_struct(); if self_ty_def.all_fields().any(|f| { cx.tcx diff --git a/tests/ui/no_effect_return.rs b/tests/ui/no_effect_return.rs index f6585aa30a6f0..e46c0d73518b9 100644 --- a/tests/ui/no_effect_return.rs +++ b/tests/ui/no_effect_return.rs @@ -76,6 +76,7 @@ fn h() -> Vec { fn i() -> () { { + // does not suggest on function with explicit unit return type (); //~^ ERROR: statement with no effect } diff --git a/tests/ui/no_effect_return.stderr b/tests/ui/no_effect_return.stderr index b036e63420474..aed079f09b986 100644 --- a/tests/ui/no_effect_return.stderr +++ b/tests/ui/no_effect_return.stderr @@ -54,15 +54,13 @@ LL | ControlFlow::Break::<()>(()); | help: did you mean to return it?: `return` error: statement with no effect - --> $DIR/no_effect_return.rs:79:9 + --> $DIR/no_effect_return.rs:80:9 | LL | (); - | -^^ - | | - | help: did you mean to return it?: `return` + | ^^^ error: statement with no effect - --> $DIR/no_effect_return.rs:88:9 + --> $DIR/no_effect_return.rs:89:9 | LL | (); | ^^^ From 2ea6ac5673931cd0804507ebe57452914d76f552 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 20 Sep 2023 20:51:14 +0200 Subject: [PATCH 59/78] rename mir::Constant -> mir::ConstOperand, mir::ConstKind -> mir::Const --- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/matches/overlapping_arms.rs | 4 ++-- clippy_utils/src/consts.rs | 20 ++++++++++---------- clippy_utils/src/lib.rs | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 96c5c7fc50931..3f60e5a7c4d64 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -50,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .tcx .const_eval_poly(def_id.to_def_id()) .ok() - .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty)); + .map(|val| rustc_middle::mir::Const::from_value(val, ty)); if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx, c)) { if let ty::Adt(adt, _) = ty.kind() { if adt.is_enum() { diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs index 6e13148f2fc18..7c0485914b83b 100644 --- a/clippy_lints/src/matches/overlapping_arms.rs +++ b/clippy_lints/src/matches/overlapping_arms.rs @@ -37,14 +37,14 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) Some(lhs) => constant(cx, cx.typeck_results(), lhs)?, None => { let min_val_const = ty.numeric_min_val(cx.tcx)?; - miri_to_const(cx, mir::ConstantKind::from_ty_const(min_val_const, cx.tcx))? + miri_to_const(cx, mir::Const::from_ty_const(min_val_const, cx.tcx))? }, }; let rhs_const = match rhs { Some(rhs) => constant(cx, cx.typeck_results(), rhs)?, None => { let max_val_const = ty.numeric_max_val(cx.tcx)?; - miri_to_const(cx, mir::ConstantKind::from_ty_const(max_val_const, cx.tcx))? + miri_to_const(cx, mir::Const::from_ty_const(max_val_const, cx.tcx))? }, }; let lhs_val = lhs_const.int_value(cx, ty)?; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index c88fce22f7fb6..a136de8624089 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -21,7 +21,7 @@ use std::iter; /// A `LitKind`-like enum to fold constant `Expr`s into. #[derive(Debug, Clone)] pub enum Constant<'tcx> { - Adt(rustc_middle::mir::ConstantKind<'tcx>), + Adt(rustc_middle::mir::Const<'tcx>), /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). @@ -482,7 +482,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .tcx .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None) .ok() - .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?; + .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; let result = miri_to_const(self.lcx, result)?; self.source = ConstantSource::Constant; Some(result) @@ -655,10 +655,10 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } -pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'tcx>) -> Option> { +pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option> { use rustc_middle::mir::ConstValue; match result { - mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { + mir::Const::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))), @@ -671,11 +671,11 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), _ => None, }, - mir::ConstantKind::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => { + mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => { let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; String::from_utf8(data.to_owned()).ok().map(Constant::Str) } - mir::ConstantKind::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { + mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => { let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory(); match result.ty().kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)), @@ -714,17 +714,17 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, lcx: &LateContext<'tcx>, - result: mir::ConstantKind<'tcx>, + result: mir::Const<'tcx>, field: &Ident, -) -> Option> { - if let mir::ConstantKind::Val(result, ty) = result +) -> Option> { + if let mir::Const::Val(result, ty) = result && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_diagnostics((result, ty)) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) && let Some(&(val, ty)) = dc.fields.get(field_idx) { - Some(mir::ConstantKind::Val(val, ty)) + Some(mir::Const::Val(val, ty)) } else { None diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 4ef3ec19647e9..1e464db8087e5 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -97,7 +97,7 @@ use rustc_hir::{ use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; -use rustc_middle::mir::ConstantKind; +use rustc_middle::mir::Const; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::fast_reject::SimplifiedType; @@ -1510,7 +1510,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let bnd_ty = subst.type_at(0) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree())) - && let min_const_kind = ConstantKind::from_value(const_val, bnd_ty) + && let min_const_kind = Const::from_value(const_val, bnd_ty) && let Some(min_const) = miri_to_const(cx, min_const_kind) && let Some(start_const) = constant(cx, cx.typeck_results(), start) { @@ -1526,7 +1526,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti && let bnd_ty = subst.type_at(0) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) && let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree())) - && let max_const_kind = ConstantKind::from_value(const_val, bnd_ty) + && let max_const_kind = Const::from_value(const_val, bnd_ty) && let Some(max_const) = miri_to_const(cx, max_const_kind) && let Some(end_const) = constant(cx, cx.typeck_results(), end) { From 238dc2828ed4b2ce49a95b8093486cd01c528af9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 18 Sep 2023 15:30:07 +0000 Subject: [PATCH 60/78] Prevent promotion of const fn calls in inline consts --- clippy_lints/src/operators/arithmetic_side_effects.rs | 2 +- clippy_lints/src/operators/numeric_arithmetic.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 8072aded8513b..a10aa65e59482 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -315,7 +315,7 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { let body_owner_def_id = cx.tcx.hir().body_owner_def_id(body.id()); let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id); - if let hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) = body_owner_kind { + if let hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) = body_owner_kind { let body_span = cx.tcx.hir().span_with_body(body_owner); if let Some(span) = self.const_span && span.contains(body_span) { return; diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index 102845ceed095..80389cbf84be2 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -72,7 +72,7 @@ impl Context { let body_owner_def_id = cx.tcx.hir().body_owner_def_id(body.id()); match cx.tcx.hir().body_owner_kind(body_owner_def_id) { - hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => { + hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const { .. } => { let body_span = cx.tcx.hir().span_with_body(body_owner); if let Some(span) = self.const_span { From 823bcb478bc55cacee0dcc1a9cc2b70decfa1d39 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 14 Sep 2023 22:38:07 +0000 Subject: [PATCH 61/78] Record asyncness span in HIR --- clippy_lints/src/redundant_closure_call.rs | 7 ++++--- clippy_utils/src/lib.rs | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index fc49b58e0a777..f42836611ca53 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -10,6 +10,7 @@ use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -84,7 +85,7 @@ fn find_innermost_closure<'tcx>( cx: &LateContext<'tcx>, mut expr: &'tcx hir::Expr<'tcx>, mut steps: usize, -) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, hir::IsAsync)> { +) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, ty::Asyncness)> { let mut data = None; while let hir::ExprKind::Closure(closure) = expr.kind @@ -98,9 +99,9 @@ fn find_innermost_closure<'tcx>( { expr = body.value; data = Some((body.value, closure.fn_decl, if is_async_closure(body) { - hir::IsAsync::Async + ty::Asyncness::Yes } else { - hir::IsAsync::NotAsync + ty::Asyncness::No })); steps -= 1; } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1e464db8087e5..be1c46319c2b6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -90,7 +90,7 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, - ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, + ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; @@ -1958,8 +1958,8 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { match kind { - FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async, - FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async, + FnKind::ItemFn(_, _, header) => header.asyncness .is_async(), + FnKind::Method(_, sig) => sig.header.asyncness.is_async(), FnKind::Closure => false, } } From 1fac304bf53f35f2088fad9b40cd919cd7a485d9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Sep 2023 08:36:50 +0200 Subject: [PATCH 62/78] adjust how closure/generator types and rvalues are printed --- tests/ui/box_default.fixed | 2 +- tests/ui/box_default.rs | 2 +- tests/ui/crashes/ice-6251.stderr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index 22c034c88ab1c..69cabcb32d333 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -36,7 +36,7 @@ fn main() { issue_10381(); // `Box::>::default()` would be valid here, but not `Box::default()` or - // `Box::::default()` + // `Box::::default()` // // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 let mut unnameable = Box::new(Option::default()); diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index 89e3888ba211a..48fa8bc33bc2d 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -36,7 +36,7 @@ fn main() { issue_10381(); // `Box::>::default()` would be valid here, but not `Box::default()` or - // `Box::::default()` + // `Box::::default()` // // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 let mut unnameable = Box::new(Option::default()); diff --git a/tests/ui/crashes/ice-6251.stderr b/tests/ui/crashes/ice-6251.stderr index 68a5766c90c0e..11081dc8087e2 100644 --- a/tests/ui/crashes/ice-6251.stderr +++ b/tests/ui/crashes/ice-6251.stderr @@ -27,7 +27,7 @@ LL | fn bug() -> impl Iterator { | ^^^^^^^^^^^ expected `usize`, found closure | = note: expected type `usize` - found closure `[closure@$DIR/ice-6251.rs:4:44: 4:53]` + found closure `{closure@$DIR/ice-6251.rs:4:44: 4:53}` error: aborting due to 3 previous errors From 74573fbab98fe3eeaef63ada418613bff0a77c62 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 22 Sep 2023 00:05:45 +0000 Subject: [PATCH 63/78] fix clippy errors (ignore effects in certainty) --- clippy_utils/src/ty/type_certainty/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index dc7756533acb0..4b06b12fb94d0 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -207,7 +207,8 @@ fn path_segment_certainty( // Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE. if cx.tcx.res_generics_def_id(path_segment.res).is_some() { let generics = cx.tcx.generics_of(def_id); - let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && generics.params.is_empty() + let count = generics.params.len() - generics.host_effect_index.is_some() as usize; + let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 { Certainty::Certain(None) } else { @@ -299,7 +300,7 @@ fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> b // Check that all type parameters appear in the functions input types. (0..(generics.parent_count + generics.params.len()) as u32).all(|index| { - fn_sig + Some(index as usize) == generics.host_effect_index || fn_sig .inputs() .iter() .any(|input_ty| contains_param(*input_ty.skip_binder(), index)) From 5ee167e00d0c0563976239416ec344e7914455bd Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 22 Sep 2023 09:14:39 +0000 Subject: [PATCH 64/78] Add a way to decouple the implementation and the declaration of a TyCtxt method. --- clippy_utils/src/consts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index a136de8624089..6b1a738aaa94c 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -718,7 +718,7 @@ fn field_of_struct<'tcx>( field: &Ident, ) -> Option> { if let mir::Const::Val(result, ty) = result - && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_diagnostics((result, ty)) + && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_diagnostics(result, ty) && let Some(dc_variant) = dc.variant && let Some(variant) = adt_def.variants().get(dc_variant) && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) From 85801f55eff002a63db51125e9bc20aab1d4982e Mon Sep 17 00:00:00 2001 From: pc-linux Date: Fri, 22 Sep 2023 16:54:21 +0200 Subject: [PATCH 65/78] fixed fp caused by moving &mut reference inside of a closure --- clippy_lints/src/needless_pass_by_ref_mut.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 36ca61c0b44d3..57652e5ff546b 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -336,7 +336,12 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId, borrow: ty::BorrowKind) { self.prev_bind = None; if let euv::Place { - base: euv::PlaceBase::Local(vid), + base: + euv::PlaceBase::Local(vid) + | euv::PlaceBase::Upvar(UpvarId { + var_path: UpvarPath { hir_id: vid }, + .. + }), base_ty, .. } = &cmt.place From f3a27d20f60a7b0f23b9256ef6f405c4da8106fd Mon Sep 17 00:00:00 2001 From: jonboh Date: Fri, 22 Sep 2023 17:18:49 +0200 Subject: [PATCH 66/78] prevent ice when threshold is 0 and enum has no variants --- clippy_lints/src/enum_variants.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index d4df6f7aa2d09..85334d09d4b4a 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -167,7 +167,10 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n return; } - let first = &def.variants[0].ident.name.as_str(); + let first = match &def.variants.get(0) { + Some(variant) => variant.ident.name.as_str(), + None => return, + }; let mut pre = camel_case_split(first); let mut post = pre.clone(); post.reverse(); From ab51f66ec087bfa0159ae19c699cb65049e48efa Mon Sep 17 00:00:00 2001 From: pc-linux Date: Fri, 22 Sep 2023 18:04:57 +0200 Subject: [PATCH 67/78] added tests --- tests/ui/needless_pass_by_ref_mut.rs | 12 ++++++++++++ tests/ui/needless_pass_by_ref_mut.stderr | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/ui/needless_pass_by_ref_mut.rs b/tests/ui/needless_pass_by_ref_mut.rs index da2a72caacb4e..9cddcb3df2378 100644 --- a/tests/ui/needless_pass_by_ref_mut.rs +++ b/tests/ui/needless_pass_by_ref_mut.rs @@ -230,6 +230,18 @@ async fn async_vec(b: &mut Vec) { async fn async_vec2(b: &mut Vec) { b.push(true); } +fn non_mut(n: &str) {} +//Should warn +pub async fn call_in_closure1(n: &mut str) { + (|| non_mut(n))() +} +fn str_mut(str: &mut String) -> bool { + str.pop().is_some() +} +//Should not warn +pub async fn call_in_closure2(str: &mut String) { + (|| str_mut(str))(); +} // Should not warn. pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { diff --git a/tests/ui/needless_pass_by_ref_mut.stderr b/tests/ui/needless_pass_by_ref_mut.stderr index 0fb9458d79488..0c7fbd5df6d9d 100644 --- a/tests/ui/needless_pass_by_ref_mut.stderr +++ b/tests/ui/needless_pass_by_ref_mut.stderr @@ -108,7 +108,15 @@ LL | async fn inner_async3(x: &mut i32, y: &mut u32) { | ^^^^^^^^ help: consider changing to: `&i32` error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:235:25 + --> $DIR/needless_pass_by_ref_mut.rs:235:34 + | +LL | pub async fn call_in_closure1(n: &mut str) { + | ^^^^^^^^ help: consider changing to: `&str` + | + = warning: changing this function will impact semver compatibility + +error: this argument is a mutable reference, but not used mutably + --> $DIR/needless_pass_by_ref_mut.rs:247:25 | LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -116,7 +124,7 @@ LL | pub async fn closure(n: &mut usize) -> impl '_ + FnMut() { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:242:20 + --> $DIR/needless_pass_by_ref_mut.rs:254:20 | LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { | ^^^^^^^^^^ help: consider changing to: `&usize` @@ -124,12 +132,12 @@ LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize { = warning: changing this function will impact semver compatibility error: this argument is a mutable reference, but not used mutably - --> $DIR/needless_pass_by_ref_mut.rs:253:26 + --> $DIR/needless_pass_by_ref_mut.rs:265:26 | LL | pub async fn closure4(n: &mut usize) { | ^^^^^^^^^^ help: consider changing to: `&usize` | = warning: changing this function will impact semver compatibility -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors From 7e46fb9a658ceb5120fdf0de8665e7cc19114d7f Mon Sep 17 00:00:00 2001 From: Mick van Gelderen Date: Fri, 22 Sep 2023 19:41:00 +0200 Subject: [PATCH 68/78] Fix large_futures example The value used in the large_futures example was not large enough to trigger the lint given the default threshold. The example also contained more code than necessary. This PR changes the value size from 1kB to 16kB and reduces the example in size. --- clippy_lints/src/large_futures.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index d67d5899350a8..19f1e08b57ad1 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -17,26 +17,20 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// async fn wait(f: impl std::future::Future) {} + /// async fn large_future(_x: [u8; 16 * 1024]) {} /// - /// async fn big_fut(arg: [u8; 1024]) {} - /// - /// pub async fn test() { - /// let fut = big_fut([0u8; 1024]); - /// wait(fut).await; + /// pub async fn trigger() { + /// large_future([0u8; 16 * 1024]).await; /// } /// ``` /// /// `Box::pin` the big future instead. /// /// ```rust - /// async fn wait(f: impl std::future::Future) {} - /// - /// async fn big_fut(arg: [u8; 1024]) {} + /// async fn large_future(_x: [u8; 16 * 1024]) {} /// - /// pub async fn test() { - /// let fut = Box::pin(big_fut([0u8; 1024])); - /// wait(fut).await; + /// pub async fn trigger() { + /// Box::pin(large_future([0u8; 16 * 1024])).await; /// } /// ``` #[clippy::version = "1.70.0"] From 0a2d39de2e0b87361432ae695cc84ad74d09972a Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 28 Jan 2023 12:56:04 +0000 Subject: [PATCH 69/78] Enable drop_tracking_mir by default. --- clippy_lints/src/await_holding_invalid.rs | 43 +++++++++++++---------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index d40a385435afb..7dd808a7b3b73 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -2,9 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_hir::{AsyncGeneratorKind, Body, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::GeneratorInteriorTypeCause; +use rustc_middle::mir::GeneratorLayout; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -197,28 +197,35 @@ impl LateLintPass<'_> for AwaitHolding { fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { use AsyncGeneratorKind::{Block, Closure, Fn}; if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let typeck_results = cx.tcx.typeck_body(body_id); - self.check_interior_types( - cx, - typeck_results.generator_interior_types.as_ref().skip_binder(), - body.value.span, - ); + let def_id = cx.tcx.hir().body_owner_def_id(body.id()); + if let Some(generator_layout) = cx.tcx.mir_generator_witnesses(def_id) { + self.check_interior_types(cx, generator_layout); + } } } } impl AwaitHolding { - fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { + fn check_interior_types(&self, cx: &LateContext<'_>, generator: &GeneratorLayout<'_>) { + for (ty_index, ty_cause) in generator.field_tys.iter_enumerated() { if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + let await_points = || { + generator + .variant_source_info + .iter_enumerated() + .filter_map(|(variant, source_info)| { + generator.variant_fields[variant] + .raw + .contains(&ty_index) + .then_some(source_info.span) + }) + .collect::>() + }; if is_mutex_guard(cx, adt.did()) { span_lint_and_then( cx, AWAIT_HOLDING_LOCK, - ty_cause.span, + ty_cause.source_info.span, "this `MutexGuard` is held across an `await` point", |diag| { diag.help( @@ -226,7 +233,7 @@ impl AwaitHolding { `MutexGuard` is dropped before calling await", ); diag.span_note( - ty_cause.scope_span.unwrap_or(span), + await_points(), "these are all the `await` points this lock is held through", ); }, @@ -235,18 +242,18 @@ impl AwaitHolding { span_lint_and_then( cx, AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, + ty_cause.source_info.span, "this `RefCell` reference is held across an `await` point", |diag| { diag.help("ensure the reference is dropped before calling `await`"); diag.span_note( - ty_cause.scope_span.unwrap_or(span), + await_points(), "these are all the `await` points this reference is held through", ); }, ); } else if let Some(disallowed) = self.def_ids.get(&adt.did()) { - emit_invalid_type(cx, ty_cause.span, disallowed); + emit_invalid_type(cx, ty_cause.source_info.span, disallowed); } } } From 19e160fe2fa0c8f04dc2b550db32f690ae27c64b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 28 Jan 2023 23:20:02 +0000 Subject: [PATCH 70/78] Bless clippy. --- .../await_holding_invalid_type.rs | 6 +- .../await_holding_invalid_type.stderr | 6 +- tests/ui/await_holding_lock.stderr | 137 ++++++------------ tests/ui/await_holding_refcell_ref.stderr | 67 +++------ tests/ui/future_not_send.rs | 1 + tests/ui/future_not_send.stderr | 36 +---- 6 files changed, 87 insertions(+), 166 deletions(-) diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs index fbef5c4564b1b..868cf00a8d469 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs @@ -7,8 +7,10 @@ async fn bad() -> u32 { } async fn bad_reason() -> u32 { - let _x = Ipv4Addr::new(127, 0, 0, 1); - baz().await + let x = Ipv4Addr::new(127, 0, 0, 1); + let y = baz().await; + let _x = x; + y } async fn good() -> u32 { diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr index 6f8b04fd12b94..ddcd1940d475c 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -11,11 +11,11 @@ LL | let _x = String::from("hello"); error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` --> $DIR/await_holding_invalid_type.rs:10:9 | -LL | let _x = Ipv4Addr::new(127, 0, 0, 1); - | ^^ +LL | let x = Ipv4Addr::new(127, 0, 0, 1); + | ^ error: `std::string::String` may not be held across an `await` point per `clippy.toml` - --> $DIR/await_holding_invalid_type.rs:31:13 + --> $DIR/await_holding_invalid_type.rs:33:13 | LL | let _x = String::from("hi!"); | ^^ diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr index 56b111c4d9e41..4782104000228 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_lock.stderr @@ -6,13 +6,10 @@ LL | let guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:9:9 + --> $DIR/await_holding_lock.rs:11:15 | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ = note: `-D clippy::await-holding-lock` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::await_holding_lock)]` @@ -24,13 +21,10 @@ LL | let guard = x.read().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:25:9 + --> $DIR/await_holding_lock.rs:27:15 | -LL | / let guard = x.read().unwrap(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:31:13 @@ -40,13 +34,10 @@ LL | let mut guard = x.write().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:31:9 + --> $DIR/await_holding_lock.rs:33:15 | -LL | / let mut guard = x.write().unwrap(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:53:13 @@ -56,16 +47,13 @@ LL | let guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:53:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | -LL | | let second = baz().await; -... | -LL | | first + second + third -LL | | } - | |_____^ + --> $DIR/await_holding_lock.rs:56:28 + | +LL | let second = baz().await; + | ^^^^^ +LL | +LL | let third = baz().await; + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:67:17 @@ -75,13 +63,10 @@ LL | let guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:67:13 + --> $DIR/await_holding_lock.rs:69:19 | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | baz().await -LL | | }; - | |_________^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:80:17 @@ -91,13 +76,10 @@ LL | let guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:80:13 + --> $DIR/await_holding_lock.rs:82:19 | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | baz().await -LL | | } - | |_________^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:93:13 @@ -107,13 +89,10 @@ LL | let guard = x.lock(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:93:9 + --> $DIR/await_holding_lock.rs:95:15 | -LL | / let guard = x.lock(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:109:13 @@ -123,13 +102,10 @@ LL | let guard = x.read(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:109:9 + --> $DIR/await_holding_lock.rs:111:15 | -LL | / let guard = x.read(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:115:13 @@ -139,13 +115,10 @@ LL | let mut guard = x.write(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:115:9 + --> $DIR/await_holding_lock.rs:117:15 | -LL | / let mut guard = x.write(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:137:13 @@ -155,16 +128,13 @@ LL | let guard = x.lock(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:137:9 - | -LL | / let guard = x.lock(); -LL | | -LL | | -LL | | let second = baz().await; -... | -LL | | first + second + third -LL | | } - | |_____^ + --> $DIR/await_holding_lock.rs:140:28 + | +LL | let second = baz().await; + | ^^^^^ +LL | +LL | let third = baz().await; + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:151:17 @@ -174,13 +144,10 @@ LL | let guard = x.lock(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:151:13 + --> $DIR/await_holding_lock.rs:153:19 | -LL | / let guard = x.lock(); -LL | | -LL | | baz().await -LL | | }; - | |_________^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:164:17 @@ -190,13 +157,10 @@ LL | let guard = x.lock(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:164:13 + --> $DIR/await_holding_lock.rs:166:19 | -LL | / let guard = x.lock(); -LL | | -LL | | baz().await -LL | | } - | |_________^ +LL | baz().await + | ^^^^^ error: this `MutexGuard` is held across an `await` point --> $DIR/await_holding_lock.rs:185:9 @@ -206,15 +170,10 @@ LL | let mut guard = x.lock().unwrap(); | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> $DIR/await_holding_lock.rs:185:5 - | -LL | / let mut guard = x.lock().unwrap(); -LL | | -LL | | *guard += 1; -LL | | drop(guard); -LL | | baz().await; -LL | | } - | |_^ + --> $DIR/await_holding_lock.rs:189:11 + | +LL | baz().await; + | ^^^^^ error: aborting due to 13 previous errors diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr index 6b7e1d1afddf4..9264af93dc168 100644 --- a/tests/ui/await_holding_refcell_ref.stderr +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -6,13 +6,10 @@ LL | let b = x.borrow(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:6:5 + --> $DIR/await_holding_refcell_ref.rs:8:11 | -LL | / let b = x.borrow(); -LL | | -LL | | baz().await -LL | | } - | |_^ +LL | baz().await + | ^^^^^ = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::await_holding_refcell_ref)]` @@ -24,13 +21,10 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:12:5 + --> $DIR/await_holding_refcell_ref.rs:14:11 | -LL | / let b = x.borrow_mut(); -LL | | -LL | | baz().await -LL | | } - | |_^ +LL | baz().await + | ^^^^^ error: this `RefCell` reference is held across an `await` point --> $DIR/await_holding_refcell_ref.rs:34:9 @@ -40,16 +34,13 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:34:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | -LL | | let second = baz().await; -... | -LL | | first + second + third -LL | | } - | |_^ + --> $DIR/await_holding_refcell_ref.rs:37:24 + | +LL | let second = baz().await; + | ^^^^^ +LL | +LL | let third = baz().await; + | ^^^^^ error: this `RefCell` reference is held across an `await` point --> $DIR/await_holding_refcell_ref.rs:47:9 @@ -59,16 +50,10 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:47:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | -LL | | let second = baz().await; -... | -LL | | first + second + third -LL | | } - | |_^ + --> $DIR/await_holding_refcell_ref.rs:50:24 + | +LL | let second = baz().await; + | ^^^^^ error: this `RefCell` reference is held across an `await` point --> $DIR/await_holding_refcell_ref.rs:63:13 @@ -78,13 +63,10 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:63:9 + --> $DIR/await_holding_refcell_ref.rs:65:15 | -LL | / let b = x.borrow_mut(); -LL | | -LL | | baz().await -LL | | }; - | |_____^ +LL | baz().await + | ^^^^^ error: this `RefCell` reference is held across an `await` point --> $DIR/await_holding_refcell_ref.rs:76:13 @@ -94,13 +76,10 @@ LL | let b = x.borrow_mut(); | = help: ensure the reference is dropped before calling `await` note: these are all the `await` points this reference is held through - --> $DIR/await_holding_refcell_ref.rs:76:9 + --> $DIR/await_holding_refcell_ref.rs:78:15 | -LL | / let b = x.borrow_mut(); -LL | | -LL | | baz().await -LL | | } - | |_____^ +LL | baz().await + | ^^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/future_not_send.rs b/tests/ui/future_not_send.rs index 06090e2713dee..9274340b5caa5 100644 --- a/tests/ui/future_not_send.rs +++ b/tests/ui/future_not_send.rs @@ -59,6 +59,7 @@ where { let rt = &t; async { true }.await; + let _ = rt; t } diff --git a/tests/ui/future_not_send.stderr b/tests/ui/future_not_send.stderr index e417de723ff3e..f43e3c8ff9f7b 100644 --- a/tests/ui/future_not_send.stderr +++ b/tests/ui/future_not_send.stderr @@ -12,19 +12,12 @@ LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { LL | LL | async { true }.await | ^^^^^ await occurs here, with `rc` maybe used later -LL | } - | - `rc` is later dropped here = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` -note: future is not `Send` as this value is used across an await - --> $DIR/future_not_send.rs:9:20 +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/future_not_send.rs:7:39 | LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ---- has type `&std::cell::Cell` which is not `Send` -LL | -LL | async { true }.await - | ^^^^^ await occurs here, with `cell` maybe used later -LL | } - | - `cell` is later dropped here + | ^^^^ has type `&std::cell::Cell` which is not `Send`, because `std::cell::Cell` is not `Sync` = note: `std::cell::Cell` doesn't implement `std::marker::Sync` = note: `-D clippy::future-not-send` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]` @@ -43,8 +36,6 @@ LL | pub async fn public_future(rc: Rc<[u8]>) { LL | LL | async { true }.await; | ^^^^^ await occurs here, with `rc` maybe used later -LL | } - | - `rc` is later dropped here = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely @@ -93,9 +84,6 @@ LL | async fn private_future(&self) -> usize { LL | LL | async { true }.await; | ^^^^^ await occurs here, with `&self` maybe used later -LL | self.rc.len() -LL | } - | - `&self` is later dropped here = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely @@ -104,16 +92,11 @@ error: future cannot be sent between threads safely LL | pub async fn public_future(&self) { | ^ future returned by `public_future` is not `Send` | -note: future is not `Send` as this value is used across an await - --> $DIR/future_not_send.rs:46:31 +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/future_not_send.rs:44:32 | LL | pub async fn public_future(&self) { - | ----- has type `&Dummy` which is not `Send` -LL | -LL | self.private_future().await; - | ^^^^^ await occurs here, with `&self` maybe used later -LL | } - | - `&self` is later dropped here + | ^^^^^ has type `&Dummy` which is not `Send`, because `Dummy` is not `Sync` = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely @@ -129,19 +112,16 @@ LL | let rt = &t; | -- has type `&T` which is not `Send` LL | async { true }.await; | ^^^^^ await occurs here, with `rt` maybe used later -LL | t -LL | } - | - `rt` is later dropped here = note: `T` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:72:34 + --> $DIR/future_not_send.rs:73:34 | LL | async fn unclear_future(t: T) {} | ^ future returned by `unclear_future` is not `Send` | note: captured value is not `Send` - --> $DIR/future_not_send.rs:72:28 + --> $DIR/future_not_send.rs:73:28 | LL | async fn unclear_future(t: T) {} | ^ has type `T` which is not `Send` From e2669b27f3cd32fbf956bfcae96c3e19f49108a1 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 3 Aug 2023 13:47:04 +0000 Subject: [PATCH 71/78] Remove GeneratorWitness and rename GeneratorWitnessMIR. --- clippy_lints/src/dereference.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 8090f821d1ff2..fe37fd4a0c192 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -940,7 +940,6 @@ impl TyCoercionStability { | ty::FnDef(..) | ty::Generator(..) | ty::GeneratorWitness(..) - | ty::GeneratorWitnessMIR(..) | ty::Closure(..) | ty::Never | ty::Tuple(_) From 6ce61d543acd54edfbd3dde5861e3a0bee752cd6 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 24 Sep 2023 14:56:40 +0200 Subject: [PATCH 72/78] add missing configuration tests --- .../clippy.toml | 1 + .../decimal_literal_representation.fixed | 6 ++++++ .../decimal_literal_representation.rs | 6 ++++++ .../decimal_literal_representation.stderr | 11 ++++++++++ .../disallowed_script_idents/clippy.toml | 1 + .../disallowed_script_idents.rs | 6 ++++++ .../disallowed_script_idents.stderr | 11 ++++++++++ tests/ui-toml/enum_variant_names/clippy.toml | 1 + .../enum_variant_names/enum_variant_names.rs | 16 ++++++++++++++ .../enum_variant_names.stderr | 18 ++++++++++++++++ tests/ui-toml/enum_variant_size/clippy.toml | 1 + .../enum_variant_size/enum_variant_size.fixed | 11 ++++++++++ .../enum_variant_size/enum_variant_size.rs | 11 ++++++++++ .../enum_variant_size.stderr | 21 +++++++++++++++++++ tests/ui-toml/explicit_iter_loop/clippy.toml | 1 + .../explicit_iter_loop.fixed | 10 +++++++++ .../explicit_iter_loop/explicit_iter_loop.rs | 10 +++++++++ .../explicit_iter_loop.stderr | 17 +++++++++++++++ tests/ui-toml/large_stack_frames/clippy.toml | 1 + .../large_stack_frames/large_stack_frames.rs | 17 +++++++++++++++ .../large_stack_frames.stderr | 15 +++++++++++++ .../large_types_passed_by_value/clippy.toml | 1 + .../large_types_passed_by_value.fixed | 7 +++++++ .../large_types_passed_by_value.rs | 7 +++++++ .../large_types_passed_by_value.stderr | 11 ++++++++++ tests/ui-toml/manual_let_else/clippy.toml | 1 + .../manual_let_else/manual_let_else.fixed | 10 +++++++++ .../manual_let_else/manual_let_else.rs | 14 +++++++++++++ .../manual_let_else/manual_let_else.stderr | 15 +++++++++++++ tests/ui-toml/path_ends_with_ext/clippy.toml | 1 + .../path_ends_with_ext/path_ends_with_ext.rs | 9 ++++++++ tests/ui-toml/result_large_err/clippy.toml | 1 + .../result_large_err/result_large_err.rs | 10 +++++++++ .../result_large_err/result_large_err.stderr | 12 +++++++++++ .../too_large_for_stack/boxed_local.rs | 5 +++++ .../too_large_for_stack/boxed_local.stderr | 11 ++++++++++ tests/ui-toml/too_large_for_stack/clippy.toml | 1 + .../too_large_for_stack/useless_vec.fixed | 9 ++++++++ .../too_large_for_stack/useless_vec.rs | 9 ++++++++ .../too_large_for_stack/useless_vec.stderr | 11 ++++++++++ tests/ui-toml/too_many_arguments/clippy.toml | 1 + .../too_many_arguments/too_many_arguments.rs | 7 +++++++ .../too_many_arguments.stderr | 11 ++++++++++ tests/ui-toml/type_complexity/clippy.toml | 1 + .../type_complexity/type_complexity.rs | 7 +++++++ .../type_complexity/type_complexity.stderr | 11 ++++++++++ .../type_repetition_in_bounds/clippy.toml | 1 + .../ui-toml/type_repetition_in_bounds/main.rs | 18 ++++++++++++++++ .../type_repetition_in_bounds/main.stderr | 12 +++++++++++ .../unnecessary_box_returns/clippy.toml | 1 + .../unnecessary_box_returns.fixed | 11 ++++++++++ .../unnecessary_box_returns.rs | 11 ++++++++++ .../unnecessary_box_returns.stderr | 12 +++++++++++ tests/ui-toml/verbose_bit_mask/clippy.toml | 1 + .../verbose_bit_mask/verbose_bit_mask.fixed | 7 +++++++ .../verbose_bit_mask/verbose_bit_mask.rs | 7 +++++++ .../verbose_bit_mask/verbose_bit_mask.stderr | 11 ++++++++++ tests/ui-toml/wildcard_imports/clippy.toml | 1 + .../wildcard_imports/wildcard_imports.fixed | 11 ++++++++++ .../wildcard_imports/wildcard_imports.rs | 11 ++++++++++ .../wildcard_imports/wildcard_imports.stderr | 11 ++++++++++ 61 files changed, 501 insertions(+) create mode 100644 tests/ui-toml/decimal_literal_representation/clippy.toml create mode 100644 tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed create mode 100644 tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs create mode 100644 tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr create mode 100644 tests/ui-toml/disallowed_script_idents/clippy.toml create mode 100644 tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs create mode 100644 tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr create mode 100644 tests/ui-toml/enum_variant_names/clippy.toml create mode 100644 tests/ui-toml/enum_variant_names/enum_variant_names.rs create mode 100644 tests/ui-toml/enum_variant_names/enum_variant_names.stderr create mode 100644 tests/ui-toml/enum_variant_size/clippy.toml create mode 100644 tests/ui-toml/enum_variant_size/enum_variant_size.fixed create mode 100644 tests/ui-toml/enum_variant_size/enum_variant_size.rs create mode 100644 tests/ui-toml/enum_variant_size/enum_variant_size.stderr create mode 100644 tests/ui-toml/explicit_iter_loop/clippy.toml create mode 100644 tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed create mode 100644 tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs create mode 100644 tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr create mode 100644 tests/ui-toml/large_stack_frames/clippy.toml create mode 100644 tests/ui-toml/large_stack_frames/large_stack_frames.rs create mode 100644 tests/ui-toml/large_stack_frames/large_stack_frames.stderr create mode 100644 tests/ui-toml/large_types_passed_by_value/clippy.toml create mode 100644 tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed create mode 100644 tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs create mode 100644 tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr create mode 100644 tests/ui-toml/manual_let_else/clippy.toml create mode 100644 tests/ui-toml/manual_let_else/manual_let_else.fixed create mode 100644 tests/ui-toml/manual_let_else/manual_let_else.rs create mode 100644 tests/ui-toml/manual_let_else/manual_let_else.stderr create mode 100644 tests/ui-toml/path_ends_with_ext/clippy.toml create mode 100644 tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs create mode 100644 tests/ui-toml/result_large_err/clippy.toml create mode 100644 tests/ui-toml/result_large_err/result_large_err.rs create mode 100644 tests/ui-toml/result_large_err/result_large_err.stderr create mode 100644 tests/ui-toml/too_large_for_stack/boxed_local.rs create mode 100644 tests/ui-toml/too_large_for_stack/boxed_local.stderr create mode 100644 tests/ui-toml/too_large_for_stack/clippy.toml create mode 100644 tests/ui-toml/too_large_for_stack/useless_vec.fixed create mode 100644 tests/ui-toml/too_large_for_stack/useless_vec.rs create mode 100644 tests/ui-toml/too_large_for_stack/useless_vec.stderr create mode 100644 tests/ui-toml/too_many_arguments/clippy.toml create mode 100644 tests/ui-toml/too_many_arguments/too_many_arguments.rs create mode 100644 tests/ui-toml/too_many_arguments/too_many_arguments.stderr create mode 100644 tests/ui-toml/type_complexity/clippy.toml create mode 100644 tests/ui-toml/type_complexity/type_complexity.rs create mode 100644 tests/ui-toml/type_complexity/type_complexity.stderr create mode 100644 tests/ui-toml/type_repetition_in_bounds/clippy.toml create mode 100644 tests/ui-toml/type_repetition_in_bounds/main.rs create mode 100644 tests/ui-toml/type_repetition_in_bounds/main.stderr create mode 100644 tests/ui-toml/unnecessary_box_returns/clippy.toml create mode 100644 tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed create mode 100644 tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs create mode 100644 tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr create mode 100644 tests/ui-toml/verbose_bit_mask/clippy.toml create mode 100644 tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed create mode 100644 tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs create mode 100644 tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr create mode 100644 tests/ui-toml/wildcard_imports/clippy.toml create mode 100644 tests/ui-toml/wildcard_imports/wildcard_imports.fixed create mode 100644 tests/ui-toml/wildcard_imports/wildcard_imports.rs create mode 100644 tests/ui-toml/wildcard_imports/wildcard_imports.stderr diff --git a/tests/ui-toml/decimal_literal_representation/clippy.toml b/tests/ui-toml/decimal_literal_representation/clippy.toml new file mode 100644 index 0000000000000..74fc5d249d005 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/clippy.toml @@ -0,0 +1 @@ +literal-representation-threshold = 0xFFFFFF diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed new file mode 100644 index 0000000000000..750f3be84c040 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.fixed @@ -0,0 +1,6 @@ +#![warn(clippy::decimal_literal_representation)] +fn main() { + let _ = 8388608; + let _ = 0x00FF_FFFF; + //~^ ERROR: integer literal has a better hexadecimal representation +} diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs new file mode 100644 index 0000000000000..26b3354d15909 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.rs @@ -0,0 +1,6 @@ +#![warn(clippy::decimal_literal_representation)] +fn main() { + let _ = 8388608; + let _ = 16777215; + //~^ ERROR: integer literal has a better hexadecimal representation +} diff --git a/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr new file mode 100644 index 0000000000000..6f817a3fdde42 --- /dev/null +++ b/tests/ui-toml/decimal_literal_representation/decimal_literal_representation.stderr @@ -0,0 +1,11 @@ +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:4:13 + | +LL | let _ = 16777215; + | ^^^^^^^^ help: consider: `0x00FF_FFFF` + | + = note: `-D clippy::decimal-literal-representation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::decimal_literal_representation)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/disallowed_script_idents/clippy.toml b/tests/ui-toml/disallowed_script_idents/clippy.toml new file mode 100644 index 0000000000000..26cb2d77bfd9f --- /dev/null +++ b/tests/ui-toml/disallowed_script_idents/clippy.toml @@ -0,0 +1 @@ +allowed-scripts = ["Cyrillic"] diff --git a/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs new file mode 100644 index 0000000000000..9df1ec6fab0d7 --- /dev/null +++ b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.rs @@ -0,0 +1,6 @@ +#![warn(clippy::disallowed_script_idents)] +fn main() { + let счётчик = 10; + let カウンタ = 10; + //~^ ERROR: identifier `カウンタ` has a Unicode script that is not allowed by configuration +} diff --git a/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr new file mode 100644 index 0000000000000..31bb5ee3514a5 --- /dev/null +++ b/tests/ui-toml/disallowed_script_idents/disallowed_script_idents.stderr @@ -0,0 +1,11 @@ +error: identifier `カウンタ` has a Unicode script that is not allowed by configuration: Katakana + --> $DIR/disallowed_script_idents.rs:4:9 + | +LL | let カウンタ = 10; + | ^^^^^^^^ + | + = note: `-D clippy::disallowed-script-idents` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::disallowed_script_idents)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/enum_variant_names/clippy.toml b/tests/ui-toml/enum_variant_names/clippy.toml new file mode 100644 index 0000000000000..0ad7a97994849 --- /dev/null +++ b/tests/ui-toml/enum_variant_names/clippy.toml @@ -0,0 +1 @@ +enum-variant-name-threshold = 5 diff --git a/tests/ui-toml/enum_variant_names/enum_variant_names.rs b/tests/ui-toml/enum_variant_names/enum_variant_names.rs new file mode 100644 index 0000000000000..8f4e178ccfe17 --- /dev/null +++ b/tests/ui-toml/enum_variant_names/enum_variant_names.rs @@ -0,0 +1,16 @@ +enum Foo { + AFoo, + BFoo, + CFoo, + DFoo, +} +enum Foo2 { + //~^ ERROR: all variants have the same postfix + AFoo, + BFoo, + CFoo, + DFoo, + EFoo, +} + +fn main() {} diff --git a/tests/ui-toml/enum_variant_names/enum_variant_names.stderr b/tests/ui-toml/enum_variant_names/enum_variant_names.stderr new file mode 100644 index 0000000000000..11039b1db4877 --- /dev/null +++ b/tests/ui-toml/enum_variant_names/enum_variant_names.stderr @@ -0,0 +1,18 @@ +error: all variants have the same postfix: `Foo` + --> $DIR/enum_variant_names.rs:7:1 + | +LL | / enum Foo2 { +LL | | +LL | | AFoo, +LL | | BFoo, +... | +LL | | EFoo, +LL | | } + | |_^ + | + = help: remove the postfixes and use full paths to the variants instead of glob imports + = note: `-D clippy::enum-variant-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::enum_variant_names)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/enum_variant_size/clippy.toml b/tests/ui-toml/enum_variant_size/clippy.toml new file mode 100644 index 0000000000000..64a8017fe02e4 --- /dev/null +++ b/tests/ui-toml/enum_variant_size/clippy.toml @@ -0,0 +1 @@ +enum-variant-size-threshold = 500 diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.fixed b/tests/ui-toml/enum_variant_size/enum_variant_size.fixed new file mode 100644 index 0000000000000..9ae760ae41aea --- /dev/null +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.fixed @@ -0,0 +1,11 @@ +enum Fine { + A(()), + B([u8; 500]), +} +enum Bad { + //~^ ERROR: large size difference between variants + A(()), + B(Box<[u8; 501]>), +} + +fn main() {} diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.rs b/tests/ui-toml/enum_variant_size/enum_variant_size.rs new file mode 100644 index 0000000000000..cf7f432bf0b14 --- /dev/null +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.rs @@ -0,0 +1,11 @@ +enum Fine { + A(()), + B([u8; 500]), +} +enum Bad { + //~^ ERROR: large size difference between variants + A(()), + B([u8; 501]), +} + +fn main() {} diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.stderr b/tests/ui-toml/enum_variant_size/enum_variant_size.stderr new file mode 100644 index 0000000000000..4d9bc9d48e45a --- /dev/null +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.stderr @@ -0,0 +1,21 @@ +error: large size difference between variants + --> $DIR/enum_variant_size.rs:5:1 + | +LL | / enum Bad { +LL | | +LL | | A(()), + | | ----- the second-largest variant contains at least 0 bytes +LL | | B([u8; 501]), + | | ------------ the largest variant contains at least 501 bytes +LL | | } + | |_^ the entire enum is at least 502 bytes + | + = note: `-D clippy::large-enum-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[u8; 501]>), + | ~~~~~~~~~~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui-toml/explicit_iter_loop/clippy.toml b/tests/ui-toml/explicit_iter_loop/clippy.toml new file mode 100644 index 0000000000000..15d175ef14785 --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/clippy.toml @@ -0,0 +1 @@ +enforce-iter-loop-reborrow = true diff --git a/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed new file mode 100644 index 0000000000000..468da22a926fe --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::explicit_iter_loop)] + +fn main() { + let mut vec = vec![1, 2, 3]; + let rmvec = &mut vec; + for _ in &*rmvec {} + //~^ ERROR: it is more concise to loop over references to containers + for _ in &mut *rmvec {} + //~^ ERROR: it is more concise to loop over references to containers +} diff --git a/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs new file mode 100644 index 0000000000000..a934648608c21 --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.rs @@ -0,0 +1,10 @@ +#![warn(clippy::explicit_iter_loop)] + +fn main() { + let mut vec = vec![1, 2, 3]; + let rmvec = &mut vec; + for _ in rmvec.iter() {} + //~^ ERROR: it is more concise to loop over references to containers + for _ in rmvec.iter_mut() {} + //~^ ERROR: it is more concise to loop over references to containers +} diff --git a/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr new file mode 100644 index 0000000000000..587d4f9b3f053 --- /dev/null +++ b/tests/ui-toml/explicit_iter_loop/explicit_iter_loop.stderr @@ -0,0 +1,17 @@ +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/explicit_iter_loop.rs:6:14 + | +LL | for _ in rmvec.iter() {} + | ^^^^^^^^^^^^ help: to write this more concisely, try: `&*rmvec` + | + = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/explicit_iter_loop.rs:8:14 + | +LL | for _ in rmvec.iter_mut() {} + | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *rmvec` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/large_stack_frames/clippy.toml b/tests/ui-toml/large_stack_frames/clippy.toml new file mode 100644 index 0000000000000..1b68801b97053 --- /dev/null +++ b/tests/ui-toml/large_stack_frames/clippy.toml @@ -0,0 +1 @@ +stack-size-threshold = 4294967296 diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.rs b/tests/ui-toml/large_stack_frames/large_stack_frames.rs new file mode 100644 index 0000000000000..39b3c571c5c91 --- /dev/null +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.rs @@ -0,0 +1,17 @@ +#![warn(clippy::large_stack_frames)] + +// We use this helper function instead of writing [0; 4294967297] directly to represent a +// case that large_stack_arrays can't catch +fn create_array() -> [u8; N] { + [0; N] +} + +fn f() { + let _x = create_array::<4294967296>(); +} +fn f2() { + //~^ ERROR: this function allocates a large amount of stack space + let _x = create_array::<4294967297>(); +} + +fn main() {} diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.stderr b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr new file mode 100644 index 0000000000000..ef7d15a3739a0 --- /dev/null +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr @@ -0,0 +1,15 @@ +error: this function allocates a large amount of stack space + --> $DIR/large_stack_frames.rs:12:1 + | +LL | / fn f2() { +LL | | +LL | | let _x = create_array::<4294967297>(); +LL | | } + | |_^ + | + = note: allocating large amounts of stack space can overflow the stack + = note: `-D clippy::large-stack-frames` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/large_types_passed_by_value/clippy.toml b/tests/ui-toml/large_types_passed_by_value/clippy.toml new file mode 100644 index 0000000000000..45bcbce1e3c53 --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/clippy.toml @@ -0,0 +1 @@ +pass-by-value-size-limit = 512 diff --git a/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed new file mode 100644 index 0000000000000..3c87c79cf2fcc --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::large_types_passed_by_value)] + +fn f(_v: [u8; 512]) {} +fn f2(_v: &[u8; 513]) {} +//~^ ERROR: this argument (513 byte) is passed by value + +fn main() {} diff --git a/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs new file mode 100644 index 0000000000000..0572373a61188 --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.rs @@ -0,0 +1,7 @@ +#![warn(clippy::large_types_passed_by_value)] + +fn f(_v: [u8; 512]) {} +fn f2(_v: [u8; 513]) {} +//~^ ERROR: this argument (513 byte) is passed by value + +fn main() {} diff --git a/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr new file mode 100644 index 0000000000000..6678a2b47214f --- /dev/null +++ b/tests/ui-toml/large_types_passed_by_value/large_types_passed_by_value.stderr @@ -0,0 +1,11 @@ +error: this argument (513 byte) is passed by value, but might be more efficient if passed by reference (limit: 512 byte) + --> $DIR/large_types_passed_by_value.rs:4:11 + | +LL | fn f2(_v: [u8; 513]) {} + | ^^^^^^^^^ help: consider passing by reference instead: `&[u8; 513]` + | + = note: `-D clippy::large-types-passed-by-value` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_types_passed_by_value)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/manual_let_else/clippy.toml b/tests/ui-toml/manual_let_else/clippy.toml new file mode 100644 index 0000000000000..cdae1da011b0a --- /dev/null +++ b/tests/ui-toml/manual_let_else/clippy.toml @@ -0,0 +1 @@ +matches-for-let-else = "AllTypes" diff --git a/tests/ui-toml/manual_let_else/manual_let_else.fixed b/tests/ui-toml/manual_let_else/manual_let_else.fixed new file mode 100644 index 0000000000000..972f6aa40303d --- /dev/null +++ b/tests/ui-toml/manual_let_else/manual_let_else.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::manual_let_else)] + +enum Foo { + A(u8), + B, +} + +fn main() { + let Foo::A(x) = Foo::A(1) else { return }; +} diff --git a/tests/ui-toml/manual_let_else/manual_let_else.rs b/tests/ui-toml/manual_let_else/manual_let_else.rs new file mode 100644 index 0000000000000..fdaba4ad2a602 --- /dev/null +++ b/tests/ui-toml/manual_let_else/manual_let_else.rs @@ -0,0 +1,14 @@ +#![warn(clippy::manual_let_else)] + +enum Foo { + A(u8), + B, +} + +fn main() { + let x = match Foo::A(1) { + //~^ ERROR: this could be rewritten as `let...else` + Foo::A(x) => x, + Foo::B => return, + }; +} diff --git a/tests/ui-toml/manual_let_else/manual_let_else.stderr b/tests/ui-toml/manual_let_else/manual_let_else.stderr new file mode 100644 index 0000000000000..5c2c86c373189 --- /dev/null +++ b/tests/ui-toml/manual_let_else/manual_let_else.stderr @@ -0,0 +1,15 @@ +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:9:5 + | +LL | / let x = match Foo::A(1) { +LL | | +LL | | Foo::A(x) => x, +LL | | Foo::B => return, +LL | | }; + | |______^ help: consider writing: `let Foo::A(x) = Foo::A(1) else { return };` + | + = note: `-D clippy::manual-let-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/path_ends_with_ext/clippy.toml b/tests/ui-toml/path_ends_with_ext/clippy.toml new file mode 100644 index 0000000000000..40d7dfd938ce0 --- /dev/null +++ b/tests/ui-toml/path_ends_with_ext/clippy.toml @@ -0,0 +1 @@ +allowed-dotfiles = ["dot"] diff --git a/tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs b/tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs new file mode 100644 index 0000000000000..a34b15f4ac9ab --- /dev/null +++ b/tests/ui-toml/path_ends_with_ext/path_ends_with_ext.rs @@ -0,0 +1,9 @@ +#![warn(clippy::path_ends_with_ext)] + +use std::path::Path; + +fn f(p: &Path) { + p.ends_with(".dot"); +} + +fn main() {} diff --git a/tests/ui-toml/result_large_err/clippy.toml b/tests/ui-toml/result_large_err/clippy.toml new file mode 100644 index 0000000000000..df505ed9672a6 --- /dev/null +++ b/tests/ui-toml/result_large_err/clippy.toml @@ -0,0 +1 @@ +large-error-threshold = 512 diff --git a/tests/ui-toml/result_large_err/result_large_err.rs b/tests/ui-toml/result_large_err/result_large_err.rs new file mode 100644 index 0000000000000..dea4d61a96bfb --- /dev/null +++ b/tests/ui-toml/result_large_err/result_large_err.rs @@ -0,0 +1,10 @@ +#![warn(clippy::result_large_err)] + +fn f() -> Result<(), [u8; 511]> { + todo!() +} +fn f2() -> Result<(), [u8; 512]> { + //~^ ERROR: the `Err`-variant returned from this function is very large + todo!() +} +fn main() {} diff --git a/tests/ui-toml/result_large_err/result_large_err.stderr b/tests/ui-toml/result_large_err/result_large_err.stderr new file mode 100644 index 0000000000000..b0936319d1b9d --- /dev/null +++ b/tests/ui-toml/result_large_err/result_large_err.stderr @@ -0,0 +1,12 @@ +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:6:12 + | +LL | fn f2() -> Result<(), [u8; 512]> { + | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes + | + = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` + = note: `-D clippy::result-large-err` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::result_large_err)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/too_large_for_stack/boxed_local.rs b/tests/ui-toml/too_large_for_stack/boxed_local.rs new file mode 100644 index 0000000000000..2f02361220650 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/boxed_local.rs @@ -0,0 +1,5 @@ +fn f(x: Box<[u8; 500]>) {} +//~^ ERROR: local variable doesn't need to be boxed here +fn f2(x: Box<[u8; 501]>) {} + +fn main() {} diff --git a/tests/ui-toml/too_large_for_stack/boxed_local.stderr b/tests/ui-toml/too_large_for_stack/boxed_local.stderr new file mode 100644 index 0000000000000..2859a29f1b2a2 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/boxed_local.stderr @@ -0,0 +1,11 @@ +error: local variable doesn't need to be boxed here + --> $DIR/boxed_local.rs:1:6 + | +LL | fn f(x: Box<[u8; 500]>) {} + | ^ + | + = note: `-D clippy::boxed-local` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::boxed_local)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/too_large_for_stack/clippy.toml b/tests/ui-toml/too_large_for_stack/clippy.toml new file mode 100644 index 0000000000000..a9c42fca46807 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/clippy.toml @@ -0,0 +1 @@ +too-large-for-stack = 500 diff --git a/tests/ui-toml/too_large_for_stack/useless_vec.fixed b/tests/ui-toml/too_large_for_stack/useless_vec.fixed new file mode 100644 index 0000000000000..ebe92d9b59946 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/useless_vec.fixed @@ -0,0 +1,9 @@ +#![warn(clippy::useless_vec)] + +fn main() { + let x = [0u8; 500]; + //~^ ERROR: useless use of `vec!` + x.contains(&1); + let y = vec![0u8; 501]; + y.contains(&1); +} diff --git a/tests/ui-toml/too_large_for_stack/useless_vec.rs b/tests/ui-toml/too_large_for_stack/useless_vec.rs new file mode 100644 index 0000000000000..e2886a8ccd126 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/useless_vec.rs @@ -0,0 +1,9 @@ +#![warn(clippy::useless_vec)] + +fn main() { + let x = vec![0u8; 500]; + //~^ ERROR: useless use of `vec!` + x.contains(&1); + let y = vec![0u8; 501]; + y.contains(&1); +} diff --git a/tests/ui-toml/too_large_for_stack/useless_vec.stderr b/tests/ui-toml/too_large_for_stack/useless_vec.stderr new file mode 100644 index 0000000000000..923cded5eef18 --- /dev/null +++ b/tests/ui-toml/too_large_for_stack/useless_vec.stderr @@ -0,0 +1,11 @@ +error: useless use of `vec!` + --> $DIR/useless_vec.rs:4:13 + | +LL | let x = vec![0u8; 500]; + | ^^^^^^^^^^^^^^ help: you can use an array directly: `[0u8; 500]` + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/too_many_arguments/clippy.toml b/tests/ui-toml/too_many_arguments/clippy.toml new file mode 100644 index 0000000000000..15906305c891b --- /dev/null +++ b/tests/ui-toml/too_many_arguments/clippy.toml @@ -0,0 +1 @@ +too-many-arguments-threshold = 10 diff --git a/tests/ui-toml/too_many_arguments/too_many_arguments.rs b/tests/ui-toml/too_many_arguments/too_many_arguments.rs new file mode 100644 index 0000000000000..7b2d6897d3c2c --- /dev/null +++ b/tests/ui-toml/too_many_arguments/too_many_arguments.rs @@ -0,0 +1,7 @@ +#![warn(clippy::too_many_arguments)] + +fn not_too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8) {} +fn too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8, p11: u8) {} +//~^ ERROR: this function has too many arguments + +fn main() {} diff --git a/tests/ui-toml/too_many_arguments/too_many_arguments.stderr b/tests/ui-toml/too_many_arguments/too_many_arguments.stderr new file mode 100644 index 0000000000000..a52e1fcb9e3a8 --- /dev/null +++ b/tests/ui-toml/too_many_arguments/too_many_arguments.stderr @@ -0,0 +1,11 @@ +error: this function has too many arguments (11/10) + --> $DIR/too_many_arguments.rs:4:1 + | +LL | fn too_many(p1: u8, p2: u8, p3: u8, p4: u8, p5: u8, p6: u8, p7: u8, p8: u8, p9: u8, p10: u8, p11: u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::too-many-arguments` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/type_complexity/clippy.toml b/tests/ui-toml/type_complexity/clippy.toml new file mode 100644 index 0000000000000..bf2ffdd0e3071 --- /dev/null +++ b/tests/ui-toml/type_complexity/clippy.toml @@ -0,0 +1 @@ +type-complexity-threshold = 500 diff --git a/tests/ui-toml/type_complexity/type_complexity.rs b/tests/ui-toml/type_complexity/type_complexity.rs new file mode 100644 index 0000000000000..b95f5134347dc --- /dev/null +++ b/tests/ui-toml/type_complexity/type_complexity.rs @@ -0,0 +1,7 @@ +// 480 +fn f(_: (u8, (u8, (u8, (u8, (u8, (u8,))))))) {} +// 550 +fn f2(_: (u8, (u8, (u8, (u8, (u8, (u8, u8))))))) {} +//~^ ERROR: very complex type used + +fn main() {} diff --git a/tests/ui-toml/type_complexity/type_complexity.stderr b/tests/ui-toml/type_complexity/type_complexity.stderr new file mode 100644 index 0000000000000..8ca637f722251 --- /dev/null +++ b/tests/ui-toml/type_complexity/type_complexity.stderr @@ -0,0 +1,11 @@ +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:4:10 + | +LL | fn f2(_: (u8, (u8, (u8, (u8, (u8, (u8, u8))))))) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::type-complexity` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::type_complexity)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/type_repetition_in_bounds/clippy.toml b/tests/ui-toml/type_repetition_in_bounds/clippy.toml new file mode 100644 index 0000000000000..2f91866aa9367 --- /dev/null +++ b/tests/ui-toml/type_repetition_in_bounds/clippy.toml @@ -0,0 +1 @@ +max-trait-bounds = 5 diff --git a/tests/ui-toml/type_repetition_in_bounds/main.rs b/tests/ui-toml/type_repetition_in_bounds/main.rs new file mode 100644 index 0000000000000..2454c10382df7 --- /dev/null +++ b/tests/ui-toml/type_repetition_in_bounds/main.rs @@ -0,0 +1,18 @@ +#![warn(clippy::type_repetition_in_bounds)] + +fn f() +where + T: Copy + Clone + Sync + Send + ?Sized + Unpin, + T: PartialEq, +{ +} + +fn f2() +where + T: Copy + Clone + Sync + Send + ?Sized, + T: Unpin + PartialEq, + //~^ ERROR: this type has already been used as a bound predicate +{ +} + +fn main() {} diff --git a/tests/ui-toml/type_repetition_in_bounds/main.stderr b/tests/ui-toml/type_repetition_in_bounds/main.stderr new file mode 100644 index 0000000000000..2ae2984975f4b --- /dev/null +++ b/tests/ui-toml/type_repetition_in_bounds/main.stderr @@ -0,0 +1,12 @@ +error: this type has already been used as a bound predicate + --> $DIR/main.rs:13:5 + | +LL | T: Unpin + PartialEq, + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider combining the bounds: `T: Copy + Clone + Sync + Send + ?Sized + Unpin + PartialEq` + = note: `-D clippy::type-repetition-in-bounds` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::type_repetition_in_bounds)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/unnecessary_box_returns/clippy.toml b/tests/ui-toml/unnecessary_box_returns/clippy.toml new file mode 100644 index 0000000000000..7c3ffc2908f3d --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/clippy.toml @@ -0,0 +1 @@ +unnecessary-box-size = 64 diff --git a/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed new file mode 100644 index 0000000000000..413bc0bf1e378 --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.fixed @@ -0,0 +1,11 @@ +#![warn(clippy::unnecessary_box_returns)] + +fn f() -> [u8; 64] { + //~^ ERROR: boxed return of the sized type `[u8; 64]` + todo!() +} +fn f2() -> Box<[u8; 65]> { + todo!() +} + +fn main() {} diff --git a/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs new file mode 100644 index 0000000000000..b44fbb5544856 --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.rs @@ -0,0 +1,11 @@ +#![warn(clippy::unnecessary_box_returns)] + +fn f() -> Box<[u8; 64]> { + //~^ ERROR: boxed return of the sized type `[u8; 64]` + todo!() +} +fn f2() -> Box<[u8; 65]> { + todo!() +} + +fn main() {} diff --git a/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr new file mode 100644 index 0000000000000..df9aa37ac10f4 --- /dev/null +++ b/tests/ui-toml/unnecessary_box_returns/unnecessary_box_returns.stderr @@ -0,0 +1,12 @@ +error: boxed return of the sized type `[u8; 64]` + --> $DIR/unnecessary_box_returns.rs:3:11 + | +LL | fn f() -> Box<[u8; 64]> { + | ^^^^^^^^^^^^^ help: try: `[u8; 64]` + | + = help: changing this also requires a change to the return expressions in this function + = note: `-D clippy::unnecessary-box-returns` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_box_returns)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/verbose_bit_mask/clippy.toml b/tests/ui-toml/verbose_bit_mask/clippy.toml new file mode 100644 index 0000000000000..55a202eefb964 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/clippy.toml @@ -0,0 +1 @@ +verbose-bit-mask-threshold = 31 diff --git a/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed new file mode 100644 index 0000000000000..437692a4d78c4 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::verbose_bit_mask)] +fn main() { + let v: i32 = 0; + let _ = v & 0b11111 == 0; + let _ = v.trailing_zeros() >= 6; + //~^ ERROR: bit mask could be simplified +} diff --git a/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs new file mode 100644 index 0000000000000..ce102708055a5 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.rs @@ -0,0 +1,7 @@ +#![warn(clippy::verbose_bit_mask)] +fn main() { + let v: i32 = 0; + let _ = v & 0b11111 == 0; + let _ = v & 0b111111 == 0; + //~^ ERROR: bit mask could be simplified +} diff --git a/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr new file mode 100644 index 0000000000000..7377921b42ab1 --- /dev/null +++ b/tests/ui-toml/verbose_bit_mask/verbose_bit_mask.stderr @@ -0,0 +1,11 @@ +error: bit mask could be simplified with a call to `trailing_zeros` + --> $DIR/verbose_bit_mask.rs:5:13 + | +LL | let _ = v & 0b111111 == 0; + | ^^^^^^^^^^^^^^^^^ help: try: `v.trailing_zeros() >= 6` + | + = note: `-D clippy::verbose-bit-mask` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::verbose_bit_mask)]` + +error: aborting due to previous error + diff --git a/tests/ui-toml/wildcard_imports/clippy.toml b/tests/ui-toml/wildcard_imports/clippy.toml new file mode 100644 index 0000000000000..875aaeef6c935 --- /dev/null +++ b/tests/ui-toml/wildcard_imports/clippy.toml @@ -0,0 +1 @@ +warn-on-all-wildcard-imports = true diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed new file mode 100644 index 0000000000000..1752f48856c2b --- /dev/null +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed @@ -0,0 +1,11 @@ +#![warn(clippy::wildcard_imports)] + +mod prelude { + pub const FOO: u8 = 1; +} +use prelude::FOO; +//~^ ERROR: usage of wildcard import + +fn main() { + let _ = FOO; +} diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/tests/ui-toml/wildcard_imports/wildcard_imports.rs new file mode 100644 index 0000000000000..331c2c59c222f --- /dev/null +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.rs @@ -0,0 +1,11 @@ +#![warn(clippy::wildcard_imports)] + +mod prelude { + pub const FOO: u8 = 1; +} +use prelude::*; +//~^ ERROR: usage of wildcard import + +fn main() { + let _ = FOO; +} diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr new file mode 100644 index 0000000000000..13ec3a229ce91 --- /dev/null +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr @@ -0,0 +1,11 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:6:5 + | +LL | use prelude::*; + | ^^^^^^^^^^ help: try: `prelude::FOO` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` + +error: aborting due to previous error + From 6e80db96ea933676ac9b36714e3af9aaa3eedc00 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 24 Sep 2023 16:29:26 +0200 Subject: [PATCH 73/78] use a smaller number in test that fits in usize for 32 bit --- tests/ui-toml/large_stack_frames/clippy.toml | 2 +- tests/ui-toml/large_stack_frames/large_stack_frames.rs | 4 ++-- tests/ui-toml/large_stack_frames/large_stack_frames.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui-toml/large_stack_frames/clippy.toml b/tests/ui-toml/large_stack_frames/clippy.toml index 1b68801b97053..584335dc28fb2 100644 --- a/tests/ui-toml/large_stack_frames/clippy.toml +++ b/tests/ui-toml/large_stack_frames/clippy.toml @@ -1 +1 @@ -stack-size-threshold = 4294967296 +stack-size-threshold = 1000 diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.rs b/tests/ui-toml/large_stack_frames/large_stack_frames.rs index 39b3c571c5c91..39798ffea4942 100644 --- a/tests/ui-toml/large_stack_frames/large_stack_frames.rs +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.rs @@ -7,11 +7,11 @@ fn create_array() -> [u8; N] { } fn f() { - let _x = create_array::<4294967296>(); + let _x = create_array::<1000>(); } fn f2() { //~^ ERROR: this function allocates a large amount of stack space - let _x = create_array::<4294967297>(); + let _x = create_array::<1001>(); } fn main() {} diff --git a/tests/ui-toml/large_stack_frames/large_stack_frames.stderr b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr index ef7d15a3739a0..67ee57ab672d4 100644 --- a/tests/ui-toml/large_stack_frames/large_stack_frames.stderr +++ b/tests/ui-toml/large_stack_frames/large_stack_frames.stderr @@ -3,7 +3,7 @@ error: this function allocates a large amount of stack space | LL | / fn f2() { LL | | -LL | | let _x = create_array::<4294967297>(); +LL | | let _x = create_array::<1001>(); LL | | } | |_^ | From b3e262acfd711f27c7450379d85092ec169388a7 Mon Sep 17 00:00:00 2001 From: jonboh Date: Sun, 24 Sep 2023 18:25:20 +0200 Subject: [PATCH 74/78] add ui-toml test --- tests/ui-toml/enum_variants_threshold0/clippy.toml | 1 + .../enum_variants_threshold0/enum_variants_name_threshold.rs | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 tests/ui-toml/enum_variants_threshold0/clippy.toml create mode 100644 tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs diff --git a/tests/ui-toml/enum_variants_threshold0/clippy.toml b/tests/ui-toml/enum_variants_threshold0/clippy.toml new file mode 100644 index 0000000000000..f85aade6ae87d --- /dev/null +++ b/tests/ui-toml/enum_variants_threshold0/clippy.toml @@ -0,0 +1 @@ +enum-variant-name-threshold = 0 diff --git a/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs b/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs new file mode 100644 index 0000000000000..6918d7528c160 --- /dev/null +++ b/tests/ui-toml/enum_variants_threshold0/enum_variants_name_threshold.rs @@ -0,0 +1,3 @@ +enum Actions {} + +fn main() {} From 0433e458dadc927e645f6307c0253a4b241dfd5b Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 24 Sep 2023 21:37:56 +0200 Subject: [PATCH 75/78] use first instead of get(0) Co-authored-by: Timo <30553356+y21@users.noreply.github.com> --- clippy_lints/src/enum_variants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 85334d09d4b4a..e332f681b6dbd 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -167,7 +167,7 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n return; } - let first = match &def.variants.get(0) { + let first = match def.variants.first() { Some(variant) => variant.ident.name.as_str(), None => return, }; From 5eb7604482f8f3264605c0e440861223576d6faf Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 25 Sep 2023 10:26:33 +0200 Subject: [PATCH 76/78] Bump nightly version -> 2023-09-25 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 9f5116eb73bb2..5ce22b65f0074 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-09-07" +channel = "nightly-2023-09-25" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 903add0c11a0205314bf49fff6851fea3478449c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 25 Sep 2023 10:31:51 +0200 Subject: [PATCH 77/78] Fix dogfood fallout --- clippy_lints/src/doc.rs | 3 ++- clippy_lints/src/raw_strings.rs | 2 +- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index bf2add6aa64f7..e789e0da6797a 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -459,7 +459,7 @@ struct Fragments<'a> { impl Fragments<'_> { fn span(self, cx: &LateContext<'_>, range: Range) -> Option { - source_span_for_markdown_range(cx.tcx, &self.doc, &range, &self.fragments) + source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments) } } @@ -513,6 +513,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; +#[allow(clippy::too_many_lines)] // Only a big match statement fn check_doc<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, valid_idents: &FxHashSet, diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index ba80e37d37ebd..2895595e03908 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -93,7 +93,7 @@ impl EarlyLintPass for RawStrings { diag.span_suggestion( start, "use a string literal instead", - format!("\"{}\"", str), + format!("\"{str}\""), Applicability::MachineApplicable, ); } else { diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 713158c3a55ae..d05d9e7640f0b 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -207,7 +207,7 @@ fn path_segment_certainty( // Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE. if cx.tcx.res_generics_def_id(path_segment.res).is_some() { let generics = cx.tcx.generics_of(def_id); - let count = generics.params.len() - generics.host_effect_index.is_some() as usize; + let count = generics.params.len() - usize::from(generics.host_effect_index.is_some()); let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 { Certainty::Certain(None) } else { From 6d331b7f03164126f46094d8e9c14d4591e79918 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 25 Sep 2023 11:29:11 +0200 Subject: [PATCH 78/78] Update Cargo.lock --- Cargo.lock | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2025e58a8d1d..48cf0c158dd8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -515,7 +515,6 @@ version = "0.1.74" dependencies = [ "clippy_lints", "clippy_utils", - "derive-new", "filetime", "futures", "if_chain", @@ -944,17 +943,6 @@ dependencies = [ "syn 2.0.29", ] -[[package]] -name = "derive-new" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_builder" version = "0.12.0"