From b587871e27504797346a8d54d989c0d2284086d2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 14 Nov 2023 22:17:02 -0500 Subject: [PATCH 01/68] Simplify `get_enclosing_loop_or_multi_call_closure` --- clippy_utils/src/lib.rs | 49 ++++----------------------- clippy_utils/src/ty.rs | 29 ---------------- tests/ui/while_let_on_iterator.fixed | 16 +++++++-- tests/ui/while_let_on_iterator.rs | 14 +++++++- tests/ui/while_let_on_iterator.stderr | 12 +++++-- 5 files changed, 42 insertions(+), 78 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2445c4e0672c1..7a14c64f5ef49 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -113,10 +113,7 @@ use visitors::Visitable; use crate::consts::{constant, mir_to_const, Constant}; use crate::higher::Range; -use crate::ty::{ - adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, - ty_is_fn_once_param, -}; +use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr; use rustc_middle::hir::nested_filter; @@ -1345,46 +1342,12 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>( for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) { match node { Node::Expr(e) => match e.kind { - ExprKind::Closure { .. } => { + ExprKind::Closure { .. } if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind() - && subs.as_closure().kind() == ClosureKind::FnOnce - { - continue; - } - let is_once = walk_to_expr_usage(cx, e, |node, id| { - let Node::Expr(e) = node else { - return None; - }; - match e.kind { - ExprKind::Call(f, _) if f.hir_id == id => Some(()), - ExprKind::Call(f, args) => { - let i = args.iter().position(|arg| arg.hir_id == id)?; - let sig = expr_sig(cx, f)?; - let predicates = sig - .predicates_id() - .map_or(cx.param_env, |id| cx.tcx.param_env(id)) - .caller_bounds(); - sig.input(i).and_then(|ty| { - ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(()) - }) - }, - ExprKind::MethodCall(_, receiver, args, _) => { - let i = std::iter::once(receiver) - .chain(args.iter()) - .position(|arg| arg.hir_id == id)?; - let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?; - let ty = cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i]; - ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(()) - }, - _ => None, - } - }) - .is_some(); - if !is_once { - return Some(e); - } - }, - ExprKind::Loop(..) => return Some(e), + && subs.as_closure().kind() == ClosureKind::FnOnce => {}, + + // Note: A closure's kind is determined by how it's used, not it's captures. + ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e), _ => (), }, Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (), diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 4d50242d81da7..0c7bfa0ea62e5 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -972,35 +972,6 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option< } } -/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`. -pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [ty::Clause<'_>]) -> bool { - let ty::Param(ty) = *ty.kind() else { - return false; - }; - let lang = tcx.lang_items(); - let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id)) = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait()) - else { - return false; - }; - predicates - .iter() - .try_fold(false, |found, p| { - if let ty::ClauseKind::Trait(p) = p.kind().skip_binder() - && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() - && ty.index == self_ty.index - { - // This should use `super_traits_of`, but that's a private function. - if p.trait_ref.def_id == fn_once_id { - return Some(true); - } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id { - return None; - } - } - Some(found) - }) - .unwrap_or(false) -} - /// Comes up with an "at least" guesstimate for the type's size, not taking into /// account the layout of type parameters. pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index d628d2227b719..59b5c858d0460 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -406,7 +406,7 @@ fn issue_8113() { fn fn_once_closure() { let mut it = 0..10; (|| { - for x in it { + for x in it.by_ref() { if x % 2 == 0 { break; } @@ -441,7 +441,19 @@ fn fn_once_closure() { break; } } - }) + }); + + trait MySpecialFnMut: FnOnce() {} + impl MySpecialFnMut for T {} + fn f4(_: impl MySpecialFnMut) {} + let mut it = 0..10; + f4(|| { + for x in it { + if x % 2 == 0 { + break; + } + } + }); } fn main() { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 525dbbaaab66f..559513d56946d 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -441,7 +441,19 @@ fn fn_once_closure() { break; } } - }) + }); + + trait MySpecialFnMut: FnOnce() {} + impl MySpecialFnMut for T {} + fn f4(_: impl MySpecialFnMut) {} + let mut it = 0..10; + f4(|| { + while let Some(x) = it.next() { + if x % 2 == 0 { + break; + } + } + }); } fn main() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index cdc83b8166707..7b9a9dc049a28 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -131,7 +131,7 @@ error: this loop could be written as a `for` loop --> $DIR/while_let_on_iterator.rs:409:9 | LL | while let Some(x) = it.next() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop --> $DIR/while_let_on_iterator.rs:419:9 @@ -152,10 +152,16 @@ LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:449:5 + --> $DIR/while_let_on_iterator.rs:451:9 + | +LL | while let Some(x) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:461:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors From cdc4c697b5de1e4a1d6cb384cec1f1da9449b366 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 14 Nov 2023 22:41:38 -0500 Subject: [PATCH 02/68] Make `expr_use_ctxt` always return `Some` unless the syntax context changes. --- clippy_utils/src/lib.rs | 176 +++++++++++--------------- tests/ui/ignored_unit_patterns.fixed | 7 +- tests/ui/ignored_unit_patterns.rs | 7 +- tests/ui/ignored_unit_patterns.stderr | 18 +-- tests/ui/needless_borrow.fixed | 3 + tests/ui/needless_borrow.rs | 3 + tests/ui/needless_borrow.stderr | 20 ++- 7 files changed, 112 insertions(+), 122 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7a14c64f5ef49..a87fae1024919 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,5 +1,6 @@ #![feature(array_chunks)] #![feature(box_patterns)] +#![feature(control_flow_enum)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(lint_reasons)] @@ -2508,26 +2509,30 @@ pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool { && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests") } -/// Walks the HIR tree from the given expression, up to the node where the value produced by the -/// expression is consumed. Calls the function for every node encountered this way until it returns -/// `Some`. +/// Walks up the HIR tree from the given expression in an attempt to find where the value is +/// consumed. /// -/// This allows walking through `if`, `match`, `break`, block expressions to find where the value -/// produced by the expression is consumed. +/// Termination has three conditions: +/// - The given function returns `Break`. This function will return the value. +/// - The consuming node is found. This function will return `Continue(use_node, child_id)`. +/// - No further parent nodes are found. This will trigger a debug assert or return `None`. +/// +/// This allows walking through `if`, `match`, `break`, and block expressions to find where the +/// value produced by the expression is consumed. pub fn walk_to_expr_usage<'tcx, T>( cx: &LateContext<'tcx>, e: &Expr<'tcx>, - mut f: impl FnMut(Node<'tcx>, HirId) -> Option, -) -> Option { + mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow, +) -> Option, HirId)>> { let map = cx.tcx.hir(); let mut iter = map.parent_iter(e.hir_id); let mut child_id = e.hir_id; while let Some((parent_id, parent)) = iter.next() { - if let Some(x) = f(parent, child_id) { - return Some(x); + if let ControlFlow::Break(x) = f(parent_id, parent, child_id) { + return Some(ControlFlow::Break(x)); } - let parent = match parent { + let parent_expr = match parent { Node::Expr(e) => e, Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => { child_id = parent_id; @@ -2537,18 +2542,19 @@ pub fn walk_to_expr_usage<'tcx, T>( child_id = parent_id; continue; }, - _ => return None, + _ => return Some(ControlFlow::Continue((parent, child_id))), }; - match parent.kind { + match parent_expr.kind { ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id, ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => { child_id = id; iter = map.parent_iter(id); }, - ExprKind::Block(..) => child_id = parent_id, - _ => return None, + ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id, + _ => return Some(ControlFlow::Continue((parent, child_id))), } } + debug_assert!(false, "no parent node found for `{child_id:?}`"); None } @@ -2592,6 +2598,8 @@ pub enum ExprUseNode<'tcx> { Callee, /// Access of a field. FieldAccess(Ident), + Expr, + Other, } impl<'tcx> ExprUseNode<'tcx> { /// Checks if the value is returned from the function. @@ -2668,144 +2676,104 @@ impl<'tcx> ExprUseNode<'tcx> { let sig = cx.tcx.fn_sig(id).skip_binder(); Some(DefinedTy::Mir(cx.tcx.param_env(id).and(sig.input(i)))) }, - Self::Local(_) | Self::FieldAccess(..) | Self::Callee => None, + Self::Local(_) | Self::FieldAccess(..) | Self::Callee | Self::Expr | Self::Other => None, } } } /// Gets the context an expression's value is used in. -#[expect(clippy::too_many_lines)] pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option> { let mut adjustments = [].as_slice(); let mut is_ty_unified = false; let mut moved_before_use = false; let ctxt = e.span.ctxt(); - walk_to_expr_usage(cx, e, &mut |parent, child_id| { - // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead. + walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| { if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) { adjustments = cx.typeck_results().expr_adjustments(e); } - match parent { - Node::Local(l) if l.span.ctxt() == ctxt => Some(ExprUseCtxt { - node: ExprUseNode::Local(l), - adjustments, - is_ty_unified, - moved_before_use, - }), + if !cx.tcx.hir().opt_span(parent_id).is_some_and(|x| x.ctxt() == ctxt) { + return ControlFlow::Break(()); + } + if let Node::Expr(e) = parent { + match e.kind { + ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => { + is_ty_unified = true; + moved_before_use = true; + }, + ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => { + is_ty_unified = true; + moved_before_use = true; + }, + ExprKind::Block(..) => moved_before_use = true, + _ => {}, + } + } + ControlFlow::Continue(()) + })? + .continue_value() + .map(|(use_node, child_id)| { + let node = match use_node { + Node::Local(l) => ExprUseNode::Local(l), + Node::ExprField(field) => ExprUseNode::Field(field), + Node::Item(&Item { kind: ItemKind::Static(..) | ItemKind::Const(..), owner_id, - span, .. }) | Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), owner_id, - span, .. }) | Node::ImplItem(&ImplItem { kind: ImplItemKind::Const(..), owner_id, - span, .. - }) if span.ctxt() == ctxt => Some(ExprUseCtxt { - node: ExprUseNode::ConstStatic(owner_id), - adjustments, - is_ty_unified, - moved_before_use, - }), + }) => ExprUseNode::ConstStatic(owner_id), Node::Item(&Item { kind: ItemKind::Fn(..), owner_id, - span, .. }) | Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), owner_id, - span, .. }) | Node::ImplItem(&ImplItem { kind: ImplItemKind::Fn(..), owner_id, - span, .. - }) if span.ctxt() == ctxt => Some(ExprUseCtxt { - node: ExprUseNode::Return(owner_id), - adjustments, - is_ty_unified, - moved_before_use, - }), - - Node::ExprField(field) if field.span.ctxt() == ctxt => Some(ExprUseCtxt { - node: ExprUseNode::Field(field), - adjustments, - is_ty_unified, - moved_before_use, - }), + }) => ExprUseNode::Return(owner_id), - Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind { - ExprKind::Ret(_) => Some(ExprUseCtxt { - node: ExprUseNode::Return(OwnerId { - def_id: cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - }), - adjustments, - is_ty_unified, - moved_before_use, - }), - ExprKind::Closure(closure) => Some(ExprUseCtxt { - node: ExprUseNode::Return(OwnerId { def_id: closure.def_id }), - adjustments, - is_ty_unified, - moved_before_use, - }), - ExprKind::Call(func, args) => Some(ExprUseCtxt { - node: match args.iter().position(|arg| arg.hir_id == child_id) { - Some(i) => ExprUseNode::FnArg(func, i), - None => ExprUseNode::Callee, - }, - adjustments, - is_ty_unified, - moved_before_use, + Node::Expr(use_expr) => match use_expr.kind { + ExprKind::Ret(_) => ExprUseNode::Return(OwnerId { + def_id: cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), }), - ExprKind::MethodCall(name, _, args, _) => Some(ExprUseCtxt { - node: ExprUseNode::MethodArg( - parent.hir_id, - name.args, - args.iter().position(|arg| arg.hir_id == child_id).map_or(0, |i| i + 1), - ), - adjustments, - is_ty_unified, - moved_before_use, - }), - ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(ExprUseCtxt { - node: ExprUseNode::FieldAccess(name), - adjustments, - is_ty_unified, - moved_before_use, - }), - ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => { - is_ty_unified = true; - moved_before_use = true; - None - }, - ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => { - is_ty_unified = true; - moved_before_use = true; - None + ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }), + ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == child_id) { + Some(i) => ExprUseNode::FnArg(func, i), + None => ExprUseNode::Callee, }, - ExprKind::Block(..) => { - moved_before_use = true; - None - }, - _ => None, + ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg( + use_expr.hir_id, + name.args, + args.iter().position(|arg| arg.hir_id == child_id).map_or(0, |i| i + 1), + ), + ExprKind::Field(child, name) if child.hir_id == e.hir_id => ExprUseNode::FieldAccess(name), + _ => ExprUseNode::Expr, }, - _ => None, + _ => ExprUseNode::Other, + }; + ExprUseCtxt { + node, + adjustments, + is_ty_unified, + moved_before_use, } }) } diff --git a/tests/ui/ignored_unit_patterns.fixed b/tests/ui/ignored_unit_patterns.fixed index 707a0e76e4ebc..118f0b4889529 100644 --- a/tests/ui/ignored_unit_patterns.fixed +++ b/tests/ui/ignored_unit_patterns.fixed @@ -1,6 +1,11 @@ //@aux-build:proc_macro_derive.rs #![warn(clippy::ignored_unit_patterns)] -#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] +#![allow( + clippy::let_unit_value, + clippy::redundant_pattern_matching, + clippy::single_match, + clippy::needless_borrow +)] fn foo() -> Result<(), ()> { unimplemented!() diff --git a/tests/ui/ignored_unit_patterns.rs b/tests/ui/ignored_unit_patterns.rs index 544f2b8f6929c..92feb9e6c2814 100644 --- a/tests/ui/ignored_unit_patterns.rs +++ b/tests/ui/ignored_unit_patterns.rs @@ -1,6 +1,11 @@ //@aux-build:proc_macro_derive.rs #![warn(clippy::ignored_unit_patterns)] -#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] +#![allow( + clippy::let_unit_value, + clippy::redundant_pattern_matching, + clippy::single_match, + clippy::needless_borrow +)] fn foo() -> Result<(), ()> { unimplemented!() diff --git a/tests/ui/ignored_unit_patterns.stderr b/tests/ui/ignored_unit_patterns.stderr index 05c8f281e554d..18ca7ebbcf2c4 100644 --- a/tests/ui/ignored_unit_patterns.stderr +++ b/tests/ui/ignored_unit_patterns.stderr @@ -1,5 +1,5 @@ error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:11:12 + --> $DIR/ignored_unit_patterns.rs:16:12 | LL | Ok(_) => {}, | ^ help: use `()` instead of `_`: `()` @@ -8,49 +8,49 @@ LL | Ok(_) => {}, = help: to override `-D warnings` add `#[allow(clippy::ignored_unit_patterns)]` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:12:13 + --> $DIR/ignored_unit_patterns.rs:17:13 | LL | Err(_) => {}, | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:14:15 + --> $DIR/ignored_unit_patterns.rs:19:15 | LL | if let Ok(_) = foo() {} | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:16:28 + --> $DIR/ignored_unit_patterns.rs:21:28 | LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:22:16 + --> $DIR/ignored_unit_patterns.rs:27:16 | LL | Ok(_) => {}, | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:24:17 + --> $DIR/ignored_unit_patterns.rs:29:17 | LL | Err(_) => {}, | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:36:9 + --> $DIR/ignored_unit_patterns.rs:41:9 | LL | let _ = foo().unwrap(); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:45:13 + --> $DIR/ignored_unit_patterns.rs:50:13 | LL | (1, _) => unimplemented!(), | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:52:13 + --> $DIR/ignored_unit_patterns.rs:57:13 | LL | for (x, _) in v { | ^ help: use `()` instead of `_`: `()` diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index ff1e2dc88756b..23e8bf8a468fd 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -131,6 +131,9 @@ fn main() { 0 } } + + // issue #11786 + let x: (&str,) = ("",); } #[allow(clippy::needless_borrowed_reference)] diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 597021539acfa..27771a8f15b30 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -131,6 +131,9 @@ fn main() { 0 } } + + // issue #11786 + let x: (&str,) = (&"",); } #[allow(clippy::needless_borrowed_reference)] diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 44552ee6abea1..a21ed8382c14e 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -121,41 +121,47 @@ error: this expression creates a reference which is immediately dereferenced by LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` +error: this expression creates a reference which is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:136:23 + | +LL | let x: (&str,) = (&"",); + | ^^^ help: change this to: `""` + error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:175:13 + --> $DIR/needless_borrow.rs:178: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:184:13 + --> $DIR/needless_borrow.rs:187:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:221:22 + --> $DIR/needless_borrow.rs:224:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:228:22 + --> $DIR/needless_borrow.rs:231:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:232:22 + --> $DIR/needless_borrow.rs:235:22 | LL | let _ = &mut (&mut x.u).x; | ^^^^^^^^^^ help: change this to: `x.u` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:233:22 + --> $DIR/needless_borrow.rs:236:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors From 57dd25e2ffbf9c7abd63fcdf057facff7a46c342 Mon Sep 17 00:00:00 2001 From: Quinn Sinclair Date: Tue, 26 Dec 2023 20:34:00 +0200 Subject: [PATCH 03/68] FP: `needless_return_with_question_mark` with implicit Error Conversion Return with a question mark was triggered in situations where the `?` desuraging was performing error conversion via `Into`/`From`. The desugared `?` produces a match over an expression with type `std::ops::ControlFlow` with `B:Result` and `C:Result<_, E':Error>`, and the arms perform the conversion. The patch adds another check in the lint that checks that `E == E'`. If `E == E'`, then the `?` is indeed unnecessary. changelog: False Positive: `needless_return_with_question_mark` when implicit Error Conversion occurs. --- clippy_lints/src/returns.rs | 28 ++++++++++++++++++- .../needless_return_with_question_mark.fixed | 27 ++++++++++++++++++ .../ui/needless_return_with_question_mark.rs | 27 ++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2293b53b42b91..2d4e7d269fd2c 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -176,12 +176,37 @@ fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { }) } +/// +/// The expression of the desugared `try` operator is a match over an expression with type: +/// `ControlFlow, B:Result<_, E'>>`, with final type `B`. +/// If E and E' are the same type, then there is no error conversion happening. +/// Error conversion happens when E can be transformed into E' via a `From` or `Into` conversion. +fn desugar_expr_performs_error_conversion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + + if let ty::Adt(_, generics) = ty.kind() + && let Some(brk) = generics.first() + && let Some(cont) = generics.get(1) + && let Some(brk_type) = brk.as_type() + && let Some(cont_type) = cont.as_type() + && let ty::Adt(_, brk_generics) = brk_type.kind() + && let ty::Adt(_, cont_generics) = cont_type.kind() + && let Some(brk_err) = brk_generics.get(1) + && let Some(cont_err) = cont_generics.get(1) + && let Some(brk_err_type) = brk_err.as_type() + && let Some(cont_err_type) = cont_err.as_type() + { + return brk_err_type != cont_err_type; + } + false +} + impl<'tcx> LateLintPass<'tcx> for Return { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if !in_external_macro(cx.sess(), stmt.span) && let StmtKind::Semi(expr) = stmt.kind && let ExprKind::Ret(Some(ret)) = expr.kind - && let ExprKind::Match(.., MatchSource::TryDesugar(_)) = ret.kind + && let ExprKind::Match(match_expr, _, MatchSource::TryDesugar(..)) = ret.kind // Ensure this is not the final stmt, otherwise removing it would cause a compile error && let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id)) && let ItemKind::Fn(_, _, body) = item.kind @@ -192,6 +217,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { && final_stmt.hir_id != stmt.hir_id && !is_from_proc_macro(cx, expr) && !stmt_needs_never_type(cx, stmt.hir_id) + && !desugar_expr_performs_error_conversion(cx, match_expr) { span_lint_and_sugg( cx, diff --git a/tests/ui/needless_return_with_question_mark.fixed b/tests/ui/needless_return_with_question_mark.fixed index 0147c73a94b24..8a97d8604a559 100644 --- a/tests/ui/needless_return_with_question_mark.fixed +++ b/tests/ui/needless_return_with_question_mark.fixed @@ -77,3 +77,30 @@ fn issue11616() -> Result<(), ()> { }; Ok(()) } + +/// This is a false positive that occurs because of the way `?` is handled. +/// The `?` operator is also doing a conversion from `Result` to `Result`. +/// In this case the conversion is needed, and thus the `?` operator is also needed. +fn issue11982() { + mod bar { + pub struct Error; + pub fn foo(_: bool) -> Result<(), Error> { + Ok(()) + } + } + + pub struct Error; + + impl From for Error { + fn from(_: bar::Error) -> Self { + Error + } + } + + fn foo(ok: bool) -> Result<(), Error> { + if !ok { + return bar::foo(ok).map(|_| Ok::<(), Error>(()))?; + }; + Ok(()) + } +} diff --git a/tests/ui/needless_return_with_question_mark.rs b/tests/ui/needless_return_with_question_mark.rs index 66e1f438f8ce0..be15b000f5d5d 100644 --- a/tests/ui/needless_return_with_question_mark.rs +++ b/tests/ui/needless_return_with_question_mark.rs @@ -77,3 +77,30 @@ fn issue11616() -> Result<(), ()> { }; Ok(()) } + +/// This is a false positive that occurs because of the way `?` is handled. +/// The `?` operator is also doing a conversion from `Result` to `Result`. +/// In this case the conversion is needed, and thus the `?` operator is also needed. +fn issue11982() { + mod bar { + pub struct Error; + pub fn foo(_: bool) -> Result<(), Error> { + Ok(()) + } + } + + pub struct Error; + + impl From for Error { + fn from(_: bar::Error) -> Self { + Error + } + } + + fn foo(ok: bool) -> Result<(), Error> { + if !ok { + return bar::foo(ok).map(|_| Ok::<(), Error>(()))?; + }; + Ok(()) + } +} From 090c228873c8109e82857145f71f34c0ccdf28dd Mon Sep 17 00:00:00 2001 From: cocodery Date: Wed, 3 Jan 2024 22:12:27 +0800 Subject: [PATCH 04/68] Fix/Issue11932: assert* in multi-condition after unrolling will cause lint emit warning --- clippy_lints/src/booleans.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index e11f83f226034..2d0af1017d586 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -502,6 +502,8 @@ impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> { && !inner.span.from_expansion() && let Some(suggestion) = simplify_not(self.cx, inner) && self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow + && let Some(snippet) = snippet_opt(self.cx, expr.span) + && !snippet.contains("assert") { span_lint_and_sugg( self.cx, From b5a219262891aaa65fc583424a8ab1bba66e8746 Mon Sep 17 00:00:00 2001 From: Yuxiang Qiu Date: Wed, 3 Jan 2024 13:05:55 -0500 Subject: [PATCH 05/68] fix: incorrect suggestions generated by `manual_retain` lint --- clippy_lints/src/manual_retain.rs | 141 ++++++++++++++++++++--------- tests/ui/manual_retain.fixed | 56 ++++++++++++ tests/ui/manual_retain.rs | 56 ++++++++++++ tests/ui/manual_retain.stderr | 142 +++++++++++++++++++++++++----- 4 files changed, 331 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 1fe247dacb932..6f15fca089eba 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -11,6 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_semver::RustcVersion; use rustc_session::impl_lint_pass; use rustc_span::symbol::sym; +use rustc_span::Span; const ACCEPTABLE_METHODS: [&[&str]; 5] = [ &paths::BINARYHEAP_ITER, @@ -28,6 +29,7 @@ const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option); 7] = [ (sym::Vec, None), (sym::VecDeque, None), ]; +const MAP_TYPES: [rustc_span::Symbol; 2] = [sym::BTreeMap, sym::HashMap]; declare_clippy_lint! { /// ### What it does @@ -44,6 +46,7 @@ declare_clippy_lint! { /// ```no_run /// let mut vec = vec![0, 1, 2]; /// vec.retain(|x| x % 2 == 0); + /// vec.retain(|x| x % 2 == 0); /// ``` #[clippy::version = "1.64.0"] pub MANUAL_RETAIN, @@ -74,9 +77,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain { && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id) { - check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); - check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); - check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv); + check_into_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv); + check_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv); + check_to_owned(cx, left_expr, target_expr, parent_expr.span, &self.msrv); } } @@ -85,9 +88,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain { fn check_into_iter( cx: &LateContext<'_>, - parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, + parent_expr_span: Span, msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind @@ -98,16 +101,39 @@ fn check_into_iter( && Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn() && match_acceptable_type(cx, left_expr, msrv) && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) + && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = target_expr.kind + && let hir::ExprKind::Closure(closure) = closure_expr.kind + && let filter_body = cx.tcx.hir().body(closure.body) + && let [filter_params] = filter_body.params { - suggest(cx, parent_expr, left_expr, target_expr); + if match_map_type(cx, left_expr) { + if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind { + if let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body) { + make_span_lint_and_sugg(cx, parent_expr_span, sugg); + } + } + // Cannot lint other cases because `retain` requires two parameters + } else { + // Can always move because `retain` and `filter` have the same bound on the predicate + // for other types + make_span_lint_and_sugg( + cx, + parent_expr_span, + format!( + "{}.retain({})", + snippet(cx, left_expr.span, ".."), + snippet(cx, closure_expr.span, "..") + ), + ); + } } } fn check_iter( cx: &LateContext<'_>, - parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, + parent_expr_span: Span, msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind @@ -122,16 +148,50 @@ fn check_iter( && match_acceptable_def_path(cx, iter_expr_def_id) && match_acceptable_type(cx, left_expr, msrv) && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) + && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind + && let hir::ExprKind::Closure(closure) = closure_expr.kind + && let filter_body = cx.tcx.hir().body(closure.body) + && let [filter_params] = filter_body.params { - suggest(cx, parent_expr, left_expr, filter_expr); + match filter_params.pat.kind { + // hir::PatKind::Binding(_, _, _, None) => { + // // Be conservative now. Do nothing here. + // // TODO: Ideally, we can rewrite the lambda by stripping one level of reference + // }, + hir::PatKind::Tuple([_, _], _) => { + // the `&&` reference for the `filter` method will be auto derefed to `ref` + // so, we can directly use the lambda + // https://doc.rust-lang.org/reference/patterns.html#binding-modes + make_span_lint_and_sugg( + cx, + parent_expr_span, + format!( + "{}.retain({})", + snippet(cx, left_expr.span, ".."), + snippet(cx, closure_expr.span, "..") + ), + ); + }, + hir::PatKind::Ref(pat, _) => make_span_lint_and_sugg( + cx, + parent_expr_span, + format!( + "{}.retain(|{}| {})", + snippet(cx, left_expr.span, ".."), + snippet(cx, pat.span, ".."), + snippet(cx, filter_body.value.span, "..") + ), + ), + _ => {}, + } } } fn check_to_owned( cx: &LateContext<'_>, - parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, + parent_expr_span: Span, msrv: &Msrv, ) { if msrv.meets(msrvs::STRING_RETAIN) @@ -147,43 +207,25 @@ fn check_to_owned( && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() && is_type_lang_item(cx, ty, hir::LangItem::String) && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) - { - suggest(cx, parent_expr, left_expr, filter_expr); - } -} - -fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, filter_expr: &hir::Expr<'_>) { - if let hir::ExprKind::MethodCall(_, _, [closure], _) = filter_expr.kind - && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = closure.kind - && let filter_body = cx.tcx.hir().body(body) + && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind + && let hir::ExprKind::Closure(closure) = closure_expr.kind + && let filter_body = cx.tcx.hir().body(closure.body) && let [filter_params] = filter_body.params - && let Some(sugg) = match filter_params.pat.kind { - hir::PatKind::Binding(_, _, filter_param_ident, None) => Some(format!( - "{}.retain(|{filter_param_ident}| {})", - snippet(cx, left_expr.span, ".."), - snippet(cx, filter_body.value.span, "..") - )), - hir::PatKind::Tuple([key_pat, value_pat], _) => make_sugg(cx, key_pat, value_pat, left_expr, filter_body), - hir::PatKind::Ref(pat, _) => match pat.kind { - hir::PatKind::Binding(_, _, filter_param_ident, None) => Some(format!( - "{}.retain(|{filter_param_ident}| {})", + { + if let hir::PatKind::Ref(pat, _) = filter_params.pat.kind { + make_span_lint_and_sugg( + cx, + parent_expr_span, + format!( + "{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), + snippet(cx, pat.span, ".."), snippet(cx, filter_body.value.span, "..") - )), - _ => None, - }, - _ => None, + ), + ); } - { - span_lint_and_sugg( - cx, - MANUAL_RETAIN, - parent_expr.span, - "this expression can be written more simply using `.retain()`", - "consider calling `.retain()` instead", - sugg, - Applicability::MachineApplicable, - ); + // Be conservative now. Do nothing for the `Binding` case. + // TODO: Ideally, we can rewrite the lambda by stripping one level of reference } } @@ -229,3 +271,20 @@ fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv)) }) } + +fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); + MAP_TYPES.iter().any(|ty| is_type_diagnostic_item(cx, expr_ty, *ty)) +} + +fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) { + span_lint_and_sugg( + cx, + MANUAL_RETAIN, + span, + "this expression can be written more simply using `.retain()`", + "consider calling `.retain()` instead", + sugg, + Applicability::MachineApplicable, + ); +} diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index 4dea3e8bfe68a..a0a697a3fb336 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -14,6 +14,9 @@ fn main() { _msrv_153(); _msrv_126(); _msrv_118(); + + issue_10393(); + issue_12081(); } fn binary_heap_retain() { @@ -23,6 +26,11 @@ fn binary_heap_retain() { binary_heap.retain(|x| x % 2 == 0); binary_heap.retain(|x| x % 2 == 0); + // Do lint, because we use pattern matching + let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]); + tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|(x, y)| *x == 0); + // Do not lint, because type conversion is performed binary_heap = binary_heap .into_iter() @@ -55,6 +63,9 @@ fn btree_map_retain() { btree_map.retain(|_, &mut v| v % 2 == 0); btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0)); + // Do not lint, because the parameters are not matched in tuple pattern + btree_map = btree_map.into_iter().filter(|t| t.0 % 2 == 0).collect(); + // Do not lint. btree_map = btree_map .into_iter() @@ -76,6 +87,11 @@ fn btree_set_retain() { btree_set.retain(|x| x % 2 == 0); btree_set.retain(|x| x % 2 == 0); + // Do lint, because we use pattern matching + let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]); + tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|(x, y)| *x == 0); + // Do not lint, because type conversion is performed btree_set = btree_set .iter() @@ -108,6 +124,9 @@ fn hash_map_retain() { hash_map.retain(|_, &mut v| v % 2 == 0); hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0)); + // Do not lint, because the parameters are not matched in tuple pattern + hash_map = hash_map.into_iter().filter(|t| t.0 % 2 == 0).collect(); + // Do not lint. hash_map = hash_map .into_iter() @@ -128,6 +147,11 @@ fn hash_set_retain() { hash_set.retain(|x| x % 2 == 0); hash_set.retain(|x| x % 2 == 0); + // Do lint, because we use pattern matching + let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]); + tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|(x, y)| *x == 0); + // Do not lint, because type conversion is performed hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect::>(); hash_set = hash_set @@ -157,6 +181,9 @@ fn string_retain() { // Do lint. s.retain(|c| c != 'o'); + // Do not lint, because we need to rewrite the lambda + s = s.chars().filter(|c| *c != 'o').to_owned().collect(); + // Do not lint, because this expression is not assign. let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect(); @@ -171,6 +198,11 @@ fn vec_retain() { vec.retain(|x| x % 2 == 0); vec.retain(|x| x % 2 == 0); + // Do lint, because we use pattern matching + let mut tuples = vec![(0, 1), (1, 2), (2, 3)]; + tuples.retain(|(ref x, ref y)| *x == 0); + tuples.retain(|(x, y)| *x == 0); + // Do not lint, because type conversion is performed vec = vec.into_iter().filter(|x| x % 2 == 0).collect::>(); vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect::>(); @@ -246,3 +278,27 @@ fn _msrv_118() { let mut hash_map: HashMap = (0..8).map(|x| (x, x * 10)).collect(); hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); } + +fn issue_10393() { + // Do lint + let mut vec = vec![(0, 1), (1, 2), (2, 3)]; + vec.retain(|(x, y)| *x == 0); + + // Do lint + let mut tuples = vec![(true, -2), (false, 3)]; + tuples.retain(|(_, n)| *n > 0); +} + +fn issue_12081() { + let mut vec = vec![0, 1, 2]; + + // Do lint + vec.retain(|&x| x == 0); + vec.retain(|&x| x == 0); + vec.retain(|&x| x == 0); + + // Do lint + vec.retain(|x| *x == 0); + vec.retain(|x| *x == 0); + vec.retain(|x| *x == 0); +} diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index d839550f33a22..7e2c597c2c41e 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -14,6 +14,9 @@ fn main() { _msrv_153(); _msrv_126(); _msrv_118(); + + issue_10393(); + issue_12081(); } fn binary_heap_retain() { @@ -23,6 +26,11 @@ fn binary_heap_retain() { binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).copied().collect(); binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().collect(); + // Do lint, because we use pattern matching + let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]); + tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); + // Do not lint, because type conversion is performed binary_heap = binary_heap .into_iter() @@ -58,6 +66,9 @@ fn btree_map_retain() { .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0)) .collect(); + // Do not lint, because the parameters are not matched in tuple pattern + btree_map = btree_map.into_iter().filter(|t| t.0 % 2 == 0).collect(); + // Do not lint. btree_map = btree_map .into_iter() @@ -79,6 +90,11 @@ fn btree_set_retain() { btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); + // Do lint, because we use pattern matching + let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]); + tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); + // Do not lint, because type conversion is performed btree_set = btree_set .iter() @@ -114,6 +130,9 @@ fn hash_map_retain() { .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0)) .collect(); + // Do not lint, because the parameters are not matched in tuple pattern + hash_map = hash_map.into_iter().filter(|t| t.0 % 2 == 0).collect(); + // Do not lint. hash_map = hash_map .into_iter() @@ -134,6 +153,11 @@ fn hash_set_retain() { hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); + // Do lint, because we use pattern matching + let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]); + tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); + // Do not lint, because type conversion is performed hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect::>(); hash_set = hash_set @@ -163,6 +187,9 @@ fn string_retain() { // Do lint. s = s.chars().filter(|&c| c != 'o').to_owned().collect(); + // Do not lint, because we need to rewrite the lambda + s = s.chars().filter(|c| *c != 'o').to_owned().collect(); + // Do not lint, because this expression is not assign. let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect(); @@ -177,6 +204,11 @@ fn vec_retain() { vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); + // Do lint, because we use pattern matching + let mut tuples = vec![(0, 1), (1, 2), (2, 3)]; + tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); + // Do not lint, because type conversion is performed vec = vec.into_iter().filter(|x| x % 2 == 0).collect::>(); vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect::>(); @@ -252,3 +284,27 @@ fn _msrv_118() { let mut hash_map: HashMap = (0..8).map(|x| (x, x * 10)).collect(); hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); } + +fn issue_10393() { + // Do lint + let mut vec = vec![(0, 1), (1, 2), (2, 3)]; + vec = vec.into_iter().filter(|(x, y)| *x == 0).collect(); + + // Do lint + let mut tuples = vec![(true, -2), (false, 3)]; + tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect(); +} + +fn issue_12081() { + let mut vec = vec![0, 1, 2]; + + // Do lint + vec = vec.iter().filter(|&&x| x == 0).copied().collect(); + vec = vec.iter().filter(|&&x| x == 0).cloned().collect(); + vec = vec.into_iter().filter(|&x| x == 0).collect(); + + // Do lint + vec = vec.iter().filter(|&x| *x == 0).copied().collect(); + vec = vec.iter().filter(|&x| *x == 0).cloned().collect(); + vec = vec.into_iter().filter(|x| *x == 0).collect(); +} diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index 0c5b1383b6aed..eca065f5335c1 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -1,5 +1,5 @@ error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:22:5 + --> $DIR/manual_retain.rs:25:5 | LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` @@ -8,31 +8,43 @@ LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); = help: to override `-D warnings` add `#[allow(clippy::manual_retain)]` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:23:5 + --> $DIR/manual_retain.rs:26:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:24:5 + --> $DIR/manual_retain.rs:27:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:54:5 + --> $DIR/manual_retain.rs:31:5 + | +LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:32:5 + | +LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:62:5 | LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:55:5 + --> $DIR/manual_retain.rs:63:5 | LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:56:5 + --> $DIR/manual_retain.rs:64:5 | LL | / btree_map = btree_map LL | | .into_iter() @@ -41,37 +53,49 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:78:5 + --> $DIR/manual_retain.rs:89:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:79:5 + --> $DIR/manual_retain.rs:90:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:80:5 + --> $DIR/manual_retain.rs:91:5 | LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:110:5 + --> $DIR/manual_retain.rs:95:5 + | +LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:96:5 + | +LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:126:5 | LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:111:5 + --> $DIR/manual_retain.rs:127:5 | LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:112:5 + --> $DIR/manual_retain.rs:128:5 | LL | / hash_map = hash_map LL | | .into_iter() @@ -80,64 +104,136 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:133:5 + --> $DIR/manual_retain.rs:152:5 | LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:134:5 + --> $DIR/manual_retain.rs:153:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:135:5 + --> $DIR/manual_retain.rs:154:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:164:5 + --> $DIR/manual_retain.rs:158:5 + | +LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:159:5 + | +LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:188:5 | LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:176:5 + --> $DIR/manual_retain.rs:203:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:177:5 + --> $DIR/manual_retain.rs:204:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:178:5 + --> $DIR/manual_retain.rs:205:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:200:5 + --> $DIR/manual_retain.rs:209:5 + | +LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:210:5 + | +LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:232:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:201:5 + --> $DIR/manual_retain.rs:233:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:202:5 + --> $DIR/manual_retain.rs:234:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` -error: aborting due to 22 previous errors +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:291:5 + | +LL | vec = vec.into_iter().filter(|(x, y)| *x == 0).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|(x, y)| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:295:5 + | +LL | tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(_, n)| *n > 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:302:5 + | +LL | vec = vec.iter().filter(|&&x| x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:303:5 + | +LL | vec = vec.iter().filter(|&&x| x == 0).cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:304:5 + | +LL | vec = vec.into_iter().filter(|&x| x == 0).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:307:5 + | +LL | vec = vec.iter().filter(|&x| *x == 0).copied().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:308:5 + | +LL | vec = vec.iter().filter(|&x| *x == 0).cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` + +error: this expression can be written more simply using `.retain()` + --> $DIR/manual_retain.rs:309:5 + | +LL | vec = vec.into_iter().filter(|x| *x == 0).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` + +error: aborting due to 38 previous errors From 03b3a16a8cac5c1573b630eb71c14ba2603af223 Mon Sep 17 00:00:00 2001 From: Yuxiang Qiu Date: Thu, 4 Jan 2024 15:29:44 -0500 Subject: [PATCH 06/68] test: add more test cases --- tests/ui/manual_retain.fixed | 13 ++++++++++--- tests/ui/manual_retain.rs | 13 ++++++++++--- tests/ui/manual_retain.stderr | 32 ++++++++++++++++---------------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index a0a697a3fb336..5540029bf6b1f 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -181,9 +181,6 @@ fn string_retain() { // Do lint. s.retain(|c| c != 'o'); - // Do not lint, because we need to rewrite the lambda - s = s.chars().filter(|c| *c != 'o').to_owned().collect(); - // Do not lint, because this expression is not assign. let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect(); @@ -289,6 +286,16 @@ fn issue_10393() { tuples.retain(|(_, n)| *n > 0); } +fn issue_11457() { + // Do not lint, as we need to modify the closure + let mut vals = vec![1, 2, 3, 4]; + vals = vals.iter().filter(|v| **v != 1).cloned().collect(); + + // Do not lint, as we need to modify the closure + let mut s = String::from("foobar"); + s = s.chars().filter(|c| *c != 'o').to_owned().collect(); +} + fn issue_12081() { let mut vec = vec![0, 1, 2]; diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 7e2c597c2c41e..cee641d9d65f9 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -187,9 +187,6 @@ fn string_retain() { // Do lint. s = s.chars().filter(|&c| c != 'o').to_owned().collect(); - // Do not lint, because we need to rewrite the lambda - s = s.chars().filter(|c| *c != 'o').to_owned().collect(); - // Do not lint, because this expression is not assign. let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect(); @@ -295,6 +292,16 @@ fn issue_10393() { tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect(); } +fn issue_11457() { + // Do not lint, as we need to modify the closure + let mut vals = vec![1, 2, 3, 4]; + vals = vals.iter().filter(|v| **v != 1).cloned().collect(); + + // Do not lint, as we need to modify the closure + let mut s = String::from("foobar"); + s = s.chars().filter(|c| *c != 'o').to_owned().collect(); +} + fn issue_12081() { let mut vec = vec![0, 1, 2]; diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index eca065f5335c1..2c872f3b430e8 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -140,97 +140,97 @@ LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:203:5 + --> $DIR/manual_retain.rs:200:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:204:5 + --> $DIR/manual_retain.rs:201:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:205:5 + --> $DIR/manual_retain.rs:202:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:209:5 + --> $DIR/manual_retain.rs:206:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:210:5 + --> $DIR/manual_retain.rs:207:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:232:5 + --> $DIR/manual_retain.rs:229:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:233:5 + --> $DIR/manual_retain.rs:230:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:234:5 + --> $DIR/manual_retain.rs:231:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:291:5 + --> $DIR/manual_retain.rs:288:5 | LL | vec = vec.into_iter().filter(|(x, y)| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:295:5 + --> $DIR/manual_retain.rs:292:5 | LL | tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(_, n)| *n > 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:302:5 + --> $DIR/manual_retain.rs:309:5 | LL | vec = vec.iter().filter(|&&x| x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:303:5 + --> $DIR/manual_retain.rs:310:5 | LL | vec = vec.iter().filter(|&&x| x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:304:5 + --> $DIR/manual_retain.rs:311:5 | LL | vec = vec.into_iter().filter(|&x| x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:307:5 + --> $DIR/manual_retain.rs:314:5 | LL | vec = vec.iter().filter(|&x| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:308:5 + --> $DIR/manual_retain.rs:315:5 | LL | vec = vec.iter().filter(|&x| *x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:309:5 + --> $DIR/manual_retain.rs:316:5 | LL | vec = vec.into_iter().filter(|x| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` From bd6e9202b47ec68841d58b1574361e09c5d7775b Mon Sep 17 00:00:00 2001 From: cocodery Date: Sat, 6 Jan 2024 14:12:41 +0800 Subject: [PATCH 07/68] modify check that any macros will be ingored in this lint, and add test --- clippy_lints/src/booleans.rs | 3 +-- tests/ui/nonminimal_bool.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 2d0af1017d586..2d1c250ace905 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -499,11 +499,10 @@ struct NotSimplificationVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind + && !expr.span.from_expansion() && !inner.span.from_expansion() && let Some(suggestion) = simplify_not(self.cx, inner) && self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow - && let Some(snippet) = snippet_opt(self.cx, expr.span) - && !snippet.contains("assert") { span_lint_and_sugg( self.cx, diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index 4d48ef14d31ce..f7c3df7066f7d 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -145,3 +145,14 @@ fn issue10836() { // Should not lint let _: bool = !!Foo(true); } + +fn issue11932() { + let x: i32 = unimplemented!(); + + #[allow(clippy::nonminimal_bool)] + let _ = x % 2 == 0 || { + // Should not lint + assert!(x > 0); + x % 3 == 0 + }; +} From e0228eeb94501cd864b0fb6edb019f401111d586 Mon Sep 17 00:00:00 2001 From: Quinn Sinclair Date: Wed, 3 Jan 2024 13:36:44 +0200 Subject: [PATCH 08/68] Fixes FP in `redundant_closure_call` when closures are passed to macros There are cases where the closure call is needed in some macros, this in particular occurs when the closure has parameters. To handle this case, we allow the lint when there are no parameters in the closure, or the closure is outside a macro invocation. fixes: #11274, #1553 changelog: FP: [`redundant_closure_call`] when closures with parameters are passed in macros. --- clippy_lints/src/redundant_closure_call.rs | 14 ++++++++++++-- tests/ui/redundant_closure_call_fixable.fixed | 9 +++++++++ tests/ui/redundant_closure_call_fixable.rs | 9 +++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index cde08dfcc748d..bd89389343564 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -2,6 +2,7 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::get_parent_expr; use clippy_utils::sugg::Sugg; +use hir::Param; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; @@ -11,6 +12,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::declare_lint_pass; +use rustc_span::ExpnKind; declare_clippy_lint! { /// ### What it does @@ -87,7 +89,12 @@ 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>, ty::Asyncness)> { +) -> Option<( + &'tcx hir::Expr<'tcx>, + &'tcx hir::FnDecl<'tcx>, + ty::Asyncness, + &'tcx [Param<'tcx>], +)> { let mut data = None; while let hir::ExprKind::Closure(closure) = expr.kind @@ -108,6 +115,7 @@ fn find_innermost_closure<'tcx>( } else { ty::Asyncness::No }, + body.params, )); steps -= 1; } @@ -150,7 +158,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { // without this check, we'd end up linting twice. && !matches!(recv.kind, hir::ExprKind::Call(..)) && let (full_expr, call_depth) = get_parent_call_exprs(cx, expr) - && let Some((body, fn_decl, coroutine_kind)) = find_innermost_closure(cx, recv, call_depth) + && let Some((body, fn_decl, coroutine_kind, params)) = find_innermost_closure(cx, recv, call_depth) + // outside macros we lint properly. Inside macros, we lint only ||() style closures. + && (!matches!(expr.span.ctxt().outer_expn_data().kind, ExpnKind::Macro(_, _)) || params.is_empty()) { span_lint_and_then( cx, diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index f272d8359a388..ce5c7f2600be7 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed @@ -102,3 +102,12 @@ mod issue11707 { fn avoid_double_parens() { std::convert::identity(13_i32 + 36_i32).leading_zeros(); } + +fn fp_11274() { + macro_rules! m { + ($closure:expr) => { + $closure(1) + }; + } + m!(|x| println!("{x}")); +} diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index f45db8c9cff5d..ac09390e6eaeb 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -102,3 +102,12 @@ mod issue11707 { fn avoid_double_parens() { std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros(); } + +fn fp_11274() { + macro_rules! m { + ($closure:expr) => { + $closure(1) + }; + } + m!(|x| println!("{x}")); +} From 3b8f62f85e4b671307afacdadbe374340ab68bab Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 18 Jan 2024 17:58:56 +0100 Subject: [PATCH 09/68] Add new `unnecessary_result_map_or_else` lint --- clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/map_unwrap_or.rs | 2 +- clippy_lints/src/methods/mod.rs | 28 ++++++ .../methods/unnecessary_result_map_or_else.rs | 95 +++++++++++++++++++ 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/methods/unnecessary_result_map_or_else.rs diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 20230106d5366..cab4647e08a84 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -451,6 +451,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::UNNECESSARY_JOIN_INFO, crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO, crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO, + crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, crate::methods::UNWRAP_OR_DEFAULT_INFO, diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 52ea584a2c8cb..3226fa9cd3fa4 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( unwrap_arg: &'tcx hir::Expr<'_>, msrv: &Msrv, ) -> bool { - // lint if the caller of `map()` is an `Option` + // lint if the caller of `map()` is an `Option` or a `Result`. let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 89ea3597dc0fd..f769696edc47f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -113,6 +113,7 @@ mod unnecessary_iter_cloned; mod unnecessary_join; mod unnecessary_lazy_eval; mod unnecessary_literal_unwrap; +mod unnecessary_result_map_or_else; mod unnecessary_sort_by; mod unnecessary_to_owned; mod unwrap_expect_used; @@ -3913,6 +3914,31 @@ declare_clippy_lint! { "cloning an `Option` via `as_ref().cloned()`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `.map_or_else()` "map closure" for `Result` type. + /// + /// ### Why is this bad? + /// This can be written more concisely by using `unwrap_or_else()`. + /// + /// ### Example + /// ```no_run + /// # fn handle_error(_: ()) -> u32 { 0 } + /// let x: Result = Ok(0); + /// let y = x.map_or_else(|err| handle_error(err), |n| n); + /// ``` + /// Use instead: + /// ```no_run + /// # fn handle_error(_: ()) -> u32 { 0 } + /// let x: Result = Ok(0); + /// let y = x.unwrap_or_else(|err| handle_error(err)); + /// ``` + #[clippy::version = "1.77.0"] + pub UNNECESSARY_RESULT_MAP_OR_ELSE, + suspicious, + "making no use of the \"map closure\" when calling `.map_or_else(|err| handle_error(err), |n| n)`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4070,6 +4096,7 @@ impl_lint_pass!(Methods => [ MANUAL_IS_VARIANT_AND, STR_SPLIT_AT_NEWLINE, OPTION_AS_REF_CLONED, + UNNECESSARY_RESULT_MAP_OR_ELSE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4553,6 +4580,7 @@ impl Methods { }, ("map_or_else", [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); + unnecessary_result_map_or_else::check(cx, expr, recv, def, map); }, ("next", []) => { if let Some((name2, recv2, args2, _, _)) = method_call(recv) { diff --git a/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs new file mode 100644 index 0000000000000..7b0cf48ac43be --- /dev/null +++ b/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -0,0 +1,95 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::peel_blocks; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath, Stmt, StmtKind}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::UNNECESSARY_RESULT_MAP_OR_ELSE; + +fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { + let msg = "unused \"map closure\" when calling `Result::map_or_else` value"; + let self_snippet = snippet(cx, recv.span, ".."); + let err_snippet = snippet(cx, def_arg.span, ".."); + span_lint_and_sugg( + cx, + UNNECESSARY_RESULT_MAP_OR_ELSE, + expr.span, + msg, + "consider using `unwrap_or_else`", + format!("{self_snippet}.unwrap_or_else({err_snippet})"), + Applicability::MachineApplicable, + ); +} + +fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option { + for stmt in statements { + if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind + && let hir::def::Res::Local(local_hir_id) = path.res + && local_hir_id == hir_id + { + hir_id = local.pat.hir_id; + } else { + return None; + } + } + Some(hir_id) +} + +fn handle_qpath( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + def_arg: &Expr<'_>, + expected_hir_id: HirId, + qpath: QPath<'_>, +) { + if let QPath::Resolved(_, path) = qpath + && let hir::def::Res::Local(hir_id) = path.res + && expected_hir_id == hir_id + { + emit_lint(cx, expr, recv, def_arg); + } +} + +/// lint use of `_.map_or_else(|err| err, |n| n)` for `Result`s. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + recv: &'tcx Expr<'_>, + def_arg: &'tcx Expr<'_>, + map_arg: &'tcx Expr<'_>, +) { + // lint if the caller of `map_or_else()` is a `Result` + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result) + && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind + && let body = cx.tcx.hir().body(body) + && let Some(first_param) = body.params.first() + { + let body_expr = peel_blocks(body.value); + + match body_expr.kind { + ExprKind::Path(qpath) => { + handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath); + }, + // If this is a block (that wasn't peeled off), then it means there are statements. + ExprKind::Block(block, _) => { + if let Some(block_expr) = block.expr + // First we ensure that this is a "binding chain" (each statement is a binding + // of the previous one) and that it is a binding of the closure argument. + && let Some(last_chain_binding_id) = + get_last_chain_binding_hir_id(first_param.pat.hir_id, block.stmts) + && let ExprKind::Path(qpath) = block_expr.kind + { + handle_qpath(cx, expr, recv, def_arg, last_chain_binding_id, qpath); + } + }, + _ => {}, + } + } +} From 32bbeba16b433d632ec89c1f1767d01666c532fa Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 18 Jan 2024 17:59:14 +0100 Subject: [PATCH 10/68] Add ui test for `unnecessary_result_map_or_else` --- tests/ui/unnecessary_result_map_or_else.fixed | 61 ++++++++++++++++ tests/ui/unnecessary_result_map_or_else.rs | 69 +++++++++++++++++++ .../ui/unnecessary_result_map_or_else.stderr | 35 ++++++++++ 3 files changed, 165 insertions(+) create mode 100644 tests/ui/unnecessary_result_map_or_else.fixed create mode 100644 tests/ui/unnecessary_result_map_or_else.rs create mode 100644 tests/ui/unnecessary_result_map_or_else.stderr diff --git a/tests/ui/unnecessary_result_map_or_else.fixed b/tests/ui/unnecessary_result_map_or_else.fixed new file mode 100644 index 0000000000000..224e0b52d75df --- /dev/null +++ b/tests/ui/unnecessary_result_map_or_else.fixed @@ -0,0 +1,61 @@ +#![warn(clippy::unnecessary_result_map_or_else)] +#![allow(clippy::unnecessary_literal_unwrap, clippy::let_and_return, clippy::let_unit_value)] + +fn main() { + let x: Result<(), ()> = Ok(()); + x.unwrap_or_else(|err| err); //~ ERROR: unused "map closure" when calling + + // Type ascribtion. + let x: Result<(), ()> = Ok(()); + x.unwrap_or_else(|err: ()| err); //~ ERROR: unused "map closure" when calling + + // Auto-deref. + let y = String::new(); + let x: Result<&String, &String> = Ok(&y); + let y: &str = x.unwrap_or_else(|err| err); //~ ERROR: unused "map closure" when calling + + // Temporary variable. + let x: Result<(), ()> = Ok(()); + x.unwrap_or_else(|err| err); + + // Should not warn. + let x: Result = Ok(0); + x.map_or_else(|err| err, |n| n + 1); + + // Should not warn. + let y = (); + let x: Result<(), ()> = Ok(()); + x.map_or_else(|err| err, |_| y); + + // Should not warn. + let y = (); + let x: Result<(), ()> = Ok(()); + x.map_or_else( + |err| err, + |_| { + let tmp = y; + tmp + }, + ); + + // Should not warn. + let x: Result = Ok(1); + x.map_or_else( + |err| err, + |n| { + let tmp = n + 1; + tmp + }, + ); + + // Should not warn. + let y = 0; + let x: Result = Ok(1); + x.map_or_else( + |err| err, + |n| { + let tmp = n; + y + }, + ); +} diff --git a/tests/ui/unnecessary_result_map_or_else.rs b/tests/ui/unnecessary_result_map_or_else.rs new file mode 100644 index 0000000000000..4fe950a4cfa6b --- /dev/null +++ b/tests/ui/unnecessary_result_map_or_else.rs @@ -0,0 +1,69 @@ +#![warn(clippy::unnecessary_result_map_or_else)] +#![allow(clippy::unnecessary_literal_unwrap, clippy::let_and_return, clippy::let_unit_value)] + +fn main() { + let x: Result<(), ()> = Ok(()); + x.map_or_else(|err| err, |n| n); //~ ERROR: unused "map closure" when calling + + // Type ascribtion. + let x: Result<(), ()> = Ok(()); + x.map_or_else(|err: ()| err, |n: ()| n); //~ ERROR: unused "map closure" when calling + + // Auto-deref. + let y = String::new(); + let x: Result<&String, &String> = Ok(&y); + let y: &str = x.map_or_else(|err| err, |n| n); //~ ERROR: unused "map closure" when calling + + // Temporary variable. + let x: Result<(), ()> = Ok(()); + x.map_or_else( + //~^ ERROR: unused "map closure" when calling + |err| err, + |n| { + let tmp = n; + let tmp2 = tmp; + tmp2 + }, + ); + + // Should not warn. + let x: Result = Ok(0); + x.map_or_else(|err| err, |n| n + 1); + + // Should not warn. + let y = (); + let x: Result<(), ()> = Ok(()); + x.map_or_else(|err| err, |_| y); + + // Should not warn. + let y = (); + let x: Result<(), ()> = Ok(()); + x.map_or_else( + |err| err, + |_| { + let tmp = y; + tmp + }, + ); + + // Should not warn. + let x: Result = Ok(1); + x.map_or_else( + |err| err, + |n| { + let tmp = n + 1; + tmp + }, + ); + + // Should not warn. + let y = 0; + let x: Result = Ok(1); + x.map_or_else( + |err| err, + |n| { + let tmp = n; + y + }, + ); +} diff --git a/tests/ui/unnecessary_result_map_or_else.stderr b/tests/ui/unnecessary_result_map_or_else.stderr new file mode 100644 index 0000000000000..0f83be5d55661 --- /dev/null +++ b/tests/ui/unnecessary_result_map_or_else.stderr @@ -0,0 +1,35 @@ +error: unused "map closure" when calling `Result::map_or_else` value + --> $DIR/unnecessary_result_map_or_else.rs:6:5 + | +LL | x.map_or_else(|err| err, |n| n); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)` + | + = note: `-D clippy::unnecessary-result-map-or-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_result_map_or_else)]` + +error: unused "map closure" when calling `Result::map_or_else` value + --> $DIR/unnecessary_result_map_or_else.rs:10:5 + | +LL | x.map_or_else(|err: ()| err, |n: ()| n); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err: ()| err)` + +error: unused "map closure" when calling `Result::map_or_else` value + --> $DIR/unnecessary_result_map_or_else.rs:15:19 + | +LL | let y: &str = x.map_or_else(|err| err, |n| n); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)` + +error: unused "map closure" when calling `Result::map_or_else` value + --> $DIR/unnecessary_result_map_or_else.rs:19:5 + | +LL | / x.map_or_else( +LL | | +LL | | |err| err, +LL | | |n| { +... | +LL | | }, +LL | | ); + | |_____^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)` + +error: aborting due to 4 previous errors + From e86da9eca3ddd3abe3a96b0af2b9a0fb1cf0470c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 18 Jan 2024 17:59:21 +0100 Subject: [PATCH 11/68] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d32bbec914a7..bc7dc34fd8540 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5676,6 +5676,7 @@ Released 2018-09-13 [`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 +[`unnecessary_result_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_result_map_or_else [`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports From 0b6cf3b78c9602798b132ea4a420ea51fe3aa795 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 8 Jan 2024 09:41:48 +0000 Subject: [PATCH 12/68] We don't look into static items anymore during const prop --- tests/ui/modulo_one.rs | 3 +-- tests/ui/modulo_one.stderr | 8 +------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/ui/modulo_one.rs b/tests/ui/modulo_one.rs index c1dbe9d9a8787..c332a15f15778 100644 --- a/tests/ui/modulo_one.rs +++ b/tests/ui/modulo_one.rs @@ -33,7 +33,6 @@ fn main() { INT_MIN % NEG_ONE; //~^ ERROR: this operation will panic at runtime //~| ERROR: any number modulo -1 will panic/overflow or result in 0 - // ONLY caught by rustc + // Not caught by lint, we don't look into static items, even if entirely immutable. INT_MIN % STATIC_NEG_ONE; - //~^ ERROR: this operation will panic at runtime } diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index cc211ab6cd345..06bbb0f5d9a80 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -12,12 +12,6 @@ error: this operation will panic at runtime LL | INT_MIN % NEG_ONE; | ^^^^^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow -error: this operation will panic at runtime - --> $DIR/modulo_one.rs:37:5 - | -LL | INT_MIN % STATIC_NEG_ONE; - | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `i64::MIN % -1_i64`, which would overflow - error: any number modulo 1 will be 0 --> $DIR/modulo_one.rs:8:5 | @@ -57,5 +51,5 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | INT_MIN % NEG_ONE; | ^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors From 99d8d334194c8228edc15270809b061bf2b0fb12 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 26 Sep 2023 09:39:41 +0200 Subject: [PATCH 13/68] remove StructuralEq trait --- tests/ui/crashes/ice-6254.rs | 2 -- tests/ui/crashes/ice-6254.stderr | 15 --------------- 2 files changed, 17 deletions(-) delete mode 100644 tests/ui/crashes/ice-6254.stderr diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs index 2ae426cf789de..8af60890390e7 100644 --- a/tests/ui/crashes/ice-6254.rs +++ b/tests/ui/crashes/ice-6254.rs @@ -11,8 +11,6 @@ fn main() { // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071) match FOO_REF_REF { FOO_REF_REF => {}, - //~^ ERROR: to use a constant of type `Foo` in a pattern, `Foo` must be annotated - //~| NOTE: for more information, see issue #62411 {}, } } diff --git a/tests/ui/crashes/ice-6254.stderr b/tests/ui/crashes/ice-6254.stderr deleted file mode 100644 index 7a34e6cceeea7..0000000000000 --- a/tests/ui/crashes/ice-6254.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/ice-6254.rs:13:9 - | -LL | FOO_REF_REF => {}, - | ^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #62411 - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralEq.html for details - = note: `-D indirect-structural-match` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(indirect_structural_match)]` - -error: aborting due to 1 previous error - From 9cbc5829a83f6b0b72c391ccded463885bd36351 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 24 Jan 2024 15:24:58 +1100 Subject: [PATCH 14/68] Rename the unescaping functions. `unescape_literal` becomes `unescape_unicode`, and `unescape_c_string` becomes `unescape_mixed`. Because rfc3349 will mean that C string literals will no longer be the only mixed utf8 literals. --- clippy_dev/src/update_lints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 6b76a44debff7..f598f5d3d50f8 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -928,7 +928,7 @@ fn remove_line_splices(s: &str) -> String { .and_then(|s| s.strip_suffix('"')) .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); let mut res = String::with_capacity(s.len()); - unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| { + unescape::unescape_unicode(s, unescape::Mode::Str, &mut |range, ch| { if ch.is_ok() { res.push_str(&s[range]); } From e456c28e11921174d50e8329ca78903e6e53b3cc Mon Sep 17 00:00:00 2001 From: Marc Dominik Migge Date: Fri, 19 Jan 2024 17:05:14 +0100 Subject: [PATCH 15/68] Don't warn about modulo arithmetic when comparing to zero Add lint configuration for `modulo_arithmetic` Collect meta-data --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 10 +++++ clippy_config/src/conf.rs | 4 ++ clippy_lints/src/lib.rs | 8 +++- clippy_lints/src/operators/mod.rs | 15 +++++-- .../src/operators/modulo_arithmetic.rs | 27 ++++++++++++- tests/ui-toml/modulo_arithmetic/clippy.toml | 1 + .../modulo_arithmetic/modulo_arithmetic.rs | 10 +++++ .../modulo_arithmetic.stderr | 40 +++++++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 ++ tests/ui/modulo_arithmetic_integral.rs | 8 ++++ 11 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 tests/ui-toml/modulo_arithmetic/clippy.toml create mode 100644 tests/ui-toml/modulo_arithmetic/modulo_arithmetic.rs create mode 100644 tests/ui-toml/modulo_arithmetic/modulo_arithmetic.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fa45ceeb39be..2a3055c19f0ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5819,4 +5819,5 @@ Released 2018-09-13 [`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior +[`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 7f7aff92bf19b..1b321b8bcb81a 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -828,3 +828,13 @@ exported visibility, or whether they are marked as "pub". * [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields) +## `allow-comparison-to-zero` +Don't lint when comparing the result of a modulo operation to zero. + +**Default Value:** `true` + +--- +**Affected lints:** +* [`modulo_arithmetic`](https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic) + + diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 4e9ddbf8259d3..8b45f250b102e 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -567,6 +567,10 @@ define_Conf! { /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported), + /// Lint: MODULO_ARITHMETIC. + /// + /// Don't lint when comparing the result of a modulo operation to zero. + (allow_comparison_to_zero: bool = true), } /// Search for the configuration file. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index feb4d188f3978..72d1aee767c7e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -575,6 +575,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { check_private_items, pub_underscore_fields_behavior, ref allowed_duplicate_crates, + allow_comparison_to_zero, blacklisted_names: _, cyclomatic_complexity_threshold: _, @@ -968,7 +969,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); - store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); + store.register_late_pass(move |_| { + Box::new(operators::Operators::new( + verbose_bit_mask_threshold, + allow_comparison_to_zero, + )) + }); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 4c09c4eea581f..e002429e3a47a 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -771,6 +771,7 @@ declare_clippy_lint! { pub struct Operators { arithmetic_context: numeric_arithmetic::Context, verbose_bit_mask_threshold: u64, + modulo_arithmetic_allow_comparison_to_zero: bool, } impl_lint_pass!(Operators => [ ABSURD_EXTREME_COMPARISONS, @@ -801,10 +802,11 @@ impl_lint_pass!(Operators => [ SELF_ASSIGNMENT, ]); impl Operators { - pub fn new(verbose_bit_mask_threshold: u64) -> Self { + pub fn new(verbose_bit_mask_threshold: u64, modulo_arithmetic_allow_comparison_to_zero: bool) -> Self { Self { arithmetic_context: numeric_arithmetic::Context::default(), verbose_bit_mask_threshold, + modulo_arithmetic_allow_comparison_to_zero, } } } @@ -835,12 +837,19 @@ impl<'tcx> LateLintPass<'tcx> for Operators { cmp_owned::check(cx, op.node, lhs, rhs); float_cmp::check(cx, e, op.node, lhs, rhs); modulo_one::check(cx, e, op.node, rhs); - modulo_arithmetic::check(cx, e, op.node, lhs, rhs); + modulo_arithmetic::check( + cx, + e, + op.node, + lhs, + rhs, + self.modulo_arithmetic_allow_comparison_to_zero, + ); }, ExprKind::AssignOp(op, lhs, rhs) => { self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs); misrefactored_assign_op::check(cx, e, op.node, lhs, rhs); - modulo_arithmetic::check(cx, e, op.node, lhs, rhs); + modulo_arithmetic::check(cx, e, op.node, lhs, rhs, false); }, ExprKind::Assign(lhs, rhs, _) => { assign_op_pattern::check(cx, e, lhs, rhs); diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index cb3916484c1d5..40d4a842befb8 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sext; -use rustc_hir::{BinOpKind, Expr}; +use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use std::fmt::Display; @@ -14,8 +14,13 @@ pub(super) fn check<'tcx>( op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, + allow_comparison_to_zero: bool, ) { if op == BinOpKind::Rem { + if allow_comparison_to_zero && used_in_comparison_with_zero(cx, e) { + return; + } + let lhs_operand = analyze_operand(lhs, cx, e); let rhs_operand = analyze_operand(rhs, cx, e); if let Some(lhs_operand) = lhs_operand @@ -28,6 +33,26 @@ pub(super) fn check<'tcx>( }; } +fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let Some(Node::Expr(parent_expr)) = cx.tcx.hir().find_parent(expr.hir_id) else { + return false; + }; + let ExprKind::Binary(op, lhs, rhs) = parent_expr.kind else { + return false; + }; + + if op.node == BinOpKind::Eq || op.node == BinOpKind::Ne { + if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), rhs) { + return true; + } + if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), lhs) { + return true; + } + } + + false +} + struct OperandInfo { string_representation: Option, is_negative: bool, diff --git a/tests/ui-toml/modulo_arithmetic/clippy.toml b/tests/ui-toml/modulo_arithmetic/clippy.toml new file mode 100644 index 0000000000000..de80f4ae59838 --- /dev/null +++ b/tests/ui-toml/modulo_arithmetic/clippy.toml @@ -0,0 +1 @@ +allow-comparison-to-zero = false diff --git a/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.rs b/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.rs new file mode 100644 index 0000000000000..27d27564baf34 --- /dev/null +++ b/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.rs @@ -0,0 +1,10 @@ +#![warn(clippy::modulo_arithmetic)] + +fn main() { + let a = -1; + let b = 2; + let c = a % b == 0; + let c = a % b != 0; + let c = 0 == a % b; + let c = 0 != a % b; +} diff --git a/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.stderr b/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.stderr new file mode 100644 index 0000000000000..da644b05a113d --- /dev/null +++ b/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.stderr @@ -0,0 +1,40 @@ +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic.rs:6:13 + | +LL | let c = a % b == 0; + | ^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + = note: `-D clippy::modulo-arithmetic` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::modulo_arithmetic)]` + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic.rs:7:13 + | +LL | let c = a % b != 0; + | ^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic.rs:8:18 + | +LL | let c = 0 == a % b; + | ^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic.rs:9:18 + | +LL | let c = 0 != a % b; + | ^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: aborting due to 4 previous errors + 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 fc683e514ba40..24fdfd945bd68 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -3,6 +3,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect absolute-paths-max-segments accept-comment-above-attributes accept-comment-above-statement + allow-comparison-to-zero allow-dbg-in-tests allow-expect-in-tests allow-mixed-uninlined-format-args @@ -80,6 +81,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect absolute-paths-max-segments accept-comment-above-attributes accept-comment-above-statement + allow-comparison-to-zero allow-dbg-in-tests allow-expect-in-tests allow-mixed-uninlined-format-args @@ -157,6 +159,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni absolute-paths-max-segments accept-comment-above-attributes accept-comment-above-statement + allow-comparison-to-zero allow-dbg-in-tests allow-expect-in-tests allow-mixed-uninlined-format-args diff --git a/tests/ui/modulo_arithmetic_integral.rs b/tests/ui/modulo_arithmetic_integral.rs index 4dbed24026cf5..c427b8580e17a 100644 --- a/tests/ui/modulo_arithmetic_integral.rs +++ b/tests/ui/modulo_arithmetic_integral.rs @@ -114,4 +114,12 @@ fn main() { a_usize % b_usize; let mut a_usize: usize = 1; a_usize %= 2; + + // No lint when comparing to zero + let a = -1; + let mut b = 2; + let c = a % b == 0; + let c = 0 == a % b; + let c = a % b != 0; + let c = 0 != a % b; } From 58de630a144deb21ed6b42678fd2d22f91ac4571 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 25 Jan 2024 12:06:01 +0000 Subject: [PATCH 16/68] Remove an unused error count check --- clippy_lints/src/transmute/utils.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 1cf6cf8548a64..7a7bb9f9c94c3 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -37,12 +37,6 @@ pub(super) fn check_cast<'tcx>( let inherited = Inherited::new(cx.tcx, local_def_id); let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, local_def_id); - // If we already have errors, we can't be sure we can pointer cast. - assert!( - !fn_ctxt.errors_reported_since_creation(), - "Newly created FnCtxt contained errors" - ); - if let Ok(check) = cast::CastCheck::new( &fn_ctxt, e, @@ -53,17 +47,7 @@ pub(super) fn check_cast<'tcx>( DUMMY_SP, hir::Constness::NotConst, ) { - let res = check.do_check(&fn_ctxt); - - // do_check's documentation says that it might return Ok and create - // errors in the fcx instead of returning Err in some cases. Those cases - // should be filtered out before getting here. - assert!( - !fn_ctxt.errors_reported_since_creation(), - "`fn_ctxt` contained errors after cast check!" - ); - - res.ok() + check.do_check(&fn_ctxt).ok() } else { None } From 798865c593b8ad255ed1e19ee2502c8d91f16c65 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 25 Jan 2024 19:17:36 +0100 Subject: [PATCH 17/68] Merge commit '66c29b973b3b10278bd39f4e26b08522a379c2c9' into clippy-subtree-update --- CHANGELOG.md | 2 + book/src/lint_configuration.md | 21 +- clippy_config/src/conf.rs | 80 ++++- clippy_config/src/lib.rs | 1 + clippy_config/src/types.rs | 2 +- clippy_dev/Cargo.toml | 4 +- clippy_dev/src/update_lints.rs | 3 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/arc_with_non_send_sync.rs | 8 +- clippy_lints/src/blocks_in_conditions.rs | 5 + clippy_lints/src/cargo/mod.rs | 6 +- .../src/cargo/multiple_crate_versions.rs | 19 +- clippy_lints/src/declared_lints.rs | 1 + .../src/default_instead_of_iter_empty.rs | 13 +- clippy_lints/src/default_numeric_fallback.rs | 14 +- clippy_lints/src/derive.rs | 3 +- clippy_lints/src/doc/needless_doctest_main.rs | 4 +- clippy_lints/src/from_over_into.rs | 9 +- clippy_lints/src/inherent_impl.rs | 4 +- clippy_lints/src/lib.rs | 4 +- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/mem_replace.rs | 15 +- .../iter_on_single_or_empty_collections.rs | 12 +- .../src/methods/join_absolute_paths.rs | 2 +- .../methods/manual_saturating_arithmetic.rs | 2 +- clippy_lints/src/methods/map_clone.rs | 20 +- clippy_lints/src/methods/mod.rs | 39 +++ clippy_lints/src/methods/open_options.rs | 230 ++++++++------ .../src/methods/option_as_ref_deref.rs | 2 +- .../src/methods/option_map_or_err_ok.rs | 2 +- .../src/methods/option_map_or_none.rs | 6 +- .../src/methods/result_map_or_else_none.rs | 2 +- clippy_lints/src/methods/search_is_some.rs | 8 +- .../src/methods/single_char_pattern.rs | 2 +- clippy_lints/src/methods/unnecessary_join.rs | 2 +- .../src/methods/unnecessary_sort_by.rs | 4 +- clippy_lints/src/methods/useless_asref.rs | 18 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 77 ++--- clippy_lints/src/no_effect.rs | 179 ++++++----- clippy_lints/src/operators/ptr_eq.rs | 8 +- clippy_lints/src/pub_underscore_fields.rs | 2 +- clippy_lints/src/read_zero_byte_vec.rs | 118 ++++--- .../src/semicolon_if_nothing_returned.rs | 6 + clippy_lints/src/single_call_fn.rs | 12 +- clippy_lints/src/trait_bounds.rs | 14 +- .../src/transmute/transmute_int_to_char.rs | 5 +- .../src/transmute/transmute_ref_to_ref.rs | 8 +- clippy_lints/src/unconditional_recursion.rs | 10 +- clippy_lints/src/unused_io_amount.rs | 292 ++++++++++++------ .../almost_standard_lint_formulation.rs | 2 +- .../src/utils/internal_lints/invalid_paths.rs | 4 +- clippy_utils/src/attrs.rs | 12 + clippy_utils/src/hir_utils.rs | 6 +- clippy_utils/src/lib.rs | 10 +- clippy_utils/src/paths.rs | 8 + clippy_utils/src/ty.rs | 3 +- rust-toolchain | 2 +- .../12145_with_dashes/Cargo.stderr | 6 + .../12145_with_dashes/Cargo.toml | 14 + .../12145_with_dashes/src/main.rs | 3 + .../12176_allow_duplicate_crates/Cargo.toml | 10 + .../12176_allow_duplicate_crates/clippy.toml | 1 + .../12176_allow_duplicate_crates/src/main.rs | 3 + tests/ui-internal/check_formulation.stderr | 4 +- tests/ui-internal/disallow_span_lint.stderr | 6 +- .../exported/clippy.toml | 2 +- tests/ui-toml/toml_unknown_key/clippy.toml | 3 + .../toml_unknown_key/conf_unknown_key.rs | 1 + .../toml_unknown_key/conf_unknown_key.stderr | 81 ++++- tests/ui/auxiliary/proc_macro_attr.rs | 34 +- tests/ui/blocks_in_conditions.fixed | 14 + tests/ui/blocks_in_conditions.rs | 14 + .../ui/branches_sharing_code/shared_at_top.rs | 8 +- .../shared_at_top.stderr | 6 +- .../branches_sharing_code/valid_if_blocks.rs | 12 +- .../valid_if_blocks.stderr | 18 +- tests/ui/crashes/ice-9041.stderr | 2 +- ...default_instead_of_iter_empty_no_std.fixed | 28 ++ .../default_instead_of_iter_empty_no_std.rs | 28 ++ ...efault_instead_of_iter_empty_no_std.stderr | 17 + tests/ui/default_numeric_fallback_i32.fixed | 40 +++ tests/ui/default_numeric_fallback_i32.rs | 40 +++ tests/ui/derive_partial_eq_without_eq.fixed | 32 ++ tests/ui/derive_partial_eq_without_eq.rs | 32 ++ tests/ui/doc/doc-fixable.fixed | 2 +- tests/ui/doc/doc-fixable.rs | 2 +- tests/ui/from_over_into.fixed | 8 + tests/ui/from_over_into.rs | 8 + tests/ui/from_over_into.stderr | 16 +- tests/ui/if_same_then_else.rs | 28 +- tests/ui/if_same_then_else.stderr | 69 ++--- tests/ui/if_same_then_else2.rs | 18 +- tests/ui/if_same_then_else2.stderr | 37 +-- tests/ui/ineffective_open_options.fixed | 9 +- tests/ui/ineffective_open_options.rs | 9 +- tests/ui/join_absolute_paths.stderr | 8 +- tests/ui/manual_ok_or.stderr | 2 +- tests/ui/manual_saturating_arithmetic.stderr | 48 +-- tests/ui/map_clone.fixed | 33 ++ tests/ui/map_clone.rs | 35 ++- tests/ui/map_clone.stderr | 36 ++- tests/ui/match_same_arms2.rs | 4 +- tests/ui/match_same_arms2.stderr | 5 +- tests/ui/mem_replace_no_std.fixed | 82 +++++ tests/ui/mem_replace_no_std.rs | 82 +++++ tests/ui/mem_replace_no_std.stderr | 50 +++ tests/ui/no_effect.rs | 2 + tests/ui/no_effect.stderr | 16 +- tests/ui/open_options.rs | 38 ++- tests/ui/open_options.stderr | 34 +- tests/ui/open_options_fixable.fixed | 7 + tests/ui/open_options_fixable.rs | 7 + tests/ui/open_options_fixable.stderr | 14 + tests/ui/option_as_ref_deref.stderr | 36 +-- tests/ui/option_map_or_err_ok.stderr | 2 +- tests/ui/option_map_or_none.stderr | 10 +- tests/ui/ptr_eq_no_std.fixed | 49 +++ tests/ui/ptr_eq_no_std.rs | 49 +++ tests/ui/ptr_eq_no_std.stderr | 17 + tests/ui/read_zero_byte_vec.rs | 29 +- tests/ui/read_zero_byte_vec.stderr | 34 +- tests/ui/result_map_or_into_option.stderr | 6 +- tests/ui/same_item_push.stderr | 10 +- tests/ui/search_is_some.stderr | 4 +- tests/ui/search_is_some_fixable_none.stderr | 86 +++--- tests/ui/search_is_some_fixable_some.stderr | 94 +++--- .../ui/seek_to_start_instead_of_rewind.fixed | 3 + tests/ui/seek_to_start_instead_of_rewind.rs | 3 + .../ui/seek_to_start_instead_of_rewind.stderr | 2 +- tests/ui/semicolon_if_nothing_returned.fixed | 34 ++ tests/ui/semicolon_if_nothing_returned.rs | 34 ++ tests/ui/semicolon_if_nothing_returned.stderr | 10 +- tests/ui/single_call_fn.rs | 11 + tests/ui/single_char_pattern.stderr | 80 ++--- tests/ui/trait_duplication_in_bounds.fixed | 31 +- tests/ui/trait_duplication_in_bounds.rs | 31 +- tests/ui/transmute.rs | 13 - tests/ui/transmute.stderr | 61 ++-- tests/ui/transmute_int_to_char.fixed | 15 + tests/ui/transmute_int_to_char.rs | 15 + tests/ui/transmute_int_to_char.stderr | 17 + tests/ui/transmute_int_to_char_no_std.fixed | 27 ++ tests/ui/transmute_int_to_char_no_std.rs | 27 ++ tests/ui/transmute_int_to_char_no_std.stderr | 17 + tests/ui/transmute_ref_to_ref.rs | 2 +- tests/ui/transmute_ref_to_ref.stderr | 4 +- tests/ui/transmute_ref_to_ref_no_std.rs | 30 ++ tests/ui/transmute_ref_to_ref_no_std.stderr | 26 ++ tests/ui/unconditional_recursion.rs | 26 +- tests/ui/unconditional_recursion.stderr | 19 +- tests/ui/unnecessary_join.stderr | 4 +- tests/ui/unnecessary_sort_by.stderr | 24 +- tests/ui/unused_io_amount.rs | 87 ++++++ tests/ui/unused_io_amount.stderr | 141 +++++++-- tests/ui/useless_asref.fixed | 36 +++ tests/ui/useless_asref.rs | 36 +++ tests/ui/useless_asref.stderr | 26 +- triagebot.toml | 1 - 158 files changed, 2839 insertions(+), 999 deletions(-) create mode 100644 tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.stderr create mode 100644 tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.toml create mode 100644 tests/ui-cargo/multiple_crate_versions/12145_with_dashes/src/main.rs create mode 100644 tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/Cargo.toml create mode 100644 tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/clippy.toml create mode 100644 tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/src/main.rs create mode 100644 tests/ui/default_instead_of_iter_empty_no_std.fixed create mode 100644 tests/ui/default_instead_of_iter_empty_no_std.rs create mode 100644 tests/ui/default_instead_of_iter_empty_no_std.stderr create mode 100644 tests/ui/mem_replace_no_std.fixed create mode 100644 tests/ui/mem_replace_no_std.rs create mode 100644 tests/ui/mem_replace_no_std.stderr create mode 100644 tests/ui/open_options_fixable.fixed create mode 100644 tests/ui/open_options_fixable.rs create mode 100644 tests/ui/open_options_fixable.stderr create mode 100644 tests/ui/ptr_eq_no_std.fixed create mode 100644 tests/ui/ptr_eq_no_std.rs create mode 100644 tests/ui/ptr_eq_no_std.stderr create mode 100644 tests/ui/transmute_int_to_char.fixed create mode 100644 tests/ui/transmute_int_to_char.rs create mode 100644 tests/ui/transmute_int_to_char.stderr create mode 100644 tests/ui/transmute_int_to_char_no_std.fixed create mode 100644 tests/ui/transmute_int_to_char_no_std.rs create mode 100644 tests/ui/transmute_int_to_char_no_std.stderr create mode 100644 tests/ui/transmute_ref_to_ref_no_std.rs create mode 100644 tests/ui/transmute_ref_to_ref_no_std.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d32bbec914a7..5fa45ceeb39be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5606,6 +5606,7 @@ Released 2018-09-13 [`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl +[`suspicious_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_open_options [`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned @@ -5814,6 +5815,7 @@ Released 2018-09-13 [`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 +[`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates [`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 3b62ae0524ab0..7f7aff92bf19b 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -212,7 +212,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** @@ -768,7 +768,19 @@ Additional dotfiles (files or directories starting with a dot) to allow * [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext) +## `allowed-duplicate-crates` +A list of crate names to allow duplicates of + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`multiple_crate_versions`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions) + + ## `enforce-iter-loop-reborrow` +Whether to recommend using implicit into iter for reborrowed values. + #### Example ```no_run let mut vec = vec![1, 2, 3]; @@ -793,7 +805,7 @@ for _ in &mut *rmvec {} ## `check-private-items` - +Whether to also run the listed lints on private items. **Default Value:** `false` @@ -806,9 +818,10 @@ for _ in &mut *rmvec {} ## `pub-underscore-fields-behavior` +Lint "public" fields in a struct that are prefixed with an underscore based on their +exported visibility, or whether they are marked as "pub". - -**Default Value:** `"PublicallyExported"` +**Default Value:** `"PubliclyExported"` --- **Affected lints:** diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 5477d9b83a72c..4e9ddbf8259d3 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -2,7 +2,9 @@ use crate::msrvs::Msrv; use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename}; use crate::ClippyConfiguration; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; use rustc_session::Session; +use rustc_span::edit_distance::edit_distance; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; use serde::de::{IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; @@ -26,7 +28,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", - "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", + "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", @@ -59,18 +61,25 @@ impl TryConf { #[derive(Debug)] struct ConfError { message: String, + suggestion: Option, span: Span, } impl ConfError { fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self { let span = error.span().unwrap_or(0..file.source_len.0 as usize); - Self::spanned(file, error.message(), span) + Self::spanned(file, error.message(), None, span) } - fn spanned(file: &SourceFile, message: impl Into, span: Range) -> Self { + fn spanned( + file: &SourceFile, + message: impl Into, + suggestion: Option, + span: Range, + ) -> Self { Self { message: message.into(), + suggestion, span: Span::new( file.start_pos + BytePos::from_usize(span.start), file.start_pos + BytePos::from_usize(span.end), @@ -147,16 +156,18 @@ macro_rules! define_Conf { match Field::deserialize(name.get_ref().as_str().into_deserializer()) { Err(e) => { let e: FieldError = e; - errors.push(ConfError::spanned(self.0, e.0, name.span())); + errors.push(ConfError::spanned(self.0, e.error, e.suggestion, name.span())); } $(Ok(Field::$name) => { - $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), name.span()));)? + $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? let raw_value = map.next_value::>()?; let value_span = raw_value.span(); match <$ty>::deserialize(raw_value.into_inner()) { - Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), value_span)), + Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), None, value_span)), Ok(value) => match $name { - Some(_) => errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), name.span())), + Some(_) => { + errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); + } None => { $name = Some(value); // $new_conf is the same as one of the defined `$name`s, so @@ -165,7 +176,7 @@ macro_rules! define_Conf { Some(_) => errors.push(ConfError::spanned(self.0, concat!( "duplicate field `", stringify!($new_conf), "` (provided as `", stringify!($name), "`)" - ), name.span())), + ), None, name.span())), None => $new_conf = $name.clone(), })? }, @@ -523,7 +534,11 @@ define_Conf! { /// /// Additional dotfiles (files or directories starting with a dot) to allow (allowed_dotfiles: FxHashSet = FxHashSet::default()), - /// Lint: EXPLICIT_ITER_LOOP + /// Lint: MULTIPLE_CRATE_VERSIONS. + /// + /// A list of crate names to allow duplicates of + (allowed_duplicate_crates: FxHashSet = FxHashSet::default()), + /// Lint: EXPLICIT_ITER_LOOP. /// /// Whether to recommend using implicit into iter for reborrowed values. /// @@ -543,15 +558,15 @@ define_Conf! { /// for _ in &mut *rmvec {} /// ``` (enforce_iter_loop_reborrow: bool = false), - /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC + /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC. /// /// Whether to also run the listed lints on private items. (check_private_items: bool = false), - /// Lint: PUB_UNDERSCORE_FIELDS + /// Lint: PUB_UNDERSCORE_FIELDS. /// /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". - (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PublicallyExported), + (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported), } /// Search for the configuration file. @@ -669,10 +684,16 @@ impl Conf { // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { - sess.dcx().span_err( + let mut diag = sess.dcx().struct_span_err( error.span, format!("error reading Clippy's configuration file: {}", error.message), ); + + if let Some(sugg) = error.suggestion { + diag.span_suggestion(error.span, sugg.message, sugg.suggestion, Applicability::MaybeIncorrect); + } + + diag.emit(); } for warning in warnings { @@ -689,19 +710,31 @@ impl Conf { const SEPARATOR_WIDTH: usize = 4; #[derive(Debug)] -struct FieldError(String); +struct FieldError { + error: String, + suggestion: Option, +} + +#[derive(Debug)] +struct Suggestion { + message: &'static str, + suggestion: &'static str, +} impl std::error::Error for FieldError {} impl Display for FieldError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.pad(&self.0) + f.pad(&self.error) } } impl serde::de::Error for FieldError { fn custom(msg: T) -> Self { - Self(msg.to_string()) + Self { + error: msg.to_string(), + suggestion: None, + } } fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { @@ -723,7 +756,20 @@ impl serde::de::Error for FieldError { write!(msg, "{:SEPARATOR_WIDTH$}{field:column_width$}", " ").unwrap(); } } - Self(msg) + + let suggestion = expected + .iter() + .filter_map(|expected| { + let dist = edit_distance(field, expected, 4)?; + Some((dist, expected)) + }) + .min_by_key(|&(dist, _)| dist) + .map(|(_, suggestion)| Suggestion { + message: "perhaps you meant", + suggestion, + }); + + Self { error: msg, suggestion } } } diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index f5dcb16d670df..533e375a3104c 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -11,6 +11,7 @@ extern crate rustc_ast; extern crate rustc_data_structures; #[allow(unused_extern_crates)] extern crate rustc_driver; +extern crate rustc_errors; extern crate rustc_session; extern crate rustc_span; diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index baee09629ac44..435aa9244c522 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -129,6 +129,6 @@ unimplemented_serialize! { #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum PubUnderscoreFieldsBehaviour { - PublicallyExported, + PubliclyExported, AllPubFields, } diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index ce738e3f4ec7c..5ec67554e7d89 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -4,11 +4,11 @@ version = "0.0.1" edition = "2021" [dependencies] -aho-corasick = "0.7" +aho-corasick = "1.0" clap = "4.1.4" indoc = "1.0" itertools = "0.11" -opener = "0.5" +opener = "0.6" shell-escape = "0.1" walkdir = "2.3" diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 6b76a44debff7..9a357466923a1 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -504,9 +504,8 @@ fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option(replacements.iter().map(|&(x, _)| x.as_bytes())) + .build(replacements.iter().map(|&(x, _)| x.as_bytes())) .unwrap(); let mut result = String::with_capacity(contents.len() + 1024); diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 8cba35f3d871c..416e9a680dd73 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] arrayvec = { version = "0.7", default-features = false } -cargo_metadata = "0.15.3" +cargo_metadata = "0.18" clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index 657d52d0e9e17..1102c7fb236b5 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -14,10 +14,10 @@ declare_clippy_lint! { /// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`. /// /// ### Why is this bad? - /// `Arc` is an Atomic `RC` and guarantees that updates to the reference counter are - /// Atomic. This is useful in multiprocessing scenarios. To send an `Arc` across processes - /// and make use of the atomic ref counter, `T` must be [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E), - /// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc` + /// `Arc` is a thread-safe `Rc` and guarantees that updates to the reference counter + /// use atomic operations. To send an `Arc` across thread boundaries and + /// share ownership between multiple threads, `T` must be [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#thread-safety), + /// so either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc` /// /// ### Example /// ```no_run diff --git a/clippy_lints/src/blocks_in_conditions.rs b/clippy_lints/src/blocks_in_conditions.rs index 1417e230aee5f..ff4dffd06079f 100644 --- a/clippy_lints/src/blocks_in_conditions.rs +++ b/clippy_lints/src/blocks_in_conditions.rs @@ -67,6 +67,11 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInConditions { ); if let ExprKind::Block(block, _) = &cond.kind { + if !block.span.eq_ctxt(expr.span) { + // If the block comes from a macro, or as an argument to a macro, + // do not lint. + return; + } if block.rules == BlockCheckMode::DefaultBlock { if block.stmts.is_empty() { if let Some(ex) = &block.expr { diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index fea6924d89e91..d8107f61f371c 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -6,6 +6,7 @@ mod wildcard_dependencies; use cargo_metadata::MetadataCommand; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_lint_allowed; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_session::impl_lint_pass; @@ -128,6 +129,8 @@ declare_clippy_lint! { /// ### Known problems /// Because this can be caused purely by the dependencies /// themselves, it's not always possible to fix this issue. + /// In those cases, you can allow that specific crate using + /// the `allowed_duplicate_crates` configuration option. /// /// ### Example /// ```toml @@ -163,6 +166,7 @@ declare_clippy_lint! { } pub struct Cargo { + pub allowed_duplicate_crates: FxHashSet, pub ignore_publish: bool, } @@ -208,7 +212,7 @@ impl LateLintPass<'_> for Cargo { { match MetadataCommand::new().exec() { Ok(metadata) => { - multiple_crate_versions::check(cx, &metadata); + multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates); }, Err(e) => { for lint in WITH_DEPS_LINTS { diff --git a/clippy_lints/src/cargo/multiple_crate_versions.rs b/clippy_lints/src/cargo/multiple_crate_versions.rs index ec681adb7aef2..3f30a77fcfe47 100644 --- a/clippy_lints/src/cargo/multiple_crate_versions.rs +++ b/clippy_lints/src/cargo/multiple_crate_versions.rs @@ -3,27 +3,40 @@ use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId}; use clippy_utils::diagnostics::span_lint; use itertools::Itertools; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::LOCAL_CRATE; use rustc_lint::LateContext; use rustc_span::DUMMY_SP; use super::MULTIPLE_CRATE_VERSIONS; -pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { +pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, allowed_duplicate_crates: &FxHashSet) { let local_name = cx.tcx.crate_name(LOCAL_CRATE); let mut packages = metadata.packages.clone(); packages.sort_by(|a, b| a.name.cmp(&b.name)); if let Some(resolve) = &metadata.resolve && let Some(local_id) = packages.iter().find_map(|p| { - if p.name == local_name.as_str() { + // p.name contains the original crate names with dashes intact + // local_name contains the crate name as a namespace, with the dashes converted to underscores + // the code below temporarily rectifies this discrepancy + if p.name + .as_bytes() + .iter() + .map(|b| if b == &b'-' { &b'_' } else { b }) + .eq(local_name.as_str().as_bytes()) + { Some(&p.id) } else { None } }) { - for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { + for (name, group) in &packages + .iter() + .filter(|p| !allowed_duplicate_crates.contains(&p.name)) + .group_by(|p| &p.name) + { let group: Vec<&Package> = group.collect(); if group.len() <= 1 { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 20230106d5366..639edd8da3012 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -439,6 +439,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::STR_SPLIT_AT_NEWLINE_INFO, crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO, crate::methods::SUSPICIOUS_MAP_INFO, + crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, crate::methods::SUSPICIOUS_TO_OWNED_INFO, crate::methods::TYPE_ID_ON_BOX_INFO, diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs index 2472e2ee76f93..e617c19eff09b 100644 --- a/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/clippy_lints/src/default_instead_of_iter_empty.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::last_path_segment; use clippy_utils::source::snippet_with_context; +use clippy_utils::{last_path_segment, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -42,12 +42,14 @@ impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { && ty.span.ctxt() == ctxt { let mut applicability = Applicability::MachineApplicable; - let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability); + let Some(path) = std_or_core(cx) else { return }; + let path = format!("{path}::iter::empty"); + let sugg = make_sugg(cx, ty_path, ctxt, &mut applicability, &path); span_lint_and_sugg( cx, DEFAULT_INSTEAD_OF_ITER_EMPTY, expr.span, - "`std::iter::empty()` is the more idiomatic way", + &format!("`{path}()` is the more idiomatic way"), "try", sugg, applicability, @@ -61,6 +63,7 @@ fn make_sugg( ty_path: &rustc_hir::QPath<'_>, ctxt: SyntaxContext, applicability: &mut Applicability, + path: &str, ) -> String { if let Some(last) = last_path_segment(ty_path).args && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg { @@ -69,10 +72,10 @@ fn make_sugg( }) { format!( - "std::iter::empty::<{}>()", + "{path}::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0 ) } else { - "std::iter::empty()".to_owned() + format!("{path}()") } } diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 712bc075650fa..c4437a3c4b339 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; -use clippy_utils::{get_parent_node, numeric_literal}; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor}; -use rustc_hir::{Block, Body, Expr, ExprKind, FnRetTy, HirId, ItemKind, Lit, Node, Stmt, StmtKind}; +use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty}; @@ -50,11 +50,11 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { - let is_parent_const = if let Some(Node::Item(item)) = get_parent_node(cx.tcx, body.id().hir_id) { - matches!(item.kind, ItemKind::Const(..)) - } else { - false - }; + let hir = cx.tcx.hir(); + let is_parent_const = matches!( + hir.body_const_context(hir.body_owner_def_id(body.id())), + Some(ConstContext::Const { inline: false } | ConstContext::Static(_)) + ); let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const); visitor.visit_body(body); } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index df596338b9500..6144ec7b3caca 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{is_lint_allowed, match_def_path, paths}; +use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; @@ -450,6 +450,7 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) && let Some(def_id) = trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) + && !has_non_exhaustive_attr(cx.tcx, *adt) && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) && !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[]) // If all of our fields implement `Eq`, we can implement `Eq` too diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs index 8b018220c1715..8dde4f227ed15 100644 --- a/clippy_lints/src/doc/needless_doctest_main.rs +++ b/clippy_lints/src/doc/needless_doctest_main.rs @@ -6,7 +6,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::HumanEmitter; -use rustc_errors::DiagCtxt; +use rustc_errors::{DiagCtxt, DiagnosticBuilder}; use rustc_lint::LateContext; use rustc_parse::maybe_new_parser_from_source_str; use rustc_parse::parser::ForceCollect; @@ -53,7 +53,7 @@ pub fn check( let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { Ok(p) => p, Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); + errs.into_iter().for_each(DiagnosticBuilder::cancel); return (false, test_attr_spans); }, }; diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index fa1f98ba01343..93527bcdf5cee 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -6,8 +6,8 @@ use clippy_utils::source::snippet_opt; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_path, Visitor}; use rustc_hir::{ - GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty, - TyKind, + FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, + PathSegment, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; @@ -181,6 +181,9 @@ fn convert_to_from( let from = snippet_opt(cx, self_ty.span)?; let into = snippet_opt(cx, target_ty.span)?; + let return_type = matches!(sig.decl.output, FnRetTy::Return(_)) + .then_some(String::from("Self")) + .unwrap_or_default(); let mut suggestions = vec![ // impl Into for U -> impl From for U // ~~~~ ~~~~ @@ -199,7 +202,7 @@ fn convert_to_from( (self_ident.span, format!("val: {from}")), // fn into(self) -> T -> fn into(self) -> Self // ~ ~~~~ - (sig.decl.output.span(), String::from("Self")), + (sig.decl.output.span(), return_type), ]; let mut finder = SelfFinder { diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index fb7b82ec304ef..1127f00abde04 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -53,7 +53,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // List of spans to lint. (lint_span, first_span) let mut lint_spans = Vec::new(); - let Ok(impls) = cx.tcx.crate_inherent_impls(()) else { return }; + let Ok(impls) = cx.tcx.crate_inherent_impls(()) else { + return; + }; let inherent_impls = cx .tcx .with_stable_hashing_context(|hcx| impls.inherent_impls.to_sorted(&hcx, true)); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index efdd392594977..feb4d188f3978 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -574,6 +574,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { warn_on_all_wildcard_imports, check_private_items, pub_underscore_fields_behavior, + ref allowed_duplicate_crates, blacklisted_names: _, cyclomatic_complexity_threshold: _, @@ -719,7 +720,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(needless_update::NeedlessUpdate)); store.register_late_pass(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)); store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef)); - store.register_late_pass(|_| Box::new(no_effect::NoEffect)); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); store.register_late_pass(move |_| { @@ -947,6 +948,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| { Box::new(cargo::Cargo { ignore_publish: cargo_ignore_publish, + allowed_duplicate_crates: allowed_duplicate_crates.clone(), }) }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index c245eaf1ab44d..920a887a6fd1b 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( vec.span, "it looks like the same item is being pushed into this Vec", None, - &format!("try using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), + &format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), ); } diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index c22f76484d03d..fa4f4a47e38e2 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lin use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators}; +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Expr, ExprKind}; @@ -128,6 +128,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' // check if replacement is mem::MaybeUninit::uninit().assume_init() && cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id) { + let Some(top_crate) = std_or_core(cx) else { return }; let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -136,7 +137,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' "replacing with `mem::MaybeUninit::uninit().assume_init()`", "consider using", format!( - "std::ptr::read({})", + "{top_crate}::ptr::read({})", snippet_with_applicability(cx, dest.span, "", &mut applicability) ), applicability, @@ -149,6 +150,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() { if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { + let Some(top_crate) = std_or_core(cx) else { return }; let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -157,7 +159,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' "replacing with `mem::uninitialized()`", "consider using", format!( - "std::ptr::read({})", + "{top_crate}::ptr::read({})", snippet_with_applicability(cx, dest.span, "", &mut applicability) ), applicability, @@ -184,14 +186,17 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< return; } if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) { + let Some(top_crate) = std_or_core(cx) else { return }; span_lint_and_then( cx, MEM_REPLACE_WITH_DEFAULT, expr_span, - "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", + &format!( + "replacing a value of type `T` with `T::default()` is better expressed using `{top_crate}::mem::take`" + ), |diag| { if !expr_span.from_expansion() { - let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); + let suggestion = format!("{top_crate}::mem::take({})", snippet(cx, dest.span, "")); diag.span_suggestion( expr_span, diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 70abe4891d985..4c7c56e7174a0 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{get_expr_use_or_unification_node, is_no_std_crate, is_res_lang_ctor, path_res}; +use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -58,10 +58,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re return; } + let Some(top_crate) = std_or_core(cx) else { return }; if let Some(i) = item { let sugg = format!( - "{}::iter::once({}{})", - if is_no_std_crate(cx) { "core" } else { "std" }, + "{top_crate}::iter::once({}{})", iter_type.ref_prefix(), snippet(cx, i.span, "...") ); @@ -81,11 +81,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re expr.span, &format!("`{method_name}` call on an empty collection"), "try", - if is_no_std_crate(cx) { - "core::iter::empty()".to_string() - } else { - "std::iter::empty()".to_string() - }, + format!("{top_crate}::iter::empty()"), Applicability::MaybeIncorrect, ); } diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs index 02f28779cf6ee..aa1ec60d434a5 100644 --- a/clippy_lints/src/methods/join_absolute_paths.rs +++ b/clippy_lints/src/methods/join_absolute_paths.rs @@ -42,7 +42,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a ) .span_suggestion( expr_span, - "if this is intentional, try using `Path::new` instead", + "if this is intentional, consider using `Path::new`", format!("PathBuf::from({arg_str})"), Applicability::Unspecified, ); diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 04bdbc1ea25fe..bf437db7e72ab 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -50,7 +50,7 @@ pub fn check( super::MANUAL_SATURATING_ARITHMETIC, expr.span, "manual saturating arithmetic", - &format!("try using `saturating_{arith}`"), + &format!("consider using `saturating_{arith}`"), format!( "{}.saturating_{arith}({})", snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index f9f636bbbf71b..27e17b43b01cc 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -113,9 +113,15 @@ fn handle_path( if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() && match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD) { - // FIXME: It would be better to infer the type to check if it's copyable or not - // to suggest to use `.copied()` instead of `.cloned()` where applicable. - lint_path(cx, e.span, recv.span); + // The `copied` and `cloned` methods are only available on `&T` and `&mut T` in `Option` + // and `Result`. + if let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind() + && let args = args.as_slice() + && let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type()) + && ty.is_ref() + { + lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); + } } } @@ -139,17 +145,19 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { ); } -fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span) { +fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; + let replacement = if is_copy { "copied" } else { "cloned" }; + span_lint_and_sugg( cx, MAP_CLONE, replace, "you are explicitly cloning with `.map()`", - "consider calling the dedicated `cloned` method", + &format!("consider calling the dedicated `{replacement}` method"), format!( - "{}.cloned()", + "{}.{replacement}()", snippet_with_applicability(cx, root, "..", &mut applicability), ), applicability, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 89ea3597dc0fd..03bcf108914a0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2827,6 +2827,44 @@ declare_clippy_lint! { "nonsensical combination of options for opening a file" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the suspicious use of `OpenOptions::create()` + /// without an explicit `OpenOptions::truncate()`. + /// + /// ### Why is this bad? + /// `create()` alone will either create a new file or open an + /// existing file. If the file already exists, it will be + /// overwritten when written to, but the file will not be + /// truncated by default. + /// If less data is written to the file + /// than it already contains, the remainder of the file will + /// remain unchanged, and the end of the file will contain old + /// data. + /// In most cases, one should either use `create_new` to ensure + /// the file is created from scratch, or ensure `truncate` is + /// called so that the truncation behaviour is explicit. `truncate(true)` + /// will ensure the file is entirely overwritten with new data, whereas + /// `truncate(false)` will explicitely keep the default behavior. + /// + /// ### Example + /// ```rust,no_run + /// use std::fs::OpenOptions; + /// + /// OpenOptions::new().create(true); + /// ``` + /// Use instead: + /// ```rust,no_run + /// use std::fs::OpenOptions; + /// + /// OpenOptions::new().create(true).truncate(true); + /// ``` + #[clippy::version = "1.75.0"] + pub SUSPICIOUS_OPEN_OPTIONS, + suspicious, + "suspicious combination of options for opening a file" +} + declare_clippy_lint! { /// ### What it does ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) @@ -4033,6 +4071,7 @@ impl_lint_pass!(Methods => [ MAP_ERR_IGNORE, MUT_MUTEX_LOCK, NONSENSICAL_OPEN_OPTIONS, + SUSPICIOUS_OPEN_OPTIONS, PATH_BUF_PUSH_OVERWRITE, RANGE_ZIP_WITH_LEN, REPEAT_ONCE, diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs index 65c986dcaccea..77484ab91a9d7 100644 --- a/clippy_lints/src/methods/open_options.rs +++ b/clippy_lints/src/methods/open_options.rs @@ -1,46 +1,74 @@ -use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use rustc_data_structures::fx::FxHashMap; + +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; +use clippy_utils::{match_any_def_paths, paths}; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_middle::ty::Ty; use rustc_span::source_map::Spanned; use rustc_span::{sym, Span}; -use super::NONSENSICAL_OPEN_OPTIONS; +use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; + +fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS) +} pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) - && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::FsOpenOptions) + && is_open_options(cx, cx.tcx.type_of(impl_id).instantiate_identity()) { let mut options = Vec::new(); - get_open_options(cx, recv, &mut options); - check_open_options(cx, &options, e.span); + if get_open_options(cx, recv, &mut options) { + check_open_options(cx, &options, e.span); + } } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Eq, PartialEq, Clone, Debug)] enum Argument { - True, - False, + Set(bool), Unknown, } -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq, Hash, Clone)] enum OpenOption { - Write, + Append, + Create, + CreateNew, Read, Truncate, - Create, - Append, + Write, +} +impl std::fmt::Display for OpenOption { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + OpenOption::Append => write!(f, "append"), + OpenOption::Create => write!(f, "create"), + OpenOption::CreateNew => write!(f, "create_new"), + OpenOption::Read => write!(f, "read"), + OpenOption::Truncate => write!(f, "truncate"), + OpenOption::Write => write!(f, "write"), + } + } } -fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) { - if let ExprKind::MethodCall(path, receiver, arguments, _) = argument.kind { +/// Collects information about a method call chain on `OpenOptions`. +/// Returns false if an unexpected expression kind was found "on the way", +/// and linting should then be avoided. +fn get_open_options( + cx: &LateContext<'_>, + argument: &Expr<'_>, + options: &mut Vec<(OpenOption, Argument, Span)>, +) -> bool { + if let ExprKind::MethodCall(path, receiver, arguments, span) = argument.kind { let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); // Only proceed if this is a call on some object of type std::fs::OpenOptions - if is_type_diagnostic_item(cx, obj_ty, sym::FsOpenOptions) && !arguments.is_empty() { + if !arguments.is_empty() && is_open_options(cx, obj_ty) { let argument_option = match arguments[0].kind { ExprKind::Lit(span) => { if let Spanned { @@ -48,11 +76,12 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec .. } = span { - if *lit { Argument::True } else { Argument::False } + Argument::Set(*lit) } else { // The function is called with a literal which is not a boolean literal. // This is theoretically possible, but not very likely. - return; + // We'll ignore it for now + return get_open_options(cx, receiver, options); } }, _ => Argument::Unknown, @@ -60,106 +89,77 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec match path.ident.as_str() { "create" => { - options.push((OpenOption::Create, argument_option)); + options.push((OpenOption::Create, argument_option, span)); + }, + "create_new" => { + options.push((OpenOption::CreateNew, argument_option, span)); }, "append" => { - options.push((OpenOption::Append, argument_option)); + options.push((OpenOption::Append, argument_option, span)); }, "truncate" => { - options.push((OpenOption::Truncate, argument_option)); + options.push((OpenOption::Truncate, argument_option, span)); }, "read" => { - options.push((OpenOption::Read, argument_option)); + options.push((OpenOption::Read, argument_option, span)); }, "write" => { - options.push((OpenOption::Write, argument_option)); + options.push((OpenOption::Write, argument_option, span)); + }, + _ => { + // Avoid linting altogether if this method is from a trait. + // This might be a user defined extension trait with a method like `truncate_write` + // which would be a false positive + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(argument.hir_id) + && cx.tcx.trait_of_item(method_def_id).is_some() + { + return false; + } }, - _ => (), } - get_open_options(cx, receiver, options); + get_open_options(cx, receiver, options) + } else { + false } + } else if let ExprKind::Call(callee, _) = argument.kind + && let ExprKind::Path(path) = callee.kind + && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id() + { + match_any_def_paths( + cx, + did, + &[ + &paths::TOKIO_IO_OPEN_OPTIONS_NEW, + &paths::OPEN_OPTIONS_NEW, + &paths::FILE_OPTIONS, + &paths::TOKIO_FILE_OPTIONS, + ], + ) + .is_some() + } else { + false } } -fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) { - let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false); - let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) = - (false, false, false, false, false); - // This code is almost duplicated (oh, the irony), but I haven't found a way to - // unify it. - - for option in options { - match *option { - (OpenOption::Create, arg) => { - if create { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `create` is called more than once", - ); - } else { - create = true; - } - create_arg = create_arg || (arg == Argument::True); - }, - (OpenOption::Append, arg) => { - if append { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `append` is called more than once", - ); - } else { - append = true; - } - append_arg = append_arg || (arg == Argument::True); - }, - (OpenOption::Truncate, arg) => { - if truncate { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `truncate` is called more than once", - ); - } else { - truncate = true; - } - truncate_arg = truncate_arg || (arg == Argument::True); - }, - (OpenOption::Read, arg) => { - if read { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `read` is called more than once", - ); - } else { - read = true; - } - read_arg = read_arg || (arg == Argument::True); - }, - (OpenOption::Write, arg) => { - if write { - span_lint( - cx, - NONSENSICAL_OPEN_OPTIONS, - span, - "the method `write` is called more than once", - ); - } else { - write = true; - } - write_arg = write_arg || (arg == Argument::True); - }, +fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument, Span)], span: Span) { + // The args passed to these methods, if they have been called + let mut options = FxHashMap::default(); + for (option, arg, sp) in settings { + if let Some((_, prev_span)) = options.insert(option.clone(), (arg.clone(), *sp)) { + span_lint( + cx, + NONSENSICAL_OPEN_OPTIONS, + prev_span, + &format!("the method `{}` is called more than once", &option), + ); } } - if read && truncate && read_arg && truncate_arg && !(write && write_arg) { + if let Some((Argument::Set(true), _)) = options.get(&OpenOption::Read) + && let Some((Argument::Set(true), _)) = options.get(&OpenOption::Truncate) + && let None | Some((Argument::Set(false), _)) = options.get(&OpenOption::Write) + { span_lint( cx, NONSENSICAL_OPEN_OPTIONS, @@ -167,7 +167,10 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], "file opened with `truncate` and `read`", ); } - if append && truncate && append_arg && truncate_arg { + + if let Some((Argument::Set(true), _)) = options.get(&OpenOption::Append) + && let Some((Argument::Set(true), _)) = options.get(&OpenOption::Truncate) + { span_lint( cx, NONSENSICAL_OPEN_OPTIONS, @@ -175,4 +178,29 @@ fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], "file opened with `append` and `truncate`", ); } + + if let Some((Argument::Set(true), create_span)) = options.get(&OpenOption::Create) + && let None = options.get(&OpenOption::Truncate) + && let None | Some((Argument::Set(false), _)) = options.get(&OpenOption::Append) + { + span_lint_and_then( + cx, + SUSPICIOUS_OPEN_OPTIONS, + *create_span, + "file opened with `create`, but `truncate` behavior not defined", + |diag| { + diag.span_suggestion( + create_span.shrink_to_hi(), + "add", + ".truncate(true)".to_string(), + rustc_errors::Applicability::MaybeIncorrect, + ) + .help("if you intend to overwrite an existing file entirely, call `.truncate(true)`") + .help( + "if you instead know that you may want to keep some parts of the old file, call `.truncate(false)`", + ) + .help("alternatively, use `.append(true)` to append to the file instead of overwriting it"); + }, + ); + } } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 756dbe62d84d6..88e2af15658ff 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -97,7 +97,7 @@ pub(super) fn check( }; let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, "..")); - let suggestion = format!("try using {method_hint} instead"); + let suggestion = format!("consider using {method_hint}"); let msg = format!("called `{current_method}` on an `Option` value"); span_lint_and_sugg( diff --git a/clippy_lints/src/methods/option_map_or_err_ok.rs b/clippy_lints/src/methods/option_map_or_err_ok.rs index 91e39d5a1cd27..4e424d4c066a6 100644 --- a/clippy_lints/src/methods/option_map_or_err_ok.rs +++ b/clippy_lints/src/methods/option_map_or_err_ok.rs @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( OPTION_MAP_OR_ERR_OK, expr.span, msg, - "try using `ok_or` instead", + "consider using `ok_or`", format!("{self_snippet}.ok_or({err_snippet})"), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index ff4d8cc9e3e1a..193deafccf650 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -72,7 +72,7 @@ pub(super) fn check<'tcx>( OPTION_MAP_OR_NONE, expr.span, msg, - "try using `map` instead", + "consider using `map`", format!("{self_snippet}.map({arg_snippet} {func_snippet})"), Applicability::MachineApplicable, ); @@ -85,7 +85,7 @@ pub(super) fn check<'tcx>( OPTION_MAP_OR_NONE, expr.span, msg, - "try using `and_then` instead", + "consider using `and_then`", format!("{self_snippet}.and_then({func_snippet})"), Applicability::MachineApplicable, ); @@ -97,7 +97,7 @@ pub(super) fn check<'tcx>( RESULT_MAP_OR_INTO_OPTION, expr.span, msg, - "try using `ok` instead", + "consider using `ok`", format!("{self_snippet}.ok()"), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/methods/result_map_or_else_none.rs b/clippy_lints/src/methods/result_map_or_else_none.rs index bc16a11281620..3b0dc506305b5 100644 --- a/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/clippy_lints/src/methods/result_map_or_else_none.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( RESULT_MAP_OR_INTO_OPTION, expr.span, msg, - "try using `ok` instead", + "consider using `ok`", format!("{self_snippet}.ok()"), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 6339011c92f58..ef1baa6c98820 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -63,7 +63,7 @@ pub(super) fn check<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), &msg, - "use `any()` instead", + "consider using", format!( "any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( SEARCH_IS_SOME, expr.span, &msg, - "use `!_.any()` instead", + "consider using", format!( "!{iter}.any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) @@ -118,7 +118,7 @@ pub(super) fn check<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), &msg, - "use `contains()` instead", + "consider using", format!("contains({find_arg})"), applicability, ); @@ -132,7 +132,7 @@ pub(super) fn check<'tcx>( SEARCH_IS_SOME, expr.span, &msg, - "use `!_.contains()` instead", + "consider using", format!("!{string}.contains({find_arg})"), applicability, ); diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs index 3983f0c0cabd4..363b1f2b81229 100644 --- a/clippy_lints/src/methods/single_char_pattern.rs +++ b/clippy_lints/src/methods/single_char_pattern.rs @@ -57,7 +57,7 @@ pub(super) fn check( SINGLE_CHAR_PATTERN, arg.span, "single-character string constant used as pattern", - "try using a `char` instead", + "consider using a `char`", hint, applicability, ); diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index e2b389e96dae0..c3ad4db387592 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( UNNECESSARY_JOIN, span.with_hi(expr.span.hi()), r#"called `.collect::>().join("")` on an iterator"#, - "try using", + "consider using", "collect::()".to_owned(), applicability, ); diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 696e5e74d60a5..6911da69b945e 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -203,7 +203,7 @@ pub(super) fn check<'tcx>( cx, UNNECESSARY_SORT_BY, expr.span, - "use Vec::sort_by_key here instead", + "consider using `sort_by_key`", "try", format!( "{}.sort{}_by_key(|{}| {})", @@ -226,7 +226,7 @@ pub(super) fn check<'tcx>( cx, UNNECESSARY_SORT_BY, expr.span, - "use Vec::sort here instead", + "consider using `sort`", "try", format!( "{}.sort{}()", diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 66727e5a29dc6..514015af0455c 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; -use clippy_utils::{get_parent_expr, is_diag_trait_item, match_def_path, paths, peel_blocks}; +use clippy_utils::{ + get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, paths, peel_blocks, strip_pat_refs, +}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -108,9 +110,12 @@ fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId) fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { match arg.kind { - hir::ExprKind::Closure(&hir::Closure { body, .. }) => { + hir::ExprKind::Closure(&hir::Closure { body, .. }) // If it's a closure, we need to check what is called. - let closure_body = cx.tcx.hir().body(body); + if let closure_body = cx.tcx.hir().body(body) + && let [param] = closure_body.params + && let hir::PatKind::Binding(_, local_id, ..) = strip_pat_refs(param.pat).kind => + { let closure_expr = peel_blocks(closure_body.value); match closure_expr.kind { hir::ExprKind::MethodCall(method, obj, [], _) => { @@ -122,14 +127,17 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + && path_to_local_id(obj, local_id) { true } else { false } }, - hir::ExprKind::Call(call, [_]) => { - if let hir::ExprKind::Path(qpath) = call.kind { + hir::ExprKind::Call(call, [recv]) => { + if let hir::ExprKind::Path(qpath) = call.kind + && path_to_local_id(recv, local_id) + { check_qpath(cx, qpath, call.hir_id) } else { false diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 64ef709e2fa2d..d2eef6ae4338e 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -5,16 +5,15 @@ 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_qpath, FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{ BlockCheckMode, Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, - PatKind, QPath, + PatKind, }; use rustc_hir_typeck::expr_use_visitor as euv; 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; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath}; use rustc_session::impl_lint_pass; @@ -234,12 +233,29 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { } } - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - cx.tcx.hir().visit_all_item_likes_in_crate(&mut FnNeedsMutVisitor { - cx, - used_fn_def_ids: &mut self.used_fn_def_ids, - }); + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + // #11182; do not lint if mutability is required elsewhere + if let ExprKind::Path(..) = expr.kind + && let Some(parent) = get_parent_node(cx.tcx, expr.hir_id) + && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(expr).kind() + && let Some(def_id) = def_id.as_local() + { + if let Node::Expr(e) = parent + && let ExprKind::Call(call, _) = e.kind + && call.hir_id == expr.hir_id + { + return; + } + // We don't need to check each argument individually as you cannot coerce a function + // taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's + // passed as a `fn`-like argument (or is unified) and should ignore every "unused" + // argument entirely + self.used_fn_def_ids.insert(def_id); + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { for (fn_def_id, unused) in self .fn_def_ids_to_maybe_unused_mut .iter() @@ -501,48 +517,3 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> { } } } - -/// A final pass to check for paths referencing this function that require the argument to be -/// `&mut`, basically if the function is ever used as a `fn`-like argument. -struct FnNeedsMutVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - used_fn_def_ids: &'a mut FxHashSet, -} - -impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> { - type NestedFilter = OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } - - fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, hir_id: HirId, _: Span) { - walk_qpath(self, qpath, hir_id); - - let Self { cx, used_fn_def_ids } = self; - - // #11182; do not lint if mutability is required elsewhere - if let Node::Expr(expr) = cx.tcx.hir_node(hir_id) - && let Some(parent) = get_parent_node(cx.tcx, expr.hir_id) - && let ty::FnDef(def_id, _) = cx - .tcx - .typeck(cx.tcx.hir().enclosing_body_owner(hir_id)) - .expr_ty(expr) - .kind() - && let Some(def_id) = def_id.as_local() - { - if let Node::Expr(e) = parent - && let ExprKind::Call(call, _) = e.kind - && call.hir_id == expr.hir_id - { - return; - } - - // We don't need to check each argument individually as you cannot coerce a function - // taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's - // passed as a `fn`-like argument (or is unified) and should ignore every "unused" - // argument entirely - used_fn_def_ids.insert(def_id); - } - } -} diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 6bbe427ea2988..0d234f7f9b52c 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,16 +1,18 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; -use clippy_utils::{get_parent_node, is_lint_allowed, peel_blocks}; +use clippy_utils::{any_parent_is_automatically_derived, get_parent_node, is_lint_allowed, path_to_local, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, ItemKind, Node, PatKind, Stmt, StmtKind, UnsafeSource, + is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, Node, PatKind, Stmt, + StmtKind, UnsafeSource, }; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; +use rustc_span::Span; use std::ops::Deref; declare_clippy_lint! { @@ -74,94 +76,125 @@ declare_clippy_lint! { "outer expressions with no effect" } -declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]); +#[derive(Default)] +pub struct NoEffect { + underscore_bindings: HirIdMap, + local_bindings: Vec>, +} + +impl_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]); impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if check_no_effect(cx, stmt) { + if self.check_no_effect(cx, stmt) { return; } check_unnecessary_operation(cx, stmt); } -} -fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { - if let StmtKind::Semi(expr) = stmt.kind { - // move `expr.span.from_expansion()` ahead - if expr.span.from_expansion() { - return false; + fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) { + self.local_bindings.push(Vec::default()); + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) { + for hir_id in self.local_bindings.pop().unwrap() { + if let Some(span) = self.underscore_bindings.remove(&hir_id) { + span_lint_hir( + cx, + NO_EFFECT_UNDERSCORE_BINDING, + hir_id, + span, + "binding to `_` prefixed variable with no side-effect", + ); + } } - let expr = peel_blocks(expr); + } - if is_operator_overridden(cx, expr) { - // Return `true`, to prevent `check_unnecessary_operation` from - // linting on this statement as well. - return true; + fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if let Some(def_id) = path_to_local(expr) { + self.underscore_bindings.remove(&def_id); } - if has_no_effect(cx, expr) { - span_lint_hir_and_then( - cx, - NO_EFFECT, - expr.hir_id, - stmt.span, - "statement with no effect", - |diag| { - for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { - if let Node::Item(item) = parent.1 - && 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 = cx - .tcx - .fn_sig(item.owner_id) - .instantiate_identity() - .output() - .skip_binder(); + } +} + +impl NoEffect { + fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { + if let StmtKind::Semi(expr) = stmt.kind { + // move `expr.span.from_expansion()` ahead + if expr.span.from_expansion() { + return false; + } + let expr = peel_blocks(expr); - // Remove `impl Future` to get `T` - if cx.tcx.ty_is_opaque_future(ret_ty) - && let Some(true_ret_ty) = cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty) + if is_operator_overridden(cx, expr) { + // Return `true`, to prevent `check_unnecessary_operation` from + // linting on this statement as well. + return true; + } + if has_no_effect(cx, expr) { + span_lint_hir_and_then( + cx, + NO_EFFECT, + expr.hir_id, + stmt.span, + "statement with no effect", + |diag| { + for parent in cx.tcx.hir().parent_iter(stmt.hir_id) { + if let Node::Item(item) = parent.1 + && 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 { - ret_ty = true_ret_ty; - } + let expr_ty = cx.typeck_results().expr_ty(expr); + 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) + && let Some(true_ret_ty) = + cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty) + { + ret_ty = true_ret_ty; + } - if !ret_ty.is_unit() && ret_ty == expr_ty { - diag.span_suggestion( - stmt.span.shrink_to_lo(), - "did you mean to return it?", - "return ", - Applicability::MaybeIncorrect, - ); + if !ret_ty.is_unit() && ret_ty == expr_ty { + diag.span_suggestion( + stmt.span.shrink_to_lo(), + "did you mean to return it?", + "return ", + Applicability::MaybeIncorrect, + ); + } } } - } - }, - ); - return true; - } - } else if let StmtKind::Local(local) = stmt.kind { - if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) - && let Some(init) = local.init - && local.els.is_none() - && !local.pat.span.from_expansion() - && has_no_effect(cx, init) - && let PatKind::Binding(_, _, ident, _) = local.pat.kind - && ident.name.to_ident_string().starts_with('_') - { - span_lint_hir( - cx, - NO_EFFECT_UNDERSCORE_BINDING, - init.hir_id, - stmt.span, - "binding to `_` prefixed variable with no side-effect", - ); - return true; + }, + ); + return true; + } + } else if let StmtKind::Local(local) = stmt.kind { + if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id) + && let Some(init) = local.init + && local.els.is_none() + && !local.pat.span.from_expansion() + && has_no_effect(cx, init) + && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind + && ident.name.to_ident_string().starts_with('_') + && !any_parent_is_automatically_derived(cx.tcx, local.hir_id) + { + if let Some(l) = self.local_bindings.last_mut() { + l.push(hir_id); + self.underscore_bindings.insert(hir_id, ident.span); + } + return true; + } } + false } - false } fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs index 9db2e24630aac..a69989e400be4 100644 --- a/clippy_lints/src/operators/ptr_eq.rs +++ b/clippy_lints/src/operators/ptr_eq.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; +use clippy_utils::std_or_core; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use super::PTR_EQ; -static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; - pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -26,13 +25,14 @@ pub(super) fn check<'tcx>( && let Some(left_snip) = snippet_opt(cx, left_var.span) && let Some(right_snip) = snippet_opt(cx, right_var.span) { + let Some(top_crate) = std_or_core(cx) else { return }; span_lint_and_sugg( cx, PTR_EQ, expr.span, - LINT_MSG, + &format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), "try", - format!("std::ptr::eq({left_snip}, {right_snip})"), + format!("{top_crate}::ptr::eq({left_snip}, {right_snip})"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/pub_underscore_fields.rs b/clippy_lints/src/pub_underscore_fields.rs index 00465ce43813e..88b5a6cfe2aa7 100644 --- a/clippy_lints/src/pub_underscore_fields.rs +++ b/clippy_lints/src/pub_underscore_fields.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { }; let is_visible = |field: &FieldDef<'_>| match self.behavior { - PubUnderscoreFieldsBehaviour::PublicallyExported => cx.effective_visibilities.is_reachable(field.def_id), + PubUnderscoreFieldsBehaviour::PubliclyExported => cx.effective_visibilities.is_reachable(field.def_id), PubUnderscoreFieldsBehaviour::AllPubFields => { // If there is a visibility span then the field is marked pub in some way. !field.vis_span.is_empty() diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index 62f3c09aa7e26..650324d4249eb 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::get_enclosing_block; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::source::snippet; -use clippy_utils::visitors::for_each_expr; -use core::ops::ControlFlow; -use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; + +use hir::{Expr, ExprKind, HirId, Local, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -49,57 +51,40 @@ declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]); impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) { - for (idx, stmt) in block.stmts.iter().enumerate() { - if !stmt.span.from_expansion() - // matches `let v = Vec::new();` - && let StmtKind::Local(local) = stmt.kind - && let Local { pat, init: Some(init), .. } = local - && let PatKind::Binding(_, _, ident, _) = pat.kind + for stmt in block.stmts { + if stmt.span.from_expansion() { + return; + } + + if let StmtKind::Local(local) = stmt.kind + && let Local { + pat, init: Some(init), .. + } = local + && let PatKind::Binding(_, id, ident, _) = pat.kind && let Some(vec_init_kind) = get_vec_init_kind(cx, init) { - let visitor = |expr: &Expr<'_>| { - if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind - && let PathSegment { - ident: read_or_read_exact, - .. - } = *path - && matches!(read_or_read_exact.as_str(), "read" | "read_exact") - && let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind - && let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind - && let [inner_seg] = inner_path.segments - && ident.name == inner_seg.ident.name - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } + let mut visitor = ReadVecVisitor { + local_id: id, + read_zero_expr: None, + has_resize: false, }; - let (read_found, next_stmt_span) = if let Some(next_stmt) = block.stmts.get(idx + 1) { - // case { .. stmt; stmt; .. } - (for_each_expr(next_stmt, visitor).is_some(), next_stmt.span) - } else if let Some(e) = block.expr { - // case { .. stmt; expr } - (for_each_expr(e, visitor).is_some(), e.span) - } else { + let Some(enclosing_block) = get_enclosing_block(cx, id) else { return; }; + visitor.visit_block(enclosing_block); - if read_found && !next_stmt_span.from_expansion() { + if let Some(expr) = visitor.read_zero_expr { let applicability = Applicability::MaybeIncorrect; match vec_init_kind { VecInitKind::WithConstCapacity(len) => { span_lint_and_sugg( cx, READ_ZERO_BYTE_VEC, - next_stmt_span, + expr.span, "reading zero byte data to `Vec`", "try", - format!( - "{}.resize({len}, 0); {}", - ident.as_str(), - snippet(cx, next_stmt_span, "..") - ), + format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")), applicability, ); }, @@ -108,25 +93,20 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { span_lint_and_sugg( cx, READ_ZERO_BYTE_VEC, - next_stmt_span, + expr.span, "reading zero byte data to `Vec`", "try", format!( "{}.resize({}, 0); {}", ident.as_str(), snippet(cx, e.span, ".."), - snippet(cx, next_stmt_span, "..") + snippet(cx, expr.span, "..") ), applicability, ); }, _ => { - span_lint( - cx, - READ_ZERO_BYTE_VEC, - next_stmt_span, - "reading zero byte data to `Vec`", - ); + span_lint(cx, READ_ZERO_BYTE_VEC, expr.span, "reading zero byte data to `Vec`"); }, } } @@ -134,3 +114,47 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { } } } + +struct ReadVecVisitor<'tcx> { + local_id: HirId, + read_zero_expr: Option<&'tcx Expr<'tcx>>, + has_resize: bool, +} + +impl<'tcx> Visitor<'tcx> for ReadVecVisitor<'tcx> { + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(path, receiver, args, _) = e.kind { + let PathSegment { ident, .. } = *path; + + match ident.as_str() { + "read" | "read_exact" => { + let [arg] = args else { return }; + if let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind + && let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind + && let [inner_seg] = inner_path.segments + && let Res::Local(res_id) = inner_seg.res + && self.local_id == res_id + { + self.read_zero_expr = Some(e); + return; + } + }, + "resize" => { + // If the Vec is resized, then it's a valid read + if let ExprKind::Path(QPath::Resolved(_, inner_path)) = receiver.kind + && let Res::Local(res_id) = inner_path.res + && self.local_id == res_id + { + self.has_resize = true; + return; + } + }, + _ => {}, + } + } + + if !self.has_resize && self.read_zero_expr.is_none() { + walk_expr(self, e); + } + } +} diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 2cd3e57f885a2..6540626f7d5a3 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::{ExpnKind, MacroKind, Span}; declare_clippy_lint! { /// ### What it does @@ -39,6 +40,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if !block.span.from_expansion() && let Some(expr) = block.expr + && !from_attr_macro(expr.span) && let t_expr = cx.typeck_results().expr_ty(expr) && t_expr.is_unit() && let mut app = Applicability::MachineApplicable @@ -63,3 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { } } } + +fn from_attr_macro(span: Span) -> bool { + matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Attr, _)) +} diff --git a/clippy_lints/src/single_call_fn.rs b/clippy_lints/src/single_call_fn.rs index 8e181c3ccc743..0387742077447 100644 --- a/clippy_lints/src/single_call_fn.rs +++ b/clippy_lints/src/single_call_fn.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::{is_from_proc_macro, is_in_test_function}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; @@ -88,16 +88,18 @@ impl<'tcx> LateLintPass<'tcx> for SingleCallFn { }; cx.tcx.hir().visit_all_item_likes_in_crate(&mut v); - for usage in self.def_id_to_usage.values() { + for (&def_id, usage) in &self.def_id_to_usage { let single_call_fn_span = usage.0; if let [caller_span] = *usage.1 { - span_lint_and_help( + span_lint_hir_and_then( cx, SINGLE_CALL_FN, + cx.tcx.local_def_id_to_hir_id(def_id), single_call_fn_span, "this function is only used once", - Some(caller_span), - "used here", + |diag| { + diag.span_help(caller_span, "used here"); + }, ); } } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index e4054393d0ad7..768623b5d0347 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -390,6 +390,14 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &' } } +fn get_ty_res(ty: Ty<'_>) -> Option { + match ty.kind { + TyKind::Path(QPath::Resolved(_, path)) => Some(path.res), + TyKind::Path(QPath::TypeRelative(ty, _)) => get_ty_res(*ty), + _ => None, + } +} + // FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef { ComparableTraitRef( @@ -401,10 +409,8 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef { .filter_map(|segment| { // get trait bound type arguments Some(segment.args?.args.iter().filter_map(|arg| { - if let GenericArg::Type(ty) = arg - && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind - { - return Some(path.res); + if let GenericArg::Type(ty) = arg { + return get_ty_res(**ty); } None })) diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs index 7d31c375f8cf2..2a6c248125459 100644 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_INT_TO_CHAR; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg; +use clippy_utils::{std_or_core, sugg}; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -25,6 +25,7 @@ pub(super) fn check<'tcx>( e.span, &format!("transmute from a `{from_ty}` to a `char`"), |diag| { + let Some(top_crate) = std_or_core(cx) else { return }; let arg = sugg::Sugg::hir(cx, arg, ".."); let arg = if let ty::Int(_) = from_ty.kind() { arg.as_ty(ast::UintTy::U32.name_str()) @@ -34,7 +35,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("std::char::from_u32({arg}).unwrap()"), + format!("{top_crate}::char::from_u32({arg}).unwrap()"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 98e9ea2d7751f..6c885ebdea11d 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -1,7 +1,7 @@ use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; -use clippy_utils::sugg; +use clippy_utils::{std_or_core, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -25,6 +25,8 @@ pub(super) fn check<'tcx>( && let ty::Uint(ty::UintTy::U8) = slice_ty.kind() && from_mutbl == to_mutbl { + let Some(top_crate) = std_or_core(cx) else { return true }; + let postfix = if *from_mutbl == Mutability::Mut { "_mut" } else { "" }; let snippet = snippet(cx, arg.span, ".."); @@ -36,9 +38,9 @@ pub(super) fn check<'tcx>( &format!("transmute from a `{from_ty}` to a `{to_ty}`"), "consider using", if const_context { - format!("std::str::from_utf8_unchecked{postfix}({snippet})") + format!("{top_crate}::str::from_utf8_unchecked{postfix}({snippet})") } else { - format!("std::str::from_utf8{postfix}({snippet}).unwrap()") + format!("{top_crate}::str::from_utf8{postfix}({snippet}).unwrap()") }, Applicability::MaybeIncorrect, ); diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index b418db53ea47c..209035804e43e 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -167,7 +167,15 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca false } }, - ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => { + ExprKind::MethodCall(segment, receiver, &[_arg], _) if segment.ident.name == name.name => { + if let Some(ty) = cx.typeck_results().expr_ty_opt(receiver) + && let Some(ty_id) = get_ty_def_id(ty) + && self_arg != ty_id + { + // Since this called on a different type, the lint should not be + // triggered here. + return; + } if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) && trait_id == trait_def_id diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 1de9adfcb963a..adc66e15ff501 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,9 +1,10 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{is_trait_method, is_try, match_trait_method, paths}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths}; +use hir::{ExprKind, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -45,126 +46,219 @@ declare_clippy_lint! { declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]); +#[derive(Copy, Clone)] +enum IoOp { + AsyncWrite(bool), + AsyncRead(bool), + SyncRead(bool), + SyncWrite(bool), +} + impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { - fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else { - return; - }; + /// We perform the check on the block level. + /// If we want to catch match and if expressions that act as returns of the block + /// we need to check them at `check_expr` or `check_block` as they are not stmts + /// but we can't check them at `check_expr` because we need the broader context + /// because we should do this only for the final expression of the block, and not for + /// `StmtKind::Local` which binds values => the io amount is used. + /// + /// To check for unused io amount in stmts, we only consider `StmtKind::Semi`. + /// `StmtKind::Local` is not considered because it binds values => the io amount is used. + /// `StmtKind::Expr` is not considered because requires unit type => the io amount is used. + /// `StmtKind::Item` is not considered because it's not an expression. + /// + /// We then check the individual expressions via `check_expr`. We use the same logic for + /// semi expressions and the final expression as we need to check match and if expressions + /// for binding of the io amount to `Ok(_)`. + /// + /// We explicitly check for the match source to be Normal as it needs special logic + /// to consider the arms, and we want to avoid breaking the logic for situations where things + /// get desugared to match. + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) { + for stmt in block.stmts { + if let hir::StmtKind::Semi(exp) = stmt.kind { + check_expr(cx, exp); + } + } - match expr.kind { - hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => { - if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind { - if matches!( - func.kind, - hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) - ) { - check_map_error(cx, arg_0, expr); + if let Some(exp) = block.expr + && matches!(exp.kind, hir::ExprKind::If(_, _, _) | hir::ExprKind::Match(_, _, _)) + { + check_expr(cx, exp); + } + } +} + +fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { + match expr.kind { + hir::ExprKind::If(cond, _, _) + if let ExprKind::Let(hir::Let { pat, init, .. }) = cond.kind + && pattern_is_ignored_ok(cx, pat) + && let Some(op) = should_lint(cx, init) => + { + emit_lint(cx, cond.span, op, &[pat.span]); + }, + hir::ExprKind::Match(expr, arms, hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => { + let found_arms: Vec<_> = arms + .iter() + .filter_map(|arm| { + if pattern_is_ignored_ok(cx, arm.pat) { + Some(arm.span) + } else { + None } - } else { - check_map_error(cx, res, expr); - } - }, - hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() { - "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => { - check_map_error(cx, arg_0, expr); - }, - _ => (), - }, - _ => (), + }) + .collect(); + if !found_arms.is_empty() { + emit_lint(cx, expr.span, op, found_arms.as_slice()); + } + }, + _ if let Some(op) = should_lint(cx, expr) => { + emit_lint(cx, expr.span, op, &[]); + }, + _ => {}, + }; +} + +fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option { + inner = unpack_match(inner); + inner = unpack_try(inner); + inner = unpack_call_chain(inner); + inner = unpack_await(inner); + // we type-check it to get whether it's a read/write or their vectorized forms + // and keep only the ones that are produce io amount + check_io_mode(cx, inner) +} + +fn pattern_is_ignored_ok(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> bool { + // the if checks whether we are in a result Ok( ) pattern + // and the return checks whether it is unhandled + + if let PatKind::TupleStruct(ref path, inner_pat, ddp) = pat.kind + // we check against Result::Ok to avoid linting on Err(_) or something else. + && is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk) + { + return match (inner_pat, ddp.as_opt_usize()) { + // Ok(_) pattern + ([inner_pat], None) if matches!(inner_pat.kind, PatKind::Wild) => true, + // Ok(..) pattern + ([], Some(0)) => true, + _ => false, + }; + } + false +} + +fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { + while let hir::ExprKind::MethodCall(path, receiver, ..) = expr.kind { + if matches!( + path.ident.as_str(), + "unwrap" | "expect" | "unwrap_or" | "unwrap_or_else" | "ok" | "is_ok" | "is_err" | "or_else" | "or" + ) { + expr = receiver; + } else { + break; } } + expr +} + +fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { + while let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind + && matches!( + func.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) + ) + { + expr = arg_0; + } + expr +} + +fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { + while let hir::ExprKind::Match(res, _, _) = expr.kind { + expr = res; + } + expr } /// If `expr` is an (e).await, return the inner expression "e" that's being /// waited on. Otherwise return None. -fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&hir::Expr<'a>> { +fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &hir::Expr<'a> { if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind { if matches!( func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) ) { - return Some(arg_0); + return arg_0; } } } - - None + expr } -fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { - let mut call = call; - while let hir::ExprKind::MethodCall(path, receiver, ..) = call.kind { - if matches!(path.ident.as_str(), "or" | "or_else" | "ok") { - call = receiver; - } else { - break; - } - } +/// Check whether the current expr is a function call for an IO operation +fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option { + let hir::ExprKind::MethodCall(path, ..) = call.kind else { + return None; + }; - if let Some(call) = try_remove_await(call) { - check_method_call(cx, call, expr, true); - } else { - check_method_call(cx, call, expr, false); + let vectorized = match path.ident.as_str() { + "write_vectored" | "read_vectored" => true, + "write" | "read" => false, + _ => { + return None; + }, + }; + + match ( + is_trait_method(cx, call, sym::IoRead), + is_trait_method(cx, call, sym::IoWrite), + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT), + match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) + || match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT), + ) { + (true, _, _, _) => Some(IoOp::SyncRead(vectorized)), + (_, true, _, _) => Some(IoOp::SyncWrite(vectorized)), + (_, _, true, _) => Some(IoOp::AsyncRead(vectorized)), + (_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)), + _ => None, } } -fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) { - if let hir::ExprKind::MethodCall(path, ..) = call.kind { - let symbol = path.ident.as_str(); - let read_trait = if is_await { - match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) - || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT) - } else { - is_trait_method(cx, call, sym::IoRead) - }; - let write_trait = if is_await { - match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT) - || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) - } else { - is_trait_method(cx, call, sym::IoWrite) - }; +fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) { + let (msg, help) = match op { + IoOp::AsyncRead(false) => ( + "read amount is not handled", + Some("use `AsyncReadExt::read_exact` instead, or handle partial reads"), + ), + IoOp::SyncRead(false) => ( + "read amount is not handled", + Some("use `Read::read_exact` instead, or handle partial reads"), + ), + IoOp::SyncWrite(false) => ( + "written amount is not handled", + Some("use `Write::write_all` instead, or handle partial writes"), + ), + IoOp::AsyncWrite(false) => ( + "written amount is not handled", + Some("use `AsyncWriteExt::write_all` instead, or handle partial writes"), + ), + IoOp::SyncRead(true) | IoOp::AsyncRead(true) => ("read amount is not handled", None), + IoOp::SyncWrite(true) | IoOp::AsyncWrite(true) => ("written amount is not handled", None), + }; - match (read_trait, write_trait, symbol, is_await) { - (true, _, "read", false) => span_lint_and_help( - cx, - UNUSED_IO_AMOUNT, - expr.span, - "read amount is not handled", - None, - "use `Read::read_exact` instead, or handle partial reads", - ), - (true, _, "read", true) => span_lint_and_help( - cx, - UNUSED_IO_AMOUNT, - expr.span, - "read amount is not handled", - None, - "use `AsyncReadExt::read_exact` instead, or handle partial reads", - ), - (true, _, "read_vectored", _) => { - span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"); - }, - (_, true, "write", false) => span_lint_and_help( - cx, - UNUSED_IO_AMOUNT, - expr.span, - "written amount is not handled", - None, - "use `Write::write_all` instead, or handle partial writes", - ), - (_, true, "write", true) => span_lint_and_help( - cx, - UNUSED_IO_AMOUNT, - expr.span, - "written amount is not handled", - None, - "use `AsyncWriteExt::write_all` instead, or handle partial writes", - ), - (_, true, "write_vectored", _) => { - span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"); - }, - _ => (), + span_lint_and_then(cx, UNUSED_IO_AMOUNT, span, msg, |diag| { + if let Some(help_str) = help { + diag.help(help_str); } - } + for span in wild_cards { + diag.span_note( + *span, + "the result is consumed here, but the amount of I/O bytes remains unhandled", + ); + } + }); } diff --git a/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs b/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs index 5ddedb24b15a4..4822970e47ef5 100644 --- a/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs +++ b/clippy_lints/src/utils/internal_lints/almost_standard_lint_formulation.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for AlmostStandardFormulation { ident.span, "non-standard lint formulation", None, - &format!("try using `{}` instead", formulation.correction), + &format!("consider using `{}`", formulation.correction), ); } return; diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 4fb615e1d579e..c62ae8d718dba 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -71,7 +71,9 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { SimplifiedType::Str, ] .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied()); + .flat_map(|&ty| cx.tcx.incoherent_impls(ty).into_iter()) + .flatten() + .copied(); for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { let lang_item_path = cx.get_def_path(item_def_id); if path_syms.starts_with(&lang_item_path) { diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index ad8619f0d3d96..2d0c2cf125364 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,5 +1,6 @@ use rustc_ast::{ast, attr}; use rustc_errors::Applicability; +use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; use rustc_span::sym; use std::str::FromStr; @@ -159,3 +160,14 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { .filter_map(ast::Attribute::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } + +pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { + adt.is_variant_list_non_exhaustive() + || tcx.has_attr(adt.did(), sym::non_exhaustive) + || adt.variants().iter().any(|variant_def| { + variant_def.is_field_list_non_exhaustive() || tcx.has_attr(variant_def.def_id, sym::non_exhaustive) + }) + || adt + .all_fields() + .any(|field_def| tcx.has_attr(field_def.did, sym::non_exhaustive)) +} diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 979b117db256a..4fa93ad23c369 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -134,7 +134,7 @@ impl HirEqInterExpr<'_, '_, '_> { /// Checks whether two blocks are the same. #[expect(clippy::similar_names)] fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { - use TokenKind::{BlockComment, LineComment, Semi, Whitespace}; + use TokenKind::{Semi, Whitespace}; if left.stmts.len() != right.stmts.len() { return false; } @@ -177,7 +177,7 @@ impl HirEqInterExpr<'_, '_, '_> { return false; } if !eq_span_tokens(self.inner.cx, lstart..lstmt_span.lo, rstart..rstmt_span.lo, |t| { - !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) + !matches!(t, Whitespace | Semi) }) { return false; } @@ -212,7 +212,7 @@ impl HirEqInterExpr<'_, '_, '_> { return false; } eq_span_tokens(self.inner.cx, lstart..lend, rstart..rend, |t| { - !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) + !matches!(t, Whitespace | Semi) }) } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index ebe520b27eb56..4e499ff4cc612 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -536,7 +536,12 @@ fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator SimplifiedType::Float(FloatTy::F32), "f64" => SimplifiedType::Float(FloatTy::F64), #[allow(trivial_casts)] - _ => return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_]).into_iter().flatten().copied(), + _ => { + return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_]) + .into_iter() + .flatten() + .copied(); + }, }; tcx.incoherent_impls(ty).into_iter().flatten().copied() @@ -1712,7 +1717,6 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)), PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), - PatKind::Lit(..) | PatKind::Range(..) => true, PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), PatKind::Or(pats) => { // TODO: should be the honest check, that pats is exhaustive set @@ -1736,7 +1740,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, } }, - PatKind::Err(_) => true, + PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) => true, } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 0a820a1754cae..0051f78451467 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -26,6 +26,7 @@ pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; +pub const FILE_OPTIONS: [&str; 4] = ["std", "fs", "File", "options"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates @@ -50,6 +51,7 @@ pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MSRV: [&str; 3] = ["clippy_config", "msrvs", "Msrv"]; +pub const OPEN_OPTIONS_NEW: [&str; 4] = ["std", "fs", "OpenOptions", "new"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; @@ -89,9 +91,15 @@ pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"]; +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"]; +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"]; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 59ebe685c11ee..11bb16ff6c5bf 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -219,7 +219,8 @@ pub fn implements_trait<'tcx>( /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. /// -/// The `callee_id` argument is used to determine whether this is a function call in a `const fn` environment, used for checking const traits. +/// The `callee_id` argument is used to determine whether this is a function call in a `const fn` +/// environment, used for checking const traits. pub fn implements_trait_with_env<'tcx>( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, diff --git a/rust-toolchain b/rust-toolchain index 2589d46e7da07..d2d56e59ee3fb 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-01-11" +channel = "nightly-2024-01-25" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.stderr b/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.stderr new file mode 100644 index 0000000000000..8f0ca76492497 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.stderr @@ -0,0 +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 1 previous error diff --git a/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.toml new file mode 100644 index 0000000000000..d39ad6c909a8b --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/Cargo.toml @@ -0,0 +1,14 @@ +# Should not lint for dev or build dependencies. See issue 5041. + +[package] +# purposefully separated by - instead of _ +name = "multiple-crate-versions" +version = "0.1.0" +publish = false + +[workspace] + +# One of the versions of winapi is only a dev dependency: allowed +[dependencies] +winapi = "0.2" +ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/src/main.rs b/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/src/main.rs new file mode 100644 index 0000000000000..4bc61dd62992f --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/12145_with_dashes/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/Cargo.toml new file mode 100644 index 0000000000000..79317659ac072 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "multiple_crate_versions" +version = "0.1.0" +publish = false + +[workspace] + +[dependencies] +winapi = "0.2" +ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/clippy.toml b/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/clippy.toml new file mode 100644 index 0000000000000..121361de2fe0b --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/clippy.toml @@ -0,0 +1 @@ +allowed-duplicate-crates = ["winapi"] diff --git a/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/src/main.rs b/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/src/main.rs new file mode 100644 index 0000000000000..4bc61dd62992f --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/12176_allow_duplicate_crates/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} diff --git a/tests/ui-internal/check_formulation.stderr b/tests/ui-internal/check_formulation.stderr index 96fa617601ff4..42a872d9a83f7 100644 --- a/tests/ui-internal/check_formulation.stderr +++ b/tests/ui-internal/check_formulation.stderr @@ -4,7 +4,7 @@ error: non-standard lint formulation LL | /// Check for lint formulations that are correct | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try using `Checks for` instead + = help: consider using `Checks for` = note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` @@ -14,7 +14,7 @@ error: non-standard lint formulation LL | /// Detects uses of incorrect formulations | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: try using `Checks for` instead + = help: consider using `Checks for` error: aborting due to 2 previous errors diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr index 03556823a8f50..5ca183d41a93a 100644 --- a/tests/ui-internal/disallow_span_lint.stderr +++ b/tests/ui-internal/disallow_span_lint.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` - --> $DIR/disallow_struct_span_lint.rs:14:5 + --> $DIR/disallow_span_lint.rs:14:5 | LL | cx.span_lint(lint, span, msg, |_| {}); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,10 +8,10 @@ LL | cx.span_lint(lint, span, msg, |_| {}); = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> $DIR/disallow_struct_span_lint.rs:24:5 + --> $DIR/disallow_span_lint.rs:24:5 | LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {}); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui-toml/pub_underscore_fields/exported/clippy.toml b/tests/ui-toml/pub_underscore_fields/exported/clippy.toml index 94a0d3554bc7b..9e472bd14ba53 100644 --- a/tests/ui-toml/pub_underscore_fields/exported/clippy.toml +++ b/tests/ui-toml/pub_underscore_fields/exported/clippy.toml @@ -1 +1 @@ -pub-underscore-fields-behavior = "PublicallyExported" \ No newline at end of file +pub-underscore-fields-behavior = "PubliclyExported" diff --git a/tests/ui-toml/toml_unknown_key/clippy.toml b/tests/ui-toml/toml_unknown_key/clippy.toml index b77b4580051ef..2b63f6e5c26f7 100644 --- a/tests/ui-toml/toml_unknown_key/clippy.toml +++ b/tests/ui-toml/toml_unknown_key/clippy.toml @@ -3,6 +3,9 @@ foobar = 42 # so is this one barfoo = 53 +# when using underscores instead of dashes, suggest the correct one +allow_mixed_uninlined_format_args = true + # that one is ignored [third-party] clippy-feature = "nightly" diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs b/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs index 38009627757c2..49139b60a9f43 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.rs @@ -1,3 +1,4 @@ +//@no-rustfix //@error-in-other-file: unknown field `foobar`, expected one of fn main() {} 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 ed4fb84df1ae1..fc683e514ba40 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -11,6 +11,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-private-module-inception allow-unwrap-in-tests allowed-dotfiles + allowed-duplicate-crates allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed @@ -87,6 +88,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-private-module-inception allow-unwrap-in-tests allowed-dotfiles + allowed-duplicate-crates allowed-idents-below-min-chars allowed-scripts arithmetic-side-effects-allowed @@ -150,5 +152,82 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect LL | barfoo = 53 | ^^^^^^ -error: aborting due to 2 previous errors +error: error reading Clippy's configuration file: unknown field `allow_mixed_uninlined_format_args`, expected one of + absolute-paths-allowed-crates + absolute-paths-max-segments + accept-comment-above-attributes + accept-comment-above-statement + allow-dbg-in-tests + allow-expect-in-tests + allow-mixed-uninlined-format-args + allow-one-hash-in-raw-strings + allow-print-in-tests + allow-private-module-inception + allow-unwrap-in-tests + allowed-dotfiles + allowed-duplicate-crates + allowed-idents-below-min-chars + allowed-scripts + arithmetic-side-effects-allowed + arithmetic-side-effects-allowed-binary + arithmetic-side-effects-allowed-unary + array-size-threshold + avoid-breaking-exported-api + await-holding-invalid-types + blacklisted-names + cargo-ignore-publish + check-private-items + cognitive-complexity-threshold + cyclomatic-complexity-threshold + disallowed-macros + disallowed-methods + disallowed-names + 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 + excessive-nesting-threshold + future-size-threshold + ignore-interior-mutability + large-error-threshold + literal-representation-threshold + matches-for-let-else + max-fn-params-bools + max-include-file-size + max-struct-bools + max-suggested-slice-pattern-length + max-trait-bounds + min-ident-chars-threshold + missing-docs-in-crate-items + msrv + pass-by-value-size-limit + pub-underscore-fields-behavior + semicolon-inside-block-ignore-singleline + semicolon-outside-block-ignore-multiline + single-char-binding-names-threshold + stack-size-threshold + standard-macro-braces + struct-field-name-threshold + suppress-restriction-lint-in-const + third-party + too-large-for-stack + too-many-arguments-threshold + too-many-lines-threshold + trivial-copy-size-limit + type-complexity-threshold + unnecessary-box-size + unreadable-literal-lint-fractions + upper-case-acronyms-aggressive + vec-box-size-threshold + verbose-bit-mask-threshold + warn-on-all-wildcard-imports + --> $DIR/$DIR/clippy.toml:7:1 + | +LL | allow_mixed_uninlined_format_args = true + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: perhaps you meant: `allow-mixed-uninlined-format-args` + +error: aborting due to 3 previous errors diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index c58795575160b..ac544737099c6 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -11,8 +11,8 @@ use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::token::Star; use syn::{ - parse_macro_input, parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, - TraitItem, Type, + parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, + Signature, TraitItem, Type, }; #[proc_macro_attribute] @@ -95,3 +95,33 @@ pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStrea TokenStream::from(quote!(#item)) } + +#[proc_macro_attribute] +pub fn fake_main(_attr: TokenStream, item: TokenStream) -> TokenStream { + let mut item = parse_macro_input!(item as ItemFn); + let span = item.block.brace_token.span; + + if item.sig.asyncness.is_some() { + item.sig.asyncness = None; + } + + let crate_name = quote! { fake_crate }; + let block = item.block; + item.block = syn::parse_quote_spanned! { + span => + { + #crate_name::block_on(async { + #block + }) + } + }; + + quote! { + mod #crate_name { + pub fn block_on(_fut: F) {} + } + + #item + } + .into() +} diff --git a/tests/ui/blocks_in_conditions.fixed b/tests/ui/blocks_in_conditions.fixed index 2ab441bbd0c69..efef60567a979 100644 --- a/tests/ui/blocks_in_conditions.fixed +++ b/tests/ui/blocks_in_conditions.fixed @@ -85,4 +85,18 @@ fn block_in_match_expr(num: i32) -> i32 { } } +// issue #12162 +macro_rules! timed { + ($name:expr, $body:expr $(,)?) => {{ + let __scope = (); + $body + }}; +} + +fn issue_12162() { + if timed!("check this!", false) { + println!(); + } +} + fn main() {} diff --git a/tests/ui/blocks_in_conditions.rs b/tests/ui/blocks_in_conditions.rs index dd5ae4fb486b7..8bd8a939eb16e 100644 --- a/tests/ui/blocks_in_conditions.rs +++ b/tests/ui/blocks_in_conditions.rs @@ -85,4 +85,18 @@ fn block_in_match_expr(num: i32) -> i32 { } } +// issue #12162 +macro_rules! timed { + ($name:expr, $body:expr $(,)?) => {{ + let __scope = (); + $body + }}; +} + +fn issue_12162() { + if timed!("check this!", false) { + println!(); + } +} + fn main() {} diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs index 44f8b2eabce3b..9af81f6f7cdda 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -9,17 +9,16 @@ fn simple_examples() { // Simple if true { - //~^ ERROR: all if blocks contain the same code at the start println!("Hello World!"); println!("I'm branch nr: 1"); } else { println!("Hello World!"); println!("I'm branch nr: 2"); } + //~^^^^^^^ ERROR: all if blocks contain the same code at the start // Else if if x == 0 { - //~^ ERROR: all if blocks contain the same code at the start let y = 9; println!("The value y was set to: `{}`", y); let _z = y; @@ -38,6 +37,7 @@ fn simple_examples() { println!("Ha, Pascal allows you to start the array where you want") } + //~^^^^^^^^^^^^^^^^^^^ ERROR: all if blocks contain the same code at the start // Return a value let _ = if x == 7 { @@ -60,7 +60,6 @@ fn simple_but_suggestion_is_invalid() { // Can't be automatically moved because used_value_name is getting used again let used_value_name = 19; if x == 10 { - //~^ ERROR: all if blocks contain the same code at the start let used_value_name = "Different type"; println!("Str: {}", used_value_name); let _ = 1; @@ -69,6 +68,7 @@ fn simple_but_suggestion_is_invalid() { println!("Str: {}", used_value_name); let _ = 2; } + //~^^^^^^^^^ ERROR: all if blocks contain the same code at the start let _ = used_value_name; // This can be automatically moved as `can_be_overridden` is not used again @@ -101,11 +101,11 @@ fn check_if_same_than_else_mask() { } if x == 2019 { - //~^ ERROR: this `if` has identical blocks println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); } else { println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); } + //~^^^^^ ERROR: this `if` has identical blocks } #[allow(clippy::vec_init_then_push)] diff --git a/tests/ui/branches_sharing_code/shared_at_top.stderr b/tests/ui/branches_sharing_code/shared_at_top.stderr index 9d4d42fb689e4..317d1577226ca 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -2,7 +2,6 @@ error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:11:5 | LL | / if true { -LL | | LL | | println!("Hello World!"); | |_________________________________^ | @@ -21,7 +20,6 @@ error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:21:5 | LL | / if x == 0 { -LL | | LL | | let y = 9; LL | | println!("The value y was set to: `{}`", y); LL | | let _z = y; @@ -54,7 +52,6 @@ error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:62:5 | LL | / if x == 10 { -LL | | LL | | let used_value_name = "Different type"; LL | | println!("Str: {}", used_value_name); | |_____________________________________________^ @@ -105,13 +102,12 @@ error: this `if` has identical blocks | LL | if x == 2019 { | __________________^ -LL | | LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); LL | | } else { | |_____^ | note: same as this - --> $DIR/shared_at_top.rs:106:12 + --> $DIR/shared_at_top.rs:105:12 | LL | } else { | ____________^ diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.rs b/tests/ui/branches_sharing_code/valid_if_blocks.rs index 2aeacb89c0cb6..b63819d7c3932 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.rs +++ b/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -107,9 +107,9 @@ fn valid_examples() { // Let's test empty blocks if false { - //~^ ERROR: this `if` has identical blocks } else { } + //~^^^ ERROR: this `if` has identical blocks } /// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint @@ -119,7 +119,6 @@ fn trigger_other_lint() { // Same block if x == 0 { - //~^ ERROR: this `if` has identical blocks let u = 19; println!("How are u today?"); let _ = "This is a string"; @@ -128,6 +127,7 @@ fn trigger_other_lint() { println!("How are u today?"); let _ = "This is a string"; } + //~^^^^^^^^^ ERROR: this `if` has identical blocks // Only same expression let _ = if x == 6 { 7 } else { 7 }; @@ -138,28 +138,24 @@ fn trigger_other_lint() { println!("Well I'm the most important block"); "I'm a pretty string" } else if x == 68 { - //~^ ERROR: this `if` has identical blocks println!("I'm a doppelgรคnger"); - // Don't listen to my clone below if y == 90 { "=^.^=" } else { ":D" } } else { - // Don't listen to my clone above println!("I'm a doppelgรคnger"); if y == 90 { "=^.^=" } else { ":D" } }; + //~^^^^^^^^^ ERROR: this `if` has identical blocks if x == 0 { println!("I'm single"); } else if x == 68 { - //~^ ERROR: this `if` has identical blocks println!("I'm a doppelgรคnger"); - // Don't listen to my clone below } else { - // Don't listen to my clone above println!("I'm a doppelgรคnger"); } + //~^^^^^ ERROR: this `if` has identical blocks } fn main() {} diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/tests/ui/branches_sharing_code/valid_if_blocks.stderr index fcbf12235aa16..0daf2ff6967cd 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.stderr +++ b/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -3,12 +3,11 @@ error: this `if` has identical blocks | LL | if false { | ______________^ -LL | | LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:111:12 + --> $DIR/valid_if_blocks.rs:110:12 | LL | } else { | ____________^ @@ -25,7 +24,6 @@ error: this `if` has identical blocks | LL | if x == 0 { | _______________^ -LL | | LL | | let u = 19; LL | | println!("How are u today?"); LL | | let _ = "This is a string"; @@ -33,7 +31,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:126:12 + --> $DIR/valid_if_blocks.rs:125:12 | LL | } else { | ____________^ @@ -60,20 +58,17 @@ error: this `if` has identical blocks | LL | } else if x == 68 { | _______________________^ -LL | | LL | | println!("I'm a doppelgรคnger"); -LL | | // Don't listen to my clone below LL | | LL | | if y == 90 { "=^.^=" } else { ":D" } LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:146:12 + --> $DIR/valid_if_blocks.rs:144:12 | LL | } else { | ____________^ -LL | | // Don't listen to my clone above LL | | println!("I'm a doppelgรคnger"); LL | | LL | | if y == 90 { "=^.^=" } else { ":D" } @@ -81,22 +76,19 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:155:23 + --> $DIR/valid_if_blocks.rs:153:23 | LL | } else if x == 68 { | _______________________^ -LL | | LL | | println!("I'm a doppelgรคnger"); -LL | | // Don't listen to my clone below LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:159:12 + --> $DIR/valid_if_blocks.rs:155:12 | LL | } else { | ____________^ -LL | | // Don't listen to my clone above LL | | println!("I'm a doppelgรคnger"); LL | | } | |_____^ diff --git a/tests/ui/crashes/ice-9041.stderr b/tests/ui/crashes/ice-9041.stderr index 00b65f00d787c..67589d1a8ab32 100644 --- a/tests/ui/crashes/ice-9041.stderr +++ b/tests/ui/crashes/ice-9041.stderr @@ -2,7 +2,7 @@ error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/ice-9041.rs:5:19 | LL | things.iter().find(|p| is_thing_ready(p)).is_some() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|p| is_thing_ready(&p))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|p| is_thing_ready(&p))` | = note: `-D clippy::search-is-some` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` diff --git a/tests/ui/default_instead_of_iter_empty_no_std.fixed b/tests/ui/default_instead_of_iter_empty_no_std.fixed new file mode 100644 index 0000000000000..7300bd9bd2abd --- /dev/null +++ b/tests/ui/default_instead_of_iter_empty_no_std.fixed @@ -0,0 +1,28 @@ +#![warn(clippy::default_instead_of_iter_empty)] +#![allow(dead_code)] +#![feature(lang_items)] +#![no_std] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +#[derive(Default)] +struct Iter { + iter: core::iter::Empty, +} + +fn main() { + // Do lint. + let _ = core::iter::empty::(); + let _foo: core::iter::Empty = core::iter::empty(); + + // Do not lint. + let _ = Iter::default(); +} diff --git a/tests/ui/default_instead_of_iter_empty_no_std.rs b/tests/ui/default_instead_of_iter_empty_no_std.rs new file mode 100644 index 0000000000000..0bc5c7169d129 --- /dev/null +++ b/tests/ui/default_instead_of_iter_empty_no_std.rs @@ -0,0 +1,28 @@ +#![warn(clippy::default_instead_of_iter_empty)] +#![allow(dead_code)] +#![feature(lang_items)] +#![no_std] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +#[derive(Default)] +struct Iter { + iter: core::iter::Empty, +} + +fn main() { + // Do lint. + let _ = core::iter::Empty::::default(); + let _foo: core::iter::Empty = core::iter::Empty::default(); + + // Do not lint. + let _ = Iter::default(); +} diff --git a/tests/ui/default_instead_of_iter_empty_no_std.stderr b/tests/ui/default_instead_of_iter_empty_no_std.stderr new file mode 100644 index 0000000000000..747a31ecbf366 --- /dev/null +++ b/tests/ui/default_instead_of_iter_empty_no_std.stderr @@ -0,0 +1,17 @@ +error: `core::iter::empty()` is the more idiomatic way + --> $DIR/default_instead_of_iter_empty_no_std.rs:23:13 + | +LL | let _ = core::iter::Empty::::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::iter::empty::()` + | + = note: `-D clippy::default-instead-of-iter-empty` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::default_instead_of_iter_empty)]` + +error: `core::iter::empty()` is the more idiomatic way + --> $DIR/default_instead_of_iter_empty_no_std.rs:24:42 + | +LL | let _foo: core::iter::Empty = core::iter::Empty::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::iter::empty()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index c364c6830578f..e7038082c089c 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -216,4 +216,44 @@ mod type_already_inferred { } } +mod issue12159 { + #![allow(non_upper_case_globals, clippy::exhaustive_structs)] + pub struct Foo; + + static F: i32 = 1; + impl Foo { + const LIFE_u8: u8 = 42; + const LIFE_i8: i8 = 42; + const LIFE_u16: u16 = 42; + const LIFE_i16: i16 = 42; + const LIFE_u32: u32 = 42; + const LIFE_i32: i32 = 42; + const LIFE_u64: u64 = 42; + const LIFE_i64: i64 = 42; + const LIFE_u128: u128 = 42; + const LIFE_i128: i128 = 42; + const LIFE_usize: usize = 42; + const LIFE_isize: isize = 42; + const LIFE_f32: f32 = 42.; + const LIFE_f64: f64 = 42.; + + const fn consts() { + const LIFE_u8: u8 = 42; + const LIFE_i8: i8 = 42; + const LIFE_u16: u16 = 42; + const LIFE_i16: i16 = 42; + const LIFE_u32: u32 = 42; + const LIFE_i32: i32 = 42; + const LIFE_u64: u64 = 42; + const LIFE_i64: i64 = 42; + const LIFE_u128: u128 = 42; + const LIFE_i128: i128 = 42; + const LIFE_usize: usize = 42; + const LIFE_isize: isize = 42; + const LIFE_f32: f32 = 42.; + const LIFE_f64: f64 = 42.; + } + } +} + fn main() {} diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index ffa7b961d1ced..d8eeda7049194 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -216,4 +216,44 @@ mod type_already_inferred { } } +mod issue12159 { + #![allow(non_upper_case_globals, clippy::exhaustive_structs)] + pub struct Foo; + + static F: i32 = 1; + impl Foo { + const LIFE_u8: u8 = 42; + const LIFE_i8: i8 = 42; + const LIFE_u16: u16 = 42; + const LIFE_i16: i16 = 42; + const LIFE_u32: u32 = 42; + const LIFE_i32: i32 = 42; + const LIFE_u64: u64 = 42; + const LIFE_i64: i64 = 42; + const LIFE_u128: u128 = 42; + const LIFE_i128: i128 = 42; + const LIFE_usize: usize = 42; + const LIFE_isize: isize = 42; + const LIFE_f32: f32 = 42.; + const LIFE_f64: f64 = 42.; + + const fn consts() { + const LIFE_u8: u8 = 42; + const LIFE_i8: i8 = 42; + const LIFE_u16: u16 = 42; + const LIFE_i16: i16 = 42; + const LIFE_u32: u32 = 42; + const LIFE_i32: i32 = 42; + const LIFE_u64: u64 = 42; + const LIFE_i64: i64 = 42; + const LIFE_u128: u128 = 42; + const LIFE_i128: i128 = 42; + const LIFE_usize: usize = 42; + const LIFE_isize: isize = 42; + const LIFE_f32: f32 = 42.; + const LIFE_f64: f64 = 42.; + } + } +} + fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed index a7f5d3ec7cd08..aa7a95405e02f 100644 --- a/tests/ui/derive_partial_eq_without_eq.fixed +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -121,4 +121,36 @@ pub fn _from_mod() -> _hidden::InPubFn { #[derive(PartialEq)] struct InternalTy; +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub struct MissingEqNonExhaustive { + foo: u32, + bar: String, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +pub struct MissingEqNonExhaustive1 { + foo: u32, + #[non_exhaustive] + bar: String, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub enum MissingEqNonExhaustive2 { + Foo, + Bar, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +pub enum MissingEqNonExhaustive3 { + Foo, + #[non_exhaustive] + Bar, +} + fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs index 476d2aee23a88..90ac7a7989e00 100644 --- a/tests/ui/derive_partial_eq_without_eq.rs +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -121,4 +121,36 @@ pub fn _from_mod() -> _hidden::InPubFn { #[derive(PartialEq)] struct InternalTy; +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub struct MissingEqNonExhaustive { + foo: u32, + bar: String, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +pub struct MissingEqNonExhaustive1 { + foo: u32, + #[non_exhaustive] + bar: String, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub enum MissingEqNonExhaustive2 { + Foo, + Bar, +} + +// This is a `non_exhaustive` type so should not warn. +#[derive(Debug, PartialEq)] +pub enum MissingEqNonExhaustive3 { + Foo, + #[non_exhaustive] + Bar, +} + fn main() {} diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index bff46e55722ba..16f7bafd44b78 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -64,7 +64,7 @@ fn test_units() { /// NaN NaNs /// OAuth GraphQL /// OCaml -/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS +/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry /// WebGL WebGL2 WebGPU /// TensorFlow /// TrueType diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 4e162a97dee9b..4058b2bad74f7 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -64,7 +64,7 @@ fn test_units() { /// NaN NaNs /// OAuth GraphQL /// OCaml -/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS +/// OpenDNS OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenTelemetry /// WebGL WebGL2 WebGPU /// TensorFlow /// TrueType diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index 18d285693e603..4a68505ee0b1c 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -89,4 +89,12 @@ fn msrv_1_41() { } } +fn issue_12138() { + struct Hello; + + impl From for () { + fn from(val: Hello) {} + } +} + fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 779ef709e9284..bf3ed0c2b6422 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -89,4 +89,12 @@ fn msrv_1_41() { } } +fn issue_12138() { + struct Hello; + + impl Into<()> for Hello { + fn into(self) {} + } +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 784843ce5fd55..f1370ed844fa9 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -86,5 +86,19 @@ LL ~ fn from(val: Vec) -> Self { LL ~ FromOverInto(val) | -error: aborting due to 6 previous errors +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:95:5 + | +LL | impl Into<()> for Hello { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implementation with `From` + | +LL ~ impl From for () { +LL ~ fn from(val: Hello) {} + | + +error: aborting due to 7 previous errors diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index e84b20e9fef07..d53e1383d845c 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -21,7 +21,6 @@ fn foo() -> bool { fn if_same_then_else() { if true { - //~^ ERROR: this `if` has identical blocks Foo { bar: 42 }; 0..10; ..; @@ -38,6 +37,7 @@ fn if_same_then_else() { 0..=10; foo(); } + //~^^^^^^^^^^^^^^^^^ ERROR: this `if` has identical blocks if true { Foo { bar: 42 }; @@ -64,19 +64,11 @@ fn if_same_then_else() { foo(); } - let _ = if true { - //~^ ERROR: this `if` has identical blocks - 0.0 - } else { - 0.0 - }; + let _ = if true { 0.0 } else { 0.0 }; + //~^ ERROR: this `if` has identical blocks - let _ = if true { - //~^ ERROR: this `if` has identical blocks - -0.0 - } else { - -0.0 - }; + let _ = if true { -0.0 } else { -0.0 }; + //~^ ERROR: this `if` has identical blocks let _ = if true { 0.0 } else { -0.0 }; @@ -87,15 +79,10 @@ fn if_same_then_else() { foo(); } - let _ = if true { - //~^ ERROR: this `if` has identical blocks - 42 - } else { - 42 - }; + let _ = if true { 42 } else { 42 }; + //~^ ERROR: this `if` has identical blocks if true { - //~^ ERROR: this `if` has identical blocks let bar = if true { 42 } else { 43 }; while foo() { @@ -110,6 +97,7 @@ fn if_same_then_else() { } bar + 1; } + //~^^^^^^^^^^^^^^^ ERROR: this `if` has identical blocks if true { let _ = match 42 { diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index fb33e94e6c3de..281f30f88b46d 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -3,16 +3,16 @@ error: this `if` has identical blocks | LL | if true { | _____________^ -LL | | LL | | Foo { bar: 42 }; LL | | 0..10; +LL | | ..; ... | LL | | foo(); LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:32:12 + --> $DIR/if_same_then_else.rs:31:12 | LL | } else { | ____________^ @@ -29,75 +29,54 @@ LL | | } error: this `if` has identical blocks --> $DIR/if_same_then_else.rs:67:21 | -LL | let _ = if true { - | _____________________^ -LL | | -LL | | 0.0 -LL | | } else { - | |_____^ +LL | let _ = if true { 0.0 } else { 0.0 }; + | ^^^^^^^ | note: same as this - --> $DIR/if_same_then_else.rs:70:12 + --> $DIR/if_same_then_else.rs:67:34 | -LL | } else { - | ____________^ -LL | | 0.0 -LL | | }; - | |_____^ +LL | let _ = if true { 0.0 } else { 0.0 }; + | ^^^^^^^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:74:21 + --> $DIR/if_same_then_else.rs:70:21 | -LL | let _ = if true { - | _____________________^ -LL | | -LL | | -0.0 -LL | | } else { - | |_____^ +LL | let _ = if true { -0.0 } else { -0.0 }; + | ^^^^^^^^ | note: same as this - --> $DIR/if_same_then_else.rs:77:12 + --> $DIR/if_same_then_else.rs:70:35 | -LL | } else { - | ____________^ -LL | | -0.0 -LL | | }; - | |_____^ +LL | let _ = if true { -0.0 } else { -0.0 }; + | ^^^^^^^^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:90:21 + --> $DIR/if_same_then_else.rs:82:21 | -LL | let _ = if true { - | _____________________^ -LL | | -LL | | 42 -LL | | } else { - | |_____^ +LL | let _ = if true { 42 } else { 42 }; + | ^^^^^^ | note: same as this - --> $DIR/if_same_then_else.rs:93:12 + --> $DIR/if_same_then_else.rs:82:33 | -LL | } else { - | ____________^ -LL | | 42 -LL | | }; - | |_____^ +LL | let _ = if true { 42 } else { 42 }; + | ^^^^^^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:97:13 + --> $DIR/if_same_then_else.rs:85:13 | LL | if true { | _____________^ -LL | | LL | | let bar = if true { 42 } else { 43 }; LL | | +LL | | while foo() { ... | LL | | bar + 1; LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:105:12 + --> $DIR/if_same_then_else.rs:92:12 | LL | } else { | ____________^ @@ -110,7 +89,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:250:14 + --> $DIR/if_same_then_else.rs:238:14 | LL | if x { | ______________^ @@ -119,7 +98,7 @@ LL | | } else { | |_________^ | note: same as this - --> $DIR/if_same_then_else.rs:252:16 + --> $DIR/if_same_then_else.rs:240:16 | LL | } else { | ________________^ diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index 0b171f21d0cc4..e23c77b082744 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -13,7 +13,6 @@ fn if_same_then_else2() -> Result<&'static str, ()> { if true { - //~^ ERROR: this `if` has identical blocks for _ in &[42] { let foo: &Option<_> = &Some::(42); if foo.is_some() { @@ -32,20 +31,21 @@ fn if_same_then_else2() -> Result<&'static str, ()> { } } } + //~^^^^^^^^^^^^^^^^^^^ ERROR: this `if` has identical blocks if true { - //~^ ERROR: this `if` has identical blocks if let Some(a) = Some(42) {} } else { if let Some(a) = Some(42) {} } + //~^^^^^ ERROR: this `if` has identical blocks if true { - //~^ ERROR: this `if` has identical blocks if let (1, .., 3) = (1, 2, 3) {} } else { if let (1, .., 3) = (1, 2, 3) {} } + //~^^^^^ ERROR: this `if` has identical blocks if true { if let (1, .., 3) = (1, 2, 3) {} @@ -90,19 +90,15 @@ fn if_same_then_else2() -> Result<&'static str, ()> { } // Same NaNs - let _ = if true { - //~^ ERROR: this `if` has identical blocks - f32::NAN - } else { - f32::NAN - }; + let _ = if true { f32::NAN } else { f32::NAN }; + //~^ ERROR: this `if` has identical blocks if true { - //~^ ERROR: this `if` has identical blocks Ok("foo")?; } else { Ok("foo")?; } + //~^^^^^ ERROR: this `if` has identical blocks if true { let foo = ""; @@ -122,13 +118,13 @@ fn if_same_then_else2() -> Result<&'static str, ()> { let foo = "bar"; return Ok(&foo[0..]); } else if true { - //~^ ERROR: this `if` has identical blocks let foo = ""; return Ok(&foo[0..]); } else { let foo = ""; return Ok(&foo[0..]); } + //~^^^^^^^ ERROR: this `if` has identical blocks // False positive `if_same_then_else`: `let (x, y)` vs. `let (y, x)`; see issue #3559. if true { diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index fe68ef2711b59..4e7a7c87dc54b 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -3,16 +3,16 @@ error: this `if` has identical blocks | LL | if true { | _____________^ -LL | | LL | | for _ in &[42] { LL | | let foo: &Option<_> = &Some::(42); +LL | | if foo.is_some() { ... | LL | | } LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:25:12 + --> $DIR/if_same_then_else2.rs:24:12 | LL | } else { | ____________^ @@ -31,13 +31,12 @@ error: this `if` has identical blocks | LL | if true { | _____________^ -LL | | LL | | if let Some(a) = Some(42) {} LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:39:12 + --> $DIR/if_same_then_else2.rs:38:12 | LL | } else { | ____________^ @@ -50,13 +49,12 @@ error: this `if` has identical blocks | LL | if true { | _____________^ -LL | | LL | | if let (1, .., 3) = (1, 2, 3) {} LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:46:12 + --> $DIR/if_same_then_else2.rs:45:12 | LL | } else { | ____________^ @@ -67,34 +65,26 @@ LL | | } error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:93:21 | -LL | let _ = if true { - | _____________________^ -LL | | -LL | | f32::NAN -LL | | } else { - | |_____^ +LL | let _ = if true { f32::NAN } else { f32::NAN }; + | ^^^^^^^^^^^^ | note: same as this - --> $DIR/if_same_then_else2.rs:96:12 + --> $DIR/if_same_then_else2.rs:93:39 | -LL | } else { - | ____________^ -LL | | f32::NAN -LL | | }; - | |_____^ +LL | let _ = if true { f32::NAN } else { f32::NAN }; + | ^^^^^^^^^^^^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:100:13 + --> $DIR/if_same_then_else2.rs:96:13 | LL | if true { | _____________^ -LL | | LL | | Ok("foo")?; LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:103:12 + --> $DIR/if_same_then_else2.rs:98:12 | LL | } else { | ____________^ @@ -103,18 +93,17 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:124:20 + --> $DIR/if_same_then_else2.rs:120:20 | LL | } else if true { | ____________________^ -LL | | LL | | let foo = ""; LL | | return Ok(&foo[0..]); LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:128:12 + --> $DIR/if_same_then_else2.rs:123:12 | LL | } else { | ____________^ diff --git a/tests/ui/ineffective_open_options.fixed b/tests/ui/ineffective_open_options.fixed index 3af8f3c5aaa44..88489dc46bbb4 100644 --- a/tests/ui/ineffective_open_options.fixed +++ b/tests/ui/ineffective_open_options.fixed @@ -26,16 +26,23 @@ fn main() { .unwrap(); let file = OpenOptions::new() .create(true) + .truncate(true) .write(true) .append(false) .open("dump.json") .unwrap(); let file = OpenOptions::new() .create(true) + .truncate(true) .write(false) .append(false) .open("dump.json") .unwrap(); let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap(); - let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap(); + let file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open("dump.json") + .unwrap(); } diff --git a/tests/ui/ineffective_open_options.rs b/tests/ui/ineffective_open_options.rs index 4eaf6293c408e..db8bca3e2acdb 100644 --- a/tests/ui/ineffective_open_options.rs +++ b/tests/ui/ineffective_open_options.rs @@ -26,16 +26,23 @@ fn main() { .unwrap(); let file = OpenOptions::new() .create(true) + .truncate(true) .write(true) .append(false) .open("dump.json") .unwrap(); let file = OpenOptions::new() .create(true) + .truncate(true) .write(false) .append(false) .open("dump.json") .unwrap(); let file = OpenOptions::new().create(true).append(true).open("dump.json").unwrap(); - let file = OpenOptions::new().create(true).write(true).open("dump.json").unwrap(); + let file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open("dump.json") + .unwrap(); } diff --git a/tests/ui/join_absolute_paths.stderr b/tests/ui/join_absolute_paths.stderr index 0c2f89d9978b3..ab4d189ca3a22 100644 --- a/tests/ui/join_absolute_paths.stderr +++ b/tests/ui/join_absolute_paths.stderr @@ -11,7 +11,7 @@ help: if this is unintentional, try removing the starting separator | LL | path.join("sh"); | ~~~~ -help: if this is intentional, try using `Path::new` instead +help: if this is intentional, consider using `Path::new` | LL | PathBuf::from("/sh"); | ~~~~~~~~~~~~~~~~~~~~ @@ -27,7 +27,7 @@ help: if this is unintentional, try removing the starting separator | LL | path.join("\user"); | ~~~~~~~ -help: if this is intentional, try using `Path::new` instead +help: if this is intentional, consider using `Path::new` | LL | PathBuf::from("\\user"); | ~~~~~~~~~~~~~~~~~~~~~~~ @@ -43,7 +43,7 @@ help: if this is unintentional, try removing the starting separator | LL | path.join("sh"); | ~~~~ -help: if this is intentional, try using `Path::new` instead +help: if this is intentional, consider using `Path::new` | LL | PathBuf::from("/sh"); | ~~~~~~~~~~~~~~~~~~~~ @@ -59,7 +59,7 @@ help: if this is unintentional, try removing the starting separator | LL | path.join(r#"sh"#); | ~~~~~~~ -help: if this is intentional, try using `Path::new` instead +help: if this is intentional, consider using `Path::new` | LL | PathBuf::from(r#"/sh"#); | ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/ui/manual_ok_or.stderr b/tests/ui/manual_ok_or.stderr index b277d22e59b63..89df6cdbedbcc 100644 --- a/tests/ui/manual_ok_or.stderr +++ b/tests/ui/manual_ok_or.stderr @@ -17,7 +17,7 @@ error: called `map_or(Err(_), Ok)` on an `Option` value --> $DIR/manual_ok_or.rs:14:5 | LL | foo.map_or(Err("error"), Ok); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok_or` instead: `foo.ok_or("error")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok_or`: `foo.ok_or("error")` | = note: `-D clippy::option-map-or-err-ok` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_map_or_err_ok)]` diff --git a/tests/ui/manual_saturating_arithmetic.stderr b/tests/ui/manual_saturating_arithmetic.stderr index dc36a5ee7c692..3ce108b1ca81e 100644 --- a/tests/ui/manual_saturating_arithmetic.stderr +++ b/tests/ui/manual_saturating_arithmetic.stderr @@ -2,7 +2,7 @@ error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:6:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` | = note: `-D clippy::manual-saturating-arithmetic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]` @@ -11,13 +11,13 @@ error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:7:13 | LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:8:13 | LL | let _ = 1u8.checked_add(1).unwrap_or(255); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u8.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:9:13 @@ -26,49 +26,49 @@ LL | let _ = 1u128 | _____________^ LL | | .checked_add(1) LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); - | |_______________________________________________________________________^ help: try using `saturating_add`: `1u128.saturating_add(1)` + | |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:14:13 | LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_mul`: `1u32.saturating_mul(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:16:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:17:13 | LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:18:13 | LL | let _ = 1u8.checked_sub(1).unwrap_or(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u8.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:22:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:23:13 | LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:24:13 | LL | let _ = 1i8.checked_add(1).unwrap_or(127); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:25:13 @@ -77,25 +77,25 @@ LL | let _ = 1i128 | _____________^ LL | | .checked_add(1) LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); - | |_______________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(1)` + | |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:28:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:29:13 | LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:30:13 | LL | let _ = 1i8.checked_add(-1).unwrap_or(-128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:31:13 @@ -104,25 +104,25 @@ LL | let _ = 1i128 | _____________^ LL | | .checked_add(-1) LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); - | |________________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(-1)` + | |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:38:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:39:13 | LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:40:13 | LL | let _ = 1i8.checked_sub(1).unwrap_or(-128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:41:13 @@ -131,25 +131,25 @@ LL | let _ = 1i128 | _____________^ LL | | .checked_sub(1) LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); - | |________________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(1)` + | |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:44:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:45:13 | LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:46:13 | LL | let _ = 1i8.checked_sub(-1).unwrap_or(127); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(-1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)` error: manual saturating arithmetic --> $DIR/manual_saturating_arithmetic.rs:47:13 @@ -158,7 +158,7 @@ LL | let _ = 1i128 | _____________^ LL | | .checked_sub(-1) LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); - | |_______________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(-1)` + | |_______________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(-1)` error: aborting due to 24 previous errors diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 08b155a1aeaf8..395eea69294dd 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -69,15 +69,48 @@ fn main() { //~^ ERROR: you are explicitly cloning with `.map()` let y = x.cloned(); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method let y = x.cloned(); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method + + let x: Option = Some(0); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.copied(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + let y = x.copied(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + + // Should not suggest `copied` or `cloned` here since `T` is not a reference. + let x: Option = Some(0); + let y = x.map(|x| u32::clone(&x)); + let y = x.map(|x| Clone::clone(&x)); // Testing with `Result` now. let x: Result = Ok(String::new()); let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. let y = x.cloned(); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method let y = x.cloned(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method + + let x: Result = Ok(0); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.copied(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + let y = x.copied(); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + + // Should not suggest `copied` or `cloned` here since `T` is not a reference. + let x: Result = Ok(0); + let y = x.map(|x| u32::clone(&x)); + let y = x.map(|x| Clone::clone(&x)); // We ensure that no warning is emitted here because `useless_asref` is taking over. let x = Some(String::new()); diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 901d9b278b47f..82a103edf5a9c 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -69,15 +69,48 @@ fn main() { //~^ ERROR: you are explicitly cloning with `.map()` let y = x.map(Clone::clone); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method let y = x.map(String::clone); //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method + + let x: Option = Some(0); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.map(|x| u32::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + let y = x.map(|x| Clone::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + + // Should not suggest `copied` or `cloned` here since `T` is not a reference. + let x: Option = Some(0); + let y = x.map(|x| u32::clone(&x)); + let y = x.map(|x| Clone::clone(&x)); // Testing with `Result` now. let x: Result = Ok(String::new()); let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. let y = x.map(|x| String::clone(x)); //~^ ERROR: you are explicitly cloning with `.map()` - let y = x.map(|x| String::clone(x)); + //~| HELP: consider calling the dedicated `cloned` method + let y = x.map(|x| Clone::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `cloned` method + + let x: Result = Ok(0); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.map(|x| u32::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + let y = x.map(|x| Clone::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + //~| HELP: consider calling the dedicated `copied` method + + // Should not suggest `copied` or `cloned` here since `T` is not a reference. + let x: Result = Ok(0); + let y = x.map(|x| u32::clone(&x)); + let y = x.map(|x| Clone::clone(&x)); // We ensure that no warning is emitted here because `useless_asref` is taking over. let x = Some(String::new()); diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 9d7e9317b58ce..2c86a67fab8d6 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -50,22 +50,46 @@ LL | let y = x.map(Clone::clone); | ^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> $DIR/map_clone.rs:72:13 + --> $DIR/map_clone.rs:73:13 | LL | let y = x.map(String::clone); | ^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> $DIR/map_clone.rs:78:13 + --> $DIR/map_clone.rs:79:13 | -LL | let y = x.map(|x| String::clone(x)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` +LL | let y = x.map(|x| u32::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` error: you are explicitly cloning with `.map()` - --> $DIR/map_clone.rs:80:13 + --> $DIR/map_clone.rs:82:13 + | +LL | let y = x.map(|x| Clone::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:94:13 | LL | let y = x.map(|x| String::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` -error: aborting due to 11 previous errors +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:97:13 + | +LL | let y = x.map(|x| Clone::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:103:13 + | +LL | let y = x.map(|x| u32::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:106:13 + | +LL | let y = x.map(|x| Clone::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` + +error: aborting due to 15 previous errors diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index 525a355f40355..3428ff4590602 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -13,7 +13,6 @@ fn foo() -> bool { fn match_same_arms() { let _ = match 42 { 42 => { - //~^ ERROR: this match arm has an identical body to the `_` wildcard arm foo(); let mut a = 42 + [23].len() as i32; if true { @@ -32,6 +31,7 @@ fn match_same_arms() { a }, }; + //~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm let _ = match 42 { 42 => foo(), @@ -146,13 +146,13 @@ fn match_same_arms() { empty!(0); }, 1 => { - //~^ ERROR: this match arm has an identical body to another arm empty!(0); }, x => { empty!(x); }, } + //~^^^^^^^ ERROR: this match arm has an identical body to another arm match_expr_like_matches_macro_priority(); } diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 40b20c7e16d28..512ca7413a795 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -2,9 +2,9 @@ error: this match arm has an identical body to the `_` wildcard arm --> $DIR/match_same_arms2.rs:15:9 | LL | / 42 => { -LL | | LL | | foo(); LL | | let mut a = 42 + [23].len() as i32; +LL | | if true { ... | LL | | a LL | | }, @@ -12,7 +12,7 @@ LL | | }, | = help: or try changing either arm body note: `_` wildcard arm here - --> $DIR/match_same_arms2.rs:25:9 + --> $DIR/match_same_arms2.rs:24:9 | LL | / _ => { LL | | foo(); @@ -122,7 +122,6 @@ LL | 1 => { | ^ help: try merging the arm patterns: `1 | 0` | _________| | | -LL | | LL | | empty!(0); LL | | }, | |_________^ diff --git a/tests/ui/mem_replace_no_std.fixed b/tests/ui/mem_replace_no_std.fixed new file mode 100644 index 0000000000000..c970f2ba2814b --- /dev/null +++ b/tests/ui/mem_replace_no_std.fixed @@ -0,0 +1,82 @@ +#![allow(unused)] +#![warn( + clippy::all, + clippy::style, + clippy::mem_replace_option_with_none, + clippy::mem_replace_with_default +)] +#![feature(lang_items)] +#![no_std] + +use core::mem; +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn replace_option_with_none() { + let mut an_option = Some(1); + let _ = an_option.take(); + let an_option = &mut Some(1); + let _ = an_option.take(); +} + +fn replace_with_default() { + let mut refstr = "hello"; + let _ = core::mem::take(&mut refstr); + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = core::mem::take(&mut slice); +} + +// lint is disabled for primitives because in this case `take` +// has no clear benefit over `replace` and sometimes is harder to read +fn dont_lint_primitive() { + let mut pbool = true; + let _ = mem::replace(&mut pbool, false); + + let mut pint = 5; + let _ = mem::replace(&mut pint, 0); +} + +fn main() { + replace_option_with_none(); + replace_with_default(); + dont_lint_primitive(); +} + +fn issue9824() { + struct Foo<'a>(Option<&'a str>); + impl<'a> core::ops::Deref for Foo<'a> { + type Target = Option<&'a str>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<'a> core::ops::DerefMut for Foo<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + struct Bar { + opt: Option, + val: u8, + } + + let mut f = Foo(Some("foo")); + let mut b = Bar { opt: Some(1), val: 12 }; + + // replace option with none + let _ = f.0.take(); + let _ = (*f).take(); + let _ = b.opt.take(); + // replace with default + let _ = mem::replace(&mut b.val, u8::default()); +} diff --git a/tests/ui/mem_replace_no_std.rs b/tests/ui/mem_replace_no_std.rs new file mode 100644 index 0000000000000..673d5c7b4f45a --- /dev/null +++ b/tests/ui/mem_replace_no_std.rs @@ -0,0 +1,82 @@ +#![allow(unused)] +#![warn( + clippy::all, + clippy::style, + clippy::mem_replace_option_with_none, + clippy::mem_replace_with_default +)] +#![feature(lang_items)] +#![no_std] + +use core::mem; +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn replace_option_with_none() { + let mut an_option = Some(1); + let _ = mem::replace(&mut an_option, None); + let an_option = &mut Some(1); + let _ = mem::replace(an_option, None); +} + +fn replace_with_default() { + let mut refstr = "hello"; + let _ = mem::replace(&mut refstr, ""); + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = mem::replace(&mut slice, &[]); +} + +// lint is disabled for primitives because in this case `take` +// has no clear benefit over `replace` and sometimes is harder to read +fn dont_lint_primitive() { + let mut pbool = true; + let _ = mem::replace(&mut pbool, false); + + let mut pint = 5; + let _ = mem::replace(&mut pint, 0); +} + +fn main() { + replace_option_with_none(); + replace_with_default(); + dont_lint_primitive(); +} + +fn issue9824() { + struct Foo<'a>(Option<&'a str>); + impl<'a> core::ops::Deref for Foo<'a> { + type Target = Option<&'a str>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl<'a> core::ops::DerefMut for Foo<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + struct Bar { + opt: Option, + val: u8, + } + + let mut f = Foo(Some("foo")); + let mut b = Bar { opt: Some(1), val: 12 }; + + // replace option with none + let _ = mem::replace(&mut f.0, None); + let _ = mem::replace(&mut *f, None); + let _ = mem::replace(&mut b.opt, None); + // replace with default + let _ = mem::replace(&mut b.val, u8::default()); +} diff --git a/tests/ui/mem_replace_no_std.stderr b/tests/ui/mem_replace_no_std.stderr new file mode 100644 index 0000000000000..744fb5a15876e --- /dev/null +++ b/tests/ui/mem_replace_no_std.stderr @@ -0,0 +1,50 @@ +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:24:13 + | +LL | let _ = mem::replace(&mut an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` + | + = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_none)]` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:26:13 + | +LL | let _ = mem::replace(an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` + +error: replacing a value of type `T` with `T::default()` is better expressed using `core::mem::take` + --> $DIR/mem_replace_no_std.rs:31:13 + | +LL | let _ = mem::replace(&mut refstr, ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::mem::take(&mut refstr)` + | + = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mem_replace_with_default)]` + +error: replacing a value of type `T` with `T::default()` is better expressed using `core::mem::take` + --> $DIR/mem_replace_no_std.rs:34:13 + | +LL | let _ = mem::replace(&mut slice, &[]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::mem::take(&mut slice)` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:77:13 + | +LL | let _ = mem::replace(&mut f.0, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:78:13 + | +LL | let _ = mem::replace(&mut *f, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace_no_std.rs:79:13 + | +LL | let _ = mem::replace(&mut b.opt, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index 777b1e52c2da6..dabeda72f0c8c 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -181,6 +181,8 @@ fn main() { //~^ ERROR: binding to `_` prefixed variable with no side-effect let _cat = [2, 4, 6, 8][2]; //~^ ERROR: binding to `_` prefixed variable with no side-effect + let _issue_12166 = 42; + let underscore_variable_above_can_be_used_dont_lint = _issue_12166; #[allow(clippy::no_effect)] 0; diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index f5ba234b4cbf6..8140ba1feee9c 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -152,31 +152,31 @@ LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:175:5 + --> $DIR/no_effect.rs:175:9 | LL | let _unused = 1; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^ | = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::no_effect_underscore_binding)]` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:178:5 + --> $DIR/no_effect.rs:178:9 | LL | let _penguin = || println!("Some helpful closure"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:180:5 + --> $DIR/no_effect.rs:180:9 | LL | let _duck = Struct { field: 0 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:182:5 + --> $DIR/no_effect.rs:182:9 | LL | let _cat = [2, 4, 6, 8][2]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ error: aborting due to 29 previous errors diff --git a/tests/ui/open_options.rs b/tests/ui/open_options.rs index 0cdc5bf2bb592..4acb3780df43e 100644 --- a/tests/ui/open_options.rs +++ b/tests/ui/open_options.rs @@ -1,7 +1,18 @@ +#![allow(unused_must_use)] +#![warn(clippy::nonsensical_open_options)] + use std::fs::OpenOptions; -#[allow(unused_must_use)] -#[warn(clippy::nonsensical_open_options)] +trait OpenOptionsExt { + fn truncate_write(&mut self, opt: bool) -> &mut Self; +} + +impl OpenOptionsExt for OpenOptions { + fn truncate_write(&mut self, opt: bool) -> &mut Self { + self.truncate(opt).write(opt) + } +} + fn main() { OpenOptions::new().read(true).truncate(true).open("foo.txt"); //~^ ERROR: file opened with `truncate` and `read` @@ -11,12 +22,31 @@ fn main() { OpenOptions::new().read(true).read(false).open("foo.txt"); //~^ ERROR: the method `read` is called more than once - OpenOptions::new().create(true).create(false).open("foo.txt"); - //~^ ERROR: the method `create` is called more than once + OpenOptions::new() + .create(true) + .truncate(true) // Ensure we don't trigger suspicious open options by having create without truncate + .create(false) + //~^ ERROR: the method `create` is called more than once + .open("foo.txt"); OpenOptions::new().write(true).write(false).open("foo.txt"); //~^ ERROR: the method `write` is called more than once OpenOptions::new().append(true).append(false).open("foo.txt"); //~^ ERROR: the method `append` is called more than once OpenOptions::new().truncate(true).truncate(false).open("foo.txt"); //~^ ERROR: the method `truncate` is called more than once + + std::fs::File::options().read(true).read(false).open("foo.txt"); + //~^ ERROR: the method `read` is called more than once + + let mut options = std::fs::OpenOptions::new(); + options.read(true); + options.read(false); + // Possible future improvement: recognize open options method call chains across statements. + options.open("foo.txt"); + + let mut options = std::fs::OpenOptions::new(); + options.truncate(true); + options.create(true).open("foo.txt"); + + OpenOptions::new().create(true).truncate_write(true).open("foo.txt"); } diff --git a/tests/ui/open_options.stderr b/tests/ui/open_options.stderr index 7ac826f52fa9d..4f84d1d09f984 100644 --- a/tests/ui/open_options.stderr +++ b/tests/ui/open_options.stderr @@ -1,5 +1,5 @@ error: file opened with `truncate` and `read` - --> $DIR/open_options.rs:6:5 + --> $DIR/open_options.rs:17:5 | LL | OpenOptions::new().read(true).truncate(true).open("foo.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,40 +8,46 @@ LL | OpenOptions::new().read(true).truncate(true).open("foo.txt"); = help: to override `-D warnings` add `#[allow(clippy::nonsensical_open_options)]` error: file opened with `append` and `truncate` - --> $DIR/open_options.rs:9:5 + --> $DIR/open_options.rs:20:5 | LL | OpenOptions::new().append(true).truncate(true).open("foo.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the method `read` is called more than once - --> $DIR/open_options.rs:12:5 + --> $DIR/open_options.rs:23:35 | LL | OpenOptions::new().read(true).read(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ error: the method `create` is called more than once - --> $DIR/open_options.rs:14:5 + --> $DIR/open_options.rs:28:10 | -LL | OpenOptions::new().create(true).create(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | .create(false) + | ^^^^^^^^^^^^^ error: the method `write` is called more than once - --> $DIR/open_options.rs:16:5 + --> $DIR/open_options.rs:31:36 | LL | OpenOptions::new().write(true).write(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ error: the method `append` is called more than once - --> $DIR/open_options.rs:18:5 + --> $DIR/open_options.rs:33:37 | LL | OpenOptions::new().append(true).append(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ error: the method `truncate` is called more than once - --> $DIR/open_options.rs:20:5 + --> $DIR/open_options.rs:35:39 | LL | OpenOptions::new().truncate(true).truncate(false).open("foo.txt"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ + +error: the method `read` is called more than once + --> $DIR/open_options.rs:38:41 + | +LL | std::fs::File::options().read(true).read(false).open("foo.txt"); + | ^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/open_options_fixable.fixed b/tests/ui/open_options_fixable.fixed new file mode 100644 index 0000000000000..90a129a9bdffd --- /dev/null +++ b/tests/ui/open_options_fixable.fixed @@ -0,0 +1,7 @@ +use std::fs::OpenOptions; +#[allow(unused_must_use)] +#[warn(clippy::suspicious_open_options)] +fn main() { + OpenOptions::new().create(true).truncate(true).open("foo.txt"); + //~^ ERROR: file opened with `create`, but `truncate` behavior not defined +} diff --git a/tests/ui/open_options_fixable.rs b/tests/ui/open_options_fixable.rs new file mode 100644 index 0000000000000..3a9e522ba1507 --- /dev/null +++ b/tests/ui/open_options_fixable.rs @@ -0,0 +1,7 @@ +use std::fs::OpenOptions; +#[allow(unused_must_use)] +#[warn(clippy::suspicious_open_options)] +fn main() { + OpenOptions::new().create(true).open("foo.txt"); + //~^ ERROR: file opened with `create`, but `truncate` behavior not defined +} diff --git a/tests/ui/open_options_fixable.stderr b/tests/ui/open_options_fixable.stderr new file mode 100644 index 0000000000000..e327661713bfb --- /dev/null +++ b/tests/ui/open_options_fixable.stderr @@ -0,0 +1,14 @@ +error: file opened with `create`, but `truncate` behavior not defined + --> $DIR/open_options_fixable.rs:5:24 + | +LL | OpenOptions::new().create(true).open("foo.txt"); + | ^^^^^^^^^^^^- help: add: `.truncate(true)` + | + = help: if you intend to overwrite an existing file entirely, call `.truncate(true)` + = help: if you instead know that you may want to keep some parts of the old file, call `.truncate(false)` + = help: alternatively, use `.append(true)` to append to the file instead of overwriting it + = note: `-D clippy::suspicious-open-options` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::suspicious_open_options)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index 9d173e409abcc..036b8c749e417 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -2,7 +2,7 @@ error: called `.as_ref().map(Deref::deref)` on an `Option` value --> $DIR/option_as_ref_deref.rs:11:13 | LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.clone().as_deref()` | = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_as_ref_deref)]` @@ -15,103 +15,103 @@ LL | let _ = opt.clone() LL | | .as_ref().map( LL | | Deref::deref LL | | ) - | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` + | |_________^ help: consider using as_deref: `opt.clone().as_deref()` error: called `.as_mut().map(DerefMut::deref_mut)` on an `Option` value --> $DIR/option_as_ref_deref.rs:20:13 | LL | let _ = opt.as_mut().map(DerefMut::deref_mut); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.as_deref_mut()` error: called `.as_ref().map(String::as_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:22:13 | LL | let _ = opt.as_ref().map(String::as_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_ref().map(|x| x.as_str())` on an `Option` value --> $DIR/option_as_ref_deref.rs:23:13 | LL | let _ = opt.as_ref().map(|x| x.as_str()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_mut().map(String::as_mut_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:24:13 | LL | let _ = opt.as_mut().map(String::as_mut_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.as_deref_mut()` error: called `.as_mut().map(|x| x.as_mut_str())` on an `Option` value --> $DIR/option_as_ref_deref.rs:25:13 | LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.as_deref_mut()` error: called `.as_ref().map(CString::as_c_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:26:13 | LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `Some(CString::new(vec![]).unwrap()).as_deref()` error: called `.as_ref().map(OsString::as_os_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:27:13 | LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `Some(OsString::new()).as_deref()` error: called `.as_ref().map(PathBuf::as_path)` on an `Option` value --> $DIR/option_as_ref_deref.rs:28:13 | LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `Some(PathBuf::new()).as_deref()` error: called `.as_ref().map(Vec::as_slice)` on an `Option` value --> $DIR/option_as_ref_deref.rs:29:13 | LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `Some(Vec::<()>::new()).as_deref()` error: called `.as_mut().map(Vec::as_mut_slice)` on an `Option` value --> $DIR/option_as_ref_deref.rs:30:13 | LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `Some(Vec::<()>::new()).as_deref_mut()` error: called `.as_ref().map(|x| x.deref())` on an `Option` value --> $DIR/option_as_ref_deref.rs:32:13 | LL | let _ = opt.as_ref().map(|x| x.deref()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_mut().map(|x| x.deref_mut())` on an `Option` value --> $DIR/option_as_ref_deref.rs:33:13 | LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.clone().as_deref_mut()` error: called `.as_ref().map(|x| &**x)` on an `Option` value --> $DIR/option_as_ref_deref.rs:40:13 | LL | let _ = opt.as_ref().map(|x| &**x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_mut().map(|x| &mut **x)` on an `Option` value --> $DIR/option_as_ref_deref.rs:41:13 | LL | let _ = opt.as_mut().map(|x| &mut **x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref_mut: `opt.as_deref_mut()` error: called `.as_ref().map(std::ops::Deref::deref)` on an `Option` value --> $DIR/option_as_ref_deref.rs:44:13 | LL | let _ = opt.as_ref().map(std::ops::Deref::deref); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: called `.as_ref().map(String::as_str)` on an `Option` value --> $DIR/option_as_ref_deref.rs:56:13 | LL | let _ = opt.as_ref().map(String::as_str); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using as_deref: `opt.as_deref()` error: aborting due to 18 previous errors diff --git a/tests/ui/option_map_or_err_ok.stderr b/tests/ui/option_map_or_err_ok.stderr index a193e3c4c49d3..381601cb6b3aa 100644 --- a/tests/ui/option_map_or_err_ok.stderr +++ b/tests/ui/option_map_or_err_ok.stderr @@ -2,7 +2,7 @@ error: called `map_or(Err(_), Ok)` on an `Option` value --> $DIR/option_map_or_err_ok.rs:5:13 | LL | let _ = x.map_or(Err("a"), Ok); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok_or` instead: `x.ok_or("a")` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok_or`: `x.ok_or("a")` | = note: `-D clippy::option-map-or-err-ok` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_map_or_err_ok)]` diff --git a/tests/ui/option_map_or_none.stderr b/tests/ui/option_map_or_none.stderr index f2cfc3f9a2815..d58ff83c3da27 100644 --- a/tests/ui/option_map_or_none.stderr +++ b/tests/ui/option_map_or_none.stderr @@ -2,7 +2,7 @@ error: called `map_or(None, ..)` on an `Option` value --> $DIR/option_map_or_none.rs:10:26 | LL | let _: Option = opt.map_or(None, |x| Some(x + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `map` instead: `opt.map(|x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `map`: `opt.map(|x| x + 1)` | = note: `-D clippy::option-map-or-none` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::option_map_or_none)]` @@ -14,13 +14,13 @@ LL | let _: Option = opt.map_or(None, |x| { | __________________________^ LL | | Some(x + 1) LL | | }); - | |_________________________^ help: try using `map` instead: `opt.map(|x| x + 1)` + | |_________________________^ help: consider using `map`: `opt.map(|x| x + 1)` error: called `map_or(None, ..)` on an `Option` value --> $DIR/option_map_or_none.rs:17:26 | LL | let _: Option = opt.map_or(None, bar); - | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(bar)` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `and_then`: `opt.and_then(bar)` error: called `map_or(None, ..)` on an `Option` value --> $DIR/option_map_or_none.rs:18:26 @@ -33,7 +33,7 @@ LL | | Some(offset + height) LL | | }); | |______^ | -help: try using `and_then` instead +help: consider using `and_then` | LL ~ let _: Option = opt.and_then(|x| { LL + let offset = 0; @@ -46,7 +46,7 @@ error: called `map_or(None, Some)` on a `Result` value --> $DIR/option_map_or_none.rs:25:26 | LL | let _: Option = r.map_or(None, Some); - | ^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `r.ok()` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `ok`: `r.ok()` | = note: `-D clippy::result-map-or-into-option` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_map_or_into_option)]` diff --git a/tests/ui/ptr_eq_no_std.fixed b/tests/ui/ptr_eq_no_std.fixed new file mode 100644 index 0000000000000..97c8c394c03d4 --- /dev/null +++ b/tests/ui/ptr_eq_no_std.fixed @@ -0,0 +1,49 @@ +#![warn(clippy::ptr_eq)] +#![no_std] +#![feature(lang_items)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = core::ptr::eq(a, b); + let _ = core::ptr::eq(a, b); + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq_no_std.rs b/tests/ui/ptr_eq_no_std.rs new file mode 100644 index 0000000000000..a7ba9b4d81746 --- /dev/null +++ b/tests/ui/ptr_eq_no_std.rs @@ -0,0 +1,49 @@ +#![warn(clippy::ptr_eq)] +#![no_std] +#![feature(lang_items)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = a as *const _ as usize == b as *const _ as usize; + let _ = a as *const _ == b as *const _; + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq_no_std.stderr b/tests/ui/ptr_eq_no_std.stderr new file mode 100644 index 0000000000000..3e289f5be613e --- /dev/null +++ b/tests/ui/ptr_eq_no_std.stderr @@ -0,0 +1,17 @@ +error: use `core::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq_no_std.rs:31:13 + | +LL | let _ = a as *const _ as usize == b as *const _ as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a, b)` + | + = note: `-D clippy::ptr-eq` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ptr_eq)]` + +error: use `core::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq_no_std.rs:32:13 + | +LL | let _ = a as *const _ == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::eq(a, b)` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs index 76b9b98185119..fd5a88a37a66c 100644 --- a/tests/ui/read_zero_byte_vec.rs +++ b/tests/ui/read_zero_byte_vec.rs @@ -55,14 +55,6 @@ fn test() -> io::Result<()> { let mut buf = [0u8; 100]; f.read(&mut buf)?; - // should not lint - let mut empty = vec![]; - let mut data7 = vec![]; - f.read(&mut empty); - - // should not lint - f.read(&mut data7); - // should not lint let mut data8 = Vec::new(); data8.resize(100, 0); @@ -75,6 +67,27 @@ fn test() -> io::Result<()> { Ok(()) } +fn test_nested() -> io::Result<()> { + let cap = 1000; + let mut f = File::open("foo.txt").unwrap(); + + // Issue #9274 + // Should not lint + let mut v = Vec::new(); + { + v.resize(10, 0); + f.read(&mut v)?; + } + + let mut v = Vec::new(); + { + f.read(&mut v)?; + //~^ ERROR: reading zero byte data to `Vec` + } + + Ok(()) +} + async fn test_futures(r: &mut R) { // should lint let mut data = Vec::new(); diff --git a/tests/ui/read_zero_byte_vec.stderr b/tests/ui/read_zero_byte_vec.stderr index 523ecb2948df3..e85aa051c34bc 100644 --- a/tests/ui/read_zero_byte_vec.stderr +++ b/tests/ui/read_zero_byte_vec.stderr @@ -2,7 +2,7 @@ error: reading zero byte data to `Vec` --> $DIR/read_zero_byte_vec.rs:21:5 | LL | f.read_exact(&mut data).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data).unwrap();` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data)` | = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::read_zero_byte_vec)]` @@ -11,19 +11,19 @@ error: reading zero byte data to `Vec` --> $DIR/read_zero_byte_vec.rs:27:5 | LL | f.read_exact(&mut data2)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)?;` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)` error: reading zero byte data to `Vec` --> $DIR/read_zero_byte_vec.rs:32:5 | LL | f.read_exact(&mut data3)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:37:5 + --> $DIR/read_zero_byte_vec.rs:37:13 | LL | let _ = f.read(&mut data4)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` --> $DIR/read_zero_byte_vec.rs:43:9 @@ -38,28 +38,34 @@ LL | f.read(&mut data6) | ^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:81:5 + --> $DIR/read_zero_byte_vec.rs:84:9 + | +LL | f.read(&mut v)?; + | ^^^^^^^^^^^^^^ + +error: reading zero byte data to `Vec` + --> $DIR/read_zero_byte_vec.rs:94:5 | LL | r.read(&mut data).await.unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:86:5 + --> $DIR/read_zero_byte_vec.rs:99:5 | LL | r.read_exact(&mut data2).await.unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:93:5 + --> $DIR/read_zero_byte_vec.rs:106:5 | LL | r.read(&mut data).await.unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ error: reading zero byte data to `Vec` - --> $DIR/read_zero_byte_vec.rs:98:5 + --> $DIR/read_zero_byte_vec.rs:111:5 | LL | r.read_exact(&mut data2).await.unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/result_map_or_into_option.stderr b/tests/ui/result_map_or_into_option.stderr index 3d6bfef48ecab..201868f09efa6 100644 --- a/tests/ui/result_map_or_into_option.stderr +++ b/tests/ui/result_map_or_into_option.stderr @@ -2,7 +2,7 @@ error: called `map_or(None, Some)` on a `Result` value --> $DIR/result_map_or_into_option.rs:5:13 | LL | let _ = opt.map_or(None, Some); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok`: `opt.ok()` | = note: `-D clippy::result-map-or-into-option` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::result_map_or_into_option)]` @@ -11,13 +11,13 @@ error: called `map_or_else(|_| None, Some)` on a `Result` value --> $DIR/result_map_or_into_option.rs:7:13 | LL | let _ = opt.map_or_else(|_| None, Some); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok`: `opt.ok()` error: called `map_or_else(|_| None, Some)` on a `Result` value --> $DIR/result_map_or_into_option.rs:10:13 | LL | let _ = opt.map_or_else(|_| { None }, Some); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `ok`: `opt.ok()` error: aborting due to 3 previous errors diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index f519be463695b..f569aab64900a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -4,7 +4,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(item); | ^^^ | - = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) = note: `-D clippy::same-item-push` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::same_item_push)]` @@ -14,7 +14,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(item); | ^^^ | - = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec --> $DIR/same_item_push.rs:36:9 @@ -22,7 +22,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(13); | ^^^ | - = help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) + = help: consider using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) error: it looks like the same item is being pushed into this Vec --> $DIR/same_item_push.rs:42:9 @@ -30,7 +30,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(VALUE); | ^^^ | - = help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) + = help: consider using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) error: it looks like the same item is being pushed into this Vec --> $DIR/same_item_push.rs:49:9 @@ -38,7 +38,7 @@ error: it looks like the same item is being pushed into this Vec LL | vec.push(item); | ^^^ | - = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: aborting due to 5 previous errors diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index a7a47447f61de..9dec8c9caf274 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -40,7 +40,7 @@ error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some.rs:42:20 | LL | let _ = (0..1).find(some_closure).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(some_closure)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(some_closure)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some.rs:52:13 @@ -82,7 +82,7 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some.rs:79:13 | LL | let _ = (0..1).find(some_closure).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(some_closure)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(some_closure)` error: aborting due to 8 previous errors diff --git a/tests/ui/search_is_some_fixable_none.stderr b/tests/ui/search_is_some_fixable_none.stderr index f33b0430912a1..107f59a97d2f3 100644 --- a/tests/ui/search_is_some_fixable_none.stderr +++ b/tests/ui/search_is_some_fixable_none.stderr @@ -2,7 +2,7 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:9:13 | LL | let _ = v.iter().find(|&x| *x < 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x < 0)` | = note: `-D clippy::search-is-some` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` @@ -11,43 +11,43 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:10:13 | LL | let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(|x| **y == x)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:11:13 | LL | let _ = (0..1).find(|x| *x == 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(0..1).any(|x| x == 0)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:12:13 | LL | let _ = v.iter().find(|x| **x == 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| *x == 0)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:13:13 | LL | let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(4..5).any(|x| x == 1 || x == 3 || x == 5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(4..5).any(|x| x == 1 || x == 3 || x == 5)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:14:13 | LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(1..3).any(|x| [1, 2, 3].contains(&x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:15:13 | LL | let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:16:13 | LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:17:13 @@ -56,91 +56,91 @@ LL | let _ = (1..3) | _____________^ LL | | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1) LL | | .is_none(); - | |__________________^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` + | |__________________^ help: consider using: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` error: called `is_none()` after searching an `Iterator` with `position` --> $DIR/search_is_some_fixable_none.rs:22:13 | LL | let _ = v.iter().position(|&x| x < 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|&x| x < 0)` error: called `is_none()` after searching an `Iterator` with `rposition` --> $DIR/search_is_some_fixable_none.rs:25:13 | LL | let _ = v.iter().rposition(|&x| x < 0).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|&x| x < 0)` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:31:13 | LL | let _ = "hello world".find("world").is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains("world")` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:32:13 | LL | let _ = "hello world".find(&s2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains(&s2)` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:33:13 | LL | let _ = "hello world".find(&s2[2..]).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!"hello world".contains(&s2[2..])` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:35:13 | LL | let _ = s1.find("world").is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains("world")` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:36:13 | LL | let _ = s1.find(&s2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains(&s2)` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:37:13 | LL | let _ = s1.find(&s2[2..]).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1.contains(&s2[2..])` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:39:13 | LL | let _ = s1[2..].find("world").is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains("world")` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:40:13 | LL | let _ = s1[2..].find(&s2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains(&s2)` error: called `is_none()` after calling `find()` on a string --> $DIR/search_is_some_fixable_none.rs:41:13 | LL | let _ = s1[2..].find(&s2[2..]).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!s1[2..].contains(&s2[2..])` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:57:25 | LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == &cc)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!filter_hand.iter().any(|cc| c == &cc)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:73:30 | LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == cc)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!filter_hand.iter().any(|cc| c == cc)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:84:17 | LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:87:17 @@ -152,7 +152,7 @@ LL | | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2) LL | | .is_none(); | |______________________^ | -help: use `!_.any()` instead +help: consider using | LL ~ let _ = !vfoo LL ~ .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2); @@ -162,49 +162,49 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:95:17 | LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|a| a[0] == 42)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|a| a[0] == 42)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:101:17 | LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:119:17 | LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:120:17 | LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:123:17 | LL | let _ = v.iter().find(|x| deref_enough(**x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| deref_enough(*x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| deref_enough(*x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:124:17 | LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| deref_enough(*x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x: &u32| deref_enough(*x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:127:17 | LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| arg_no_deref(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x| arg_no_deref(&x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:129:17 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| arg_no_deref(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|x: &u32| arg_no_deref(&x))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:149:17 @@ -216,7 +216,7 @@ LL | | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == LL | | .is_none(); | |______________________^ | -help: use `!_.any()` instead +help: consider using | LL ~ let _ = !vfoo LL ~ .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2); @@ -226,61 +226,61 @@ error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:165:17 | LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.inner[0].bar == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.inner[0].bar == 2)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:170:17 | LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|x| (**x)[0] == 9)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|x| (**x)[0] == 9)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:183:17 | LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.by_ref(&v.bar))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!vfoo.iter().any(|v| v.by_ref(&v.bar))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:187:17 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:188:17 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:207:17 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| s[0].is_empty())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| s[0].is_empty())` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:208:17 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| test_string_1(&s[0]))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|s| test_string_1(&s[0]))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:217:17 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| fp.field.is_power_of_two())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| fp.field.is_power_of_two())` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:218:17 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_1(fp.field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_1(fp.field))` error: called `is_none()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_none.rs:219:17 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `!v.iter().any(|fp| test_u32_2(*fp.field))` error: aborting due to 43 previous errors diff --git a/tests/ui/search_is_some_fixable_some.stderr b/tests/ui/search_is_some_fixable_some.stderr index e878e62de07f3..e706ce646923d 100644 --- a/tests/ui/search_is_some_fixable_some.stderr +++ b/tests/ui/search_is_some_fixable_some.stderr @@ -2,7 +2,7 @@ error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:9:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| *x < 0)` | = note: `-D clippy::search-is-some` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::search_is_some)]` @@ -11,43 +11,43 @@ error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:10:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:11:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:12:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:13:20 | LL | let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 1 || x == 3 || x == 5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 1 || x == 3 || x == 5)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:14:20 | LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| [1, 2, 3].contains(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:15:20 | LL | let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0 || [1, 2, 3].contains(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| x == 0 || [1, 2, 3].contains(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:16:20 | LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:18:10 @@ -55,91 +55,91 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1) | __________^ LL | | .is_some(); - | |__________________^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` + | |__________________^ help: consider using: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)` error: called `is_some()` after searching an `Iterator` with `position` --> $DIR/search_is_some_fixable_some.rs:22:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with `rposition` --> $DIR/search_is_some_fixable_some.rs:25:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|&x| x < 0)` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:30:27 | LL | let _ = "hello world".find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:31:27 | LL | let _ = "hello world".find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:32:27 | LL | let _ = "hello world".find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:34:16 | LL | let _ = s1.find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:35:16 | LL | let _ = s1.find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:36:16 | LL | let _ = s1.find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:38:21 | LL | let _ = s1[2..].find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains("world")` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:39:21 | LL | let _ = s1[2..].find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2)` error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable_some.rs:40:21 | LL | let _ = s1[2..].find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `contains(&s2[2..])` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:56:44 | LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == &cc)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == &cc)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:72:49 | LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == cc)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|cc| c == cc)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:83:29 | LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.foo == 1 && v.bar == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.foo == 1 && v.bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:88:14 @@ -147,55 +147,55 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2) | ______________^ LL | | .is_some(); - | |______________________^ help: use `any()` instead: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)` + | |______________________^ help: consider using: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:94:29 | LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|a| a[0] == 42)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|a| a[0] == 42)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:100:29 | LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|sub| sub[1..4].len() == 3)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|sub| sub[1..4].len() == 3)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:118:30 | LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|ppp_x: &&u32| please(ppp_x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|ppp_x: &&u32| please(ppp_x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:119:50 | LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s.len() == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s.len() == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:122:26 | LL | let _ = v.iter().find(|x| deref_enough(**x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| deref_enough(*x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| deref_enough(*x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:123:26 | LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| deref_enough(*x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| deref_enough(*x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:126:26 | LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| arg_no_deref(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| arg_no_deref(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:128:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:150:14 @@ -203,91 +203,91 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2) | ______________^ LL | | .is_some(); - | |______________________^ help: use `any()` instead: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)` + | |______________________^ help: consider using: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:164:29 | LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.inner[0].bar == 2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.inner[0].bar == 2)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:169:29 | LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| (**x)[0] == 9)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x| (**x)[0] == 9)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:182:29 | LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.by_ref(&v.bar))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| v.by_ref(&v.bar))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:186:55 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:187:55 | LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|(&x, y)| x == *y)` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:206:26 | LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s[0].is_empty())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| s[0].is_empty())` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:207:26 | LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| test_string_1(&s[0]))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|s| test_string_1(&s[0]))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:216:26 | LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| fp.field.is_power_of_two())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| fp.field.is_power_of_two())` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:217:26 | LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_1(fp.field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_1(fp.field))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:218:26 | LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|fp| test_u32_2(*fp.field))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:233:18 | LL | v.iter().find(|x: &&u32| func(x)).is_some() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| func(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| func(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:242:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_impl(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_impl(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:245:26 | LL | let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_dyn(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| arg_no_deref_dyn(&x))` error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable_some.rs:248:26 | LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` error: aborting due to 47 previous errors diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed index 15cc8d54faab3..8859a68320f00 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -80,6 +80,7 @@ fn main() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); @@ -104,6 +105,7 @@ fn msrv_1_54() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); @@ -124,6 +126,7 @@ fn msrv_1_55() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs index 197225ffbd5c9..7b72efb34ff82 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/tests/ui/seek_to_start_instead_of_rewind.rs @@ -80,6 +80,7 @@ fn main() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); @@ -104,6 +105,7 @@ fn msrv_1_54() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); @@ -124,6 +126,7 @@ fn msrv_1_55() { .write(true) .read(true) .create(true) + .truncate(true) .open("foo.txt") .unwrap(); diff --git a/tests/ui/seek_to_start_instead_of_rewind.stderr b/tests/ui/seek_to_start_instead_of_rewind.stderr index 05c11cf7f8c61..b6b0d2effa815 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.stderr +++ b/tests/ui/seek_to_start_instead_of_rewind.stderr @@ -14,7 +14,7 @@ LL | t.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:133:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:136:7 | LL | f.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` diff --git a/tests/ui/semicolon_if_nothing_returned.fixed b/tests/ui/semicolon_if_nothing_returned.fixed index bbcc0de27d115..cdfa5d9cc7804 100644 --- a/tests/ui/semicolon_if_nothing_returned.fixed +++ b/tests/ui/semicolon_if_nothing_returned.fixed @@ -1,6 +1,11 @@ +//@aux-build:proc_macro_attr.rs + #![warn(clippy::semicolon_if_nothing_returned)] #![allow(clippy::redundant_closure, clippy::uninlined_format_args, clippy::needless_late_init)] +#[macro_use] +extern crate proc_macro_attr; + fn get_unit() {} // the functions below trigger the lint @@ -120,3 +125,32 @@ fn let_else_stmts() { return; }; } + +mod issue12123 { + #[rustfmt::skip] + mod this_triggers { + #[fake_main] + async fn main() { + + } + } + + mod and_this { + #[fake_main] + async fn main() { + println!("hello"); + } + } + + #[rustfmt::skip] + mod maybe_this { + /** */ #[fake_main] + async fn main() { + } + } + + mod but_this_does_not { + #[fake_main] + async fn main() {} + } +} diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index fdc9c0c33f5a3..315b7e4f38399 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -1,6 +1,11 @@ +//@aux-build:proc_macro_attr.rs + #![warn(clippy::semicolon_if_nothing_returned)] #![allow(clippy::redundant_closure, clippy::uninlined_format_args, clippy::needless_late_init)] +#[macro_use] +extern crate proc_macro_attr; + fn get_unit() {} // the functions below trigger the lint @@ -120,3 +125,32 @@ fn let_else_stmts() { return; }; } + +mod issue12123 { + #[rustfmt::skip] + mod this_triggers { + #[fake_main] + async fn main() { + + } + } + + mod and_this { + #[fake_main] + async fn main() { + println!("hello"); + } + } + + #[rustfmt::skip] + mod maybe_this { + /** */ #[fake_main] + async fn main() { + } + } + + mod but_this_does_not { + #[fake_main] + async fn main() {} + } +} diff --git a/tests/ui/semicolon_if_nothing_returned.stderr b/tests/ui/semicolon_if_nothing_returned.stderr index 66373a13c5690..09c4d12f216c5 100644 --- a/tests/ui/semicolon_if_nothing_returned.stderr +++ b/tests/ui/semicolon_if_nothing_returned.stderr @@ -1,5 +1,5 @@ error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:8:5 + --> $DIR/semicolon_if_nothing_returned.rs:13:5 | LL | println!("Hello") | ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");` @@ -8,25 +8,25 @@ LL | println!("Hello") = help: to override `-D warnings` add `#[allow(clippy::semicolon_if_nothing_returned)]` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:12:5 + --> $DIR/semicolon_if_nothing_returned.rs:17:5 | LL | get_unit() | ^^^^^^^^^^ help: add a `;` here: `get_unit();` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:17:5 + --> $DIR/semicolon_if_nothing_returned.rs:22:5 | LL | y = x + 1 | ^^^^^^^^^ help: add a `;` here: `y = x + 1;` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:23:9 + --> $DIR/semicolon_if_nothing_returned.rs:28:9 | LL | hello() | ^^^^^^^ help: add a `;` here: `hello();` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:34:9 + --> $DIR/semicolon_if_nothing_returned.rs:39:9 | LL | ptr::drop_in_place(s.as_mut_ptr()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());` diff --git a/tests/ui/single_call_fn.rs b/tests/ui/single_call_fn.rs index 3cc8061647dff..c20214fecccea 100644 --- a/tests/ui/single_call_fn.rs +++ b/tests/ui/single_call_fn.rs @@ -69,6 +69,17 @@ fn e() { #[test] fn k() {} +mod issue12182 { + #[allow(clippy::single_call_fn)] + fn print_foo(text: &str) { + println!("{text}"); + } + + fn use_print_foo() { + print_foo("foo"); + } +} + #[test] fn l() { k(); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index 781ab316d9de3..664d6b5a1e9b0 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -2,7 +2,7 @@ error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:7:13 | LL | x.split("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` | = note: `-D clippy::single-char-pattern` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::single_char_pattern)]` @@ -11,235 +11,235 @@ error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:13:13 | LL | x.split("รŸ"); - | ^^^ help: try using a `char` instead: `'รŸ'` + | ^^^ help: consider using a `char`: `'รŸ'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:14:13 | LL | x.split("โ„"); - | ^^^ help: try using a `char` instead: `'โ„'` + | ^^^ help: consider using a `char`: `'โ„'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:15:13 | LL | x.split("๐Ÿ’ฃ"); - | ^^^^ help: try using a `char` instead: `'๐Ÿ’ฃ'` + | ^^^^ help: consider using a `char`: `'๐Ÿ’ฃ'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:18:23 | LL | x.split_inclusive("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:19:16 | LL | x.contains("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:20:19 | LL | x.starts_with("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:21:17 | LL | x.ends_with("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:22:12 | LL | x.find("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:23:13 | LL | x.rfind("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:24:14 | LL | x.rsplit("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:25:24 | LL | x.split_terminator("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:26:25 | LL | x.rsplit_terminator("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:27:17 | LL | x.splitn(2, "x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:28:18 | LL | x.rsplitn(2, "x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:29:18 | LL | x.split_once("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:30:19 | LL | x.rsplit_once("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:31:15 | LL | x.matches("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:32:16 | LL | x.rmatches("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:33:21 | LL | x.match_indices("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:34:22 | LL | x.rmatch_indices("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:35:26 | LL | x.trim_start_matches("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:36:24 | LL | x.trim_end_matches("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:37:20 | LL | x.strip_prefix("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:38:20 | LL | x.strip_suffix("x"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:39:15 | LL | x.replace("x", "y"); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:40:16 | LL | x.replacen("x", "y", 3); - | ^^^ help: try using a `char` instead: `'x'` + | ^^^ help: consider using a `char`: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:42:13 | LL | x.split("\n"); - | ^^^^ help: try using a `char` instead: `'\n'` + | ^^^^ help: consider using a `char`: `'\n'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:43:13 | LL | x.split("'"); - | ^^^ help: try using a `char` instead: `'\''` + | ^^^ help: consider using a `char`: `'\''` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:44:13 | LL | x.split("\'"); - | ^^^^ help: try using a `char` instead: `'\''` + | ^^^^ help: consider using a `char`: `'\''` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:46:13 | LL | x.split("\""); - | ^^^^ help: try using a `char` instead: `'"'` + | ^^^^ help: consider using a `char`: `'"'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:51:31 | LL | x.replace(';', ",").split(","); // issue #2978 - | ^^^ help: try using a `char` instead: `','` + | ^^^ help: consider using a `char`: `','` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:52:19 | LL | x.starts_with("\x03"); // issue #2996 - | ^^^^^^ help: try using a `char` instead: `'\x03'` + | ^^^^^^ help: consider using a `char`: `'\x03'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:59:13 | LL | x.split(r"a"); - | ^^^^ help: try using a `char` instead: `'a'` + | ^^^^ help: consider using a `char`: `'a'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:60:13 | LL | x.split(r#"a"#); - | ^^^^^^ help: try using a `char` instead: `'a'` + | ^^^^^^ help: consider using a `char`: `'a'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:61:13 | LL | x.split(r###"a"###); - | ^^^^^^^^^^ help: try using a `char` instead: `'a'` + | ^^^^^^^^^^ help: consider using a `char`: `'a'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:62:13 | LL | x.split(r###"'"###); - | ^^^^^^^^^^ help: try using a `char` instead: `'\''` + | ^^^^^^^^^^ help: consider using a `char`: `'\''` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:63:13 | LL | x.split(r###"#"###); - | ^^^^^^^^^^ help: try using a `char` instead: `'#'` + | ^^^^^^^^^^ help: consider using a `char`: `'#'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:65:13 | LL | x.split(r#"\"#); - | ^^^^^^ help: try using a `char` instead: `'\\'` + | ^^^^^^ help: consider using a `char`: `'\\'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:66:13 | LL | x.split(r"\"); - | ^^^^ help: try using a `char` instead: `'\\'` + | ^^^^ help: consider using a `char`: `'\\'` error: aborting due to 40 previous errors diff --git a/tests/ui/trait_duplication_in_bounds.fixed b/tests/ui/trait_duplication_in_bounds.fixed index 4fca29698e06b..7e2663d734f63 100644 --- a/tests/ui/trait_duplication_in_bounds.fixed +++ b/tests/ui/trait_duplication_in_bounds.fixed @@ -118,4 +118,33 @@ fn bad_trait_object(arg0: &(dyn Any + Send)) { unimplemented!(); } -fn main() {} +trait Proj { + type S; +} + +impl Proj for () { + type S = (); +} + +impl Proj for i32 { + type S = i32; +} + +trait Base { + fn is_base(&self); +} + +trait Derived: Base + Base<()> { + fn is_derived(&self); +} + +fn f(obj: &dyn Derived

) { + obj.is_derived(); + Base::::is_base(obj); + Base::<()>::is_base(obj); +} + +fn main() { + let _x: fn(_) = f::<()>; + let _x: fn(_) = f::; +} diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index f67c8e35ed4cf..fede1671a4363 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -118,4 +118,33 @@ fn bad_trait_object(arg0: &(dyn Any + Send + Send)) { unimplemented!(); } -fn main() {} +trait Proj { + type S; +} + +impl Proj for () { + type S = (); +} + +impl Proj for i32 { + type S = i32; +} + +trait Base { + fn is_base(&self); +} + +trait Derived: Base + Base<()> { + fn is_derived(&self); +} + +fn f(obj: &dyn Derived

) { + obj.is_derived(); + Base::::is_base(obj); + Base::<()>::is_base(obj); +} + +fn main() { + let _x: fn(_) = f::<()>; + let _x: fn(_) = f::; +} diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 32f6027e99111..1796ccaf28e1a 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -102,19 +102,6 @@ fn crosspointer() { } } -#[warn(clippy::transmute_int_to_char)] -fn int_to_char() { - let _: char = unsafe { std::mem::transmute(0_u32) }; - //~^ ERROR: transmute from a `u32` to a `char` - //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` - let _: char = unsafe { std::mem::transmute(0_i32) }; - //~^ ERROR: transmute from a `i32` to a `char` - - // These shouldn't warn - const _: char = unsafe { std::mem::transmute(0_u32) }; - const _: char = unsafe { std::mem::transmute(0_i32) }; -} - #[warn(clippy::transmute_int_to_bool)] fn int_to_bool() { let _: bool = unsafe { std::mem::transmute(0_u8) }; diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index cdc733b54a9b2..df32d478cbf40 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -88,23 +88,8 @@ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:107:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` - | - = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` - -error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:110:28 - | -LL | let _: char = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` - error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:120:28 + --> $DIR/transmute.rs:107:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -113,7 +98,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:128:31 + --> $DIR/transmute.rs:115:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -122,25 +107,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:131:31 + --> $DIR/transmute.rs:118:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> $DIR/transmute.rs:133:31 + --> $DIR/transmute.rs:120:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> $DIR/transmute.rs:135:31 + --> $DIR/transmute.rs:122:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:156:30 + --> $DIR/transmute.rs:143:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -149,85 +134,85 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:159:30 + --> $DIR/transmute.rs:146:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:161:31 + --> $DIR/transmute.rs:148:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:163:30 + --> $DIR/transmute.rs:150:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:165:30 + --> $DIR/transmute.rs:152:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:167:31 + --> $DIR/transmute.rs:154:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> $DIR/transmute.rs:169:30 + --> $DIR/transmute.rs:156:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> $DIR/transmute.rs:171:30 + --> $DIR/transmute.rs:158:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:177:30 + --> $DIR/transmute.rs:164:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:179:30 + --> $DIR/transmute.rs:166:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:181:31 + --> $DIR/transmute.rs:168:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:183:30 + --> $DIR/transmute.rs:170:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:185:30 + --> $DIR/transmute.rs:172:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:187:31 + --> $DIR/transmute.rs:174:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:198:28 + --> $DIR/transmute.rs:185:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -236,16 +221,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:201:32 + --> $DIR/transmute.rs:188:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:203:30 + --> $DIR/transmute.rs:190:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 38 previous errors +error: aborting due to 36 previous errors diff --git a/tests/ui/transmute_int_to_char.fixed b/tests/ui/transmute_int_to_char.fixed new file mode 100644 index 0000000000000..1708011817541 --- /dev/null +++ b/tests/ui/transmute_int_to_char.fixed @@ -0,0 +1,15 @@ +#![warn(clippy::transmute_int_to_char)] + +fn int_to_char() { + let _: char = unsafe { std::char::from_u32(0_u32).unwrap() }; + //~^ ERROR: transmute from a `u32` to a `char` + //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` + let _: char = unsafe { std::char::from_u32(0_i32 as u32).unwrap() }; + //~^ ERROR: transmute from a `i32` to a `char` + + // These shouldn't warn + const _: char = unsafe { std::mem::transmute(0_u32) }; + const _: char = unsafe { std::mem::transmute(0_i32) }; +} + +fn main() {} diff --git a/tests/ui/transmute_int_to_char.rs b/tests/ui/transmute_int_to_char.rs new file mode 100644 index 0000000000000..5846a97e88abf --- /dev/null +++ b/tests/ui/transmute_int_to_char.rs @@ -0,0 +1,15 @@ +#![warn(clippy::transmute_int_to_char)] + +fn int_to_char() { + let _: char = unsafe { std::mem::transmute(0_u32) }; + //~^ ERROR: transmute from a `u32` to a `char` + //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` + let _: char = unsafe { std::mem::transmute(0_i32) }; + //~^ ERROR: transmute from a `i32` to a `char` + + // These shouldn't warn + const _: char = unsafe { std::mem::transmute(0_u32) }; + const _: char = unsafe { std::mem::transmute(0_i32) }; +} + +fn main() {} diff --git a/tests/ui/transmute_int_to_char.stderr b/tests/ui/transmute_int_to_char.stderr new file mode 100644 index 0000000000000..2297f5b4f8bf2 --- /dev/null +++ b/tests/ui/transmute_int_to_char.stderr @@ -0,0 +1,17 @@ +error: transmute from a `u32` to a `char` + --> $DIR/transmute_int_to_char.rs:4:28 + | +LL | let _: char = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` + | + = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` + +error: transmute from a `i32` to a `char` + --> $DIR/transmute_int_to_char.rs:7:28 + | +LL | let _: char = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/transmute_int_to_char_no_std.fixed b/tests/ui/transmute_int_to_char_no_std.fixed new file mode 100644 index 0000000000000..9ae4e11fb56e7 --- /dev/null +++ b/tests/ui/transmute_int_to_char_no_std.fixed @@ -0,0 +1,27 @@ +#![no_std] +#![feature(lang_items)] +#![warn(clippy::transmute_int_to_char)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn int_to_char() { + let _: char = unsafe { core::char::from_u32(0_u32).unwrap() }; + //~^ ERROR: transmute from a `u32` to a `char` + //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` + let _: char = unsafe { core::char::from_u32(0_i32 as u32).unwrap() }; + //~^ ERROR: transmute from a `i32` to a `char` + + // These shouldn't warn + const _: char = unsafe { core::mem::transmute(0_u32) }; + const _: char = unsafe { core::mem::transmute(0_i32) }; +} + +fn main() {} diff --git a/tests/ui/transmute_int_to_char_no_std.rs b/tests/ui/transmute_int_to_char_no_std.rs new file mode 100644 index 0000000000000..9a2afd5bd2fd6 --- /dev/null +++ b/tests/ui/transmute_int_to_char_no_std.rs @@ -0,0 +1,27 @@ +#![no_std] +#![feature(lang_items)] +#![warn(clippy::transmute_int_to_char)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn int_to_char() { + let _: char = unsafe { core::mem::transmute(0_u32) }; + //~^ ERROR: transmute from a `u32` to a `char` + //~| NOTE: `-D clippy::transmute-int-to-char` implied by `-D warnings` + let _: char = unsafe { core::mem::transmute(0_i32) }; + //~^ ERROR: transmute from a `i32` to a `char` + + // These shouldn't warn + const _: char = unsafe { core::mem::transmute(0_u32) }; + const _: char = unsafe { core::mem::transmute(0_i32) }; +} + +fn main() {} diff --git a/tests/ui/transmute_int_to_char_no_std.stderr b/tests/ui/transmute_int_to_char_no_std.stderr new file mode 100644 index 0000000000000..aace6968696da --- /dev/null +++ b/tests/ui/transmute_int_to_char_no_std.stderr @@ -0,0 +1,17 @@ +error: transmute from a `u32` to a `char` + --> $DIR/transmute_int_to_char_no_std.rs:16:28 + | +LL | let _: char = unsafe { core::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_u32).unwrap()` + | + = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_char)]` + +error: transmute from a `i32` to a `char` + --> $DIR/transmute_int_to_char_no_std.rs:19:28 + | +LL | let _: char = unsafe { core::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::char::from_u32(0_i32 as u32).unwrap()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/transmute_ref_to_ref.rs b/tests/ui/transmute_ref_to_ref.rs index e7f35c5743684..bdc7b9f647877 100644 --- a/tests/ui/transmute_ref_to_ref.rs +++ b/tests/ui/transmute_ref_to_ref.rs @@ -12,7 +12,7 @@ fn main() { let b: &[u8] = unsafe { std::mem::transmute(a) }; //~^ ERROR: transmute from a reference to a reference let bytes = &[1u8, 2u8, 3u8, 4u8] as &[u8]; - let alt_slice: &[u32] = unsafe { core::mem::transmute(bytes) }; + let alt_slice: &[u32] = unsafe { std::mem::transmute(bytes) }; //~^ ERROR: transmute from a reference to a reference } } diff --git a/tests/ui/transmute_ref_to_ref.stderr b/tests/ui/transmute_ref_to_ref.stderr index cc6b156b18885..4238bc637ad04 100644 --- a/tests/ui/transmute_ref_to_ref.stderr +++ b/tests/ui/transmute_ref_to_ref.stderr @@ -19,8 +19,8 @@ LL | let b: &[u8] = unsafe { std::mem::transmute(a) }; error: transmute from a reference to a reference --> $DIR/transmute_ref_to_ref.rs:15:42 | -LL | let alt_slice: &[u32] = unsafe { core::mem::transmute(bytes) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])` +LL | let alt_slice: &[u32] = unsafe { std::mem::transmute(bytes) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])` error: aborting due to 3 previous errors diff --git a/tests/ui/transmute_ref_to_ref_no_std.rs b/tests/ui/transmute_ref_to_ref_no_std.rs new file mode 100644 index 0000000000000..b67386f858852 --- /dev/null +++ b/tests/ui/transmute_ref_to_ref_no_std.rs @@ -0,0 +1,30 @@ +//@no-rustfix + +#![deny(clippy::transmute_ptr_to_ptr)] +#![allow(dead_code)] +#![feature(lang_items)] +#![no_std] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn main() { + unsafe { + let single_u64: &[u64] = &[0xDEAD_BEEF_DEAD_BEEF]; + let bools: &[bool] = unsafe { core::mem::transmute(single_u64) }; + //~^ ERROR: transmute from a reference to a reference + let a: &[u32] = &[0x12345678, 0x90ABCDEF, 0xFEDCBA09, 0x87654321]; + let b: &[u8] = unsafe { core::mem::transmute(a) }; + //~^ ERROR: transmute from a reference to a reference + let bytes = &[1u8, 2u8, 3u8, 4u8] as &[u8]; + let alt_slice: &[u32] = unsafe { core::mem::transmute(bytes) }; + //~^ ERROR: transmute from a reference to a reference + } +} diff --git a/tests/ui/transmute_ref_to_ref_no_std.stderr b/tests/ui/transmute_ref_to_ref_no_std.stderr new file mode 100644 index 0000000000000..ca7966ffa9e88 --- /dev/null +++ b/tests/ui/transmute_ref_to_ref_no_std.stderr @@ -0,0 +1,26 @@ +error: transmute from a reference to a reference + --> $DIR/transmute_ref_to_ref_no_std.rs:21:39 + | +LL | let bools: &[bool] = unsafe { core::mem::transmute(single_u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(single_u64 as *const [u64] as *const [bool])` + | +note: the lint level is defined here + --> $DIR/transmute_ref_to_ref_no_std.rs:3:9 + | +LL | #![deny(clippy::transmute_ptr_to_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a reference to a reference + --> $DIR/transmute_ref_to_ref_no_std.rs:24:33 + | +LL | let b: &[u8] = unsafe { core::mem::transmute(a) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const [u32] as *const [u8])` + +error: transmute from a reference to a reference + --> $DIR/transmute_ref_to_ref_no_std.rs:27:42 + | +LL | let alt_slice: &[u32] = unsafe { core::mem::transmute(bytes) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/unconditional_recursion.rs b/tests/ui/unconditional_recursion.rs index e1a2d6a90b896..7b898a6e0e784 100644 --- a/tests/ui/unconditional_recursion.rs +++ b/tests/ui/unconditional_recursion.rs @@ -264,6 +264,28 @@ impl S13 { } } -fn main() { - // test code goes here +struct S14 { + field: String, +} + +impl PartialEq for S14 { + fn eq(&self, other: &Self) -> bool { + // Should not warn! + self.field.eq(&other.field) + } +} + +struct S15<'a> { + field: &'a S15<'a>, } + +impl PartialEq for S15<'_> { + fn eq(&self, other: &Self) -> bool { + //~^ ERROR: function cannot return without recursing + let mine = &self.field; + let theirs = &other.field; + mine.eq(theirs) + } +} + +fn main() {} diff --git a/tests/ui/unconditional_recursion.stderr b/tests/ui/unconditional_recursion.stderr index 5d82e2a9f3194..094b80d4586c8 100644 --- a/tests/ui/unconditional_recursion.stderr +++ b/tests/ui/unconditional_recursion.stderr @@ -340,5 +340,22 @@ note: recursive call site LL | Self::default() | ^^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: function cannot return without recursing + --> $DIR/unconditional_recursion.rs:283:5 + | +LL | / fn eq(&self, other: &Self) -> bool { +LL | | +LL | | let mine = &self.field; +LL | | let theirs = &other.field; +LL | | mine.eq(theirs) +LL | | } + | |_____^ + | +note: recursive call site + --> $DIR/unconditional_recursion.rs:287:9 + | +LL | mine.eq(theirs) + | ^^^^^^^^^^^^^^^ + +error: aborting due to 27 previous errors diff --git a/tests/ui/unnecessary_join.stderr b/tests/ui/unnecessary_join.stderr index 8bf2ac5fdb1d9..205a714b694ae 100644 --- a/tests/ui/unnecessary_join.stderr +++ b/tests/ui/unnecessary_join.stderr @@ -4,7 +4,7 @@ error: called `.collect::>().join("")` on an iterator LL | .collect::>() | __________^ LL | | .join(""); - | |_________________^ help: try using: `collect::()` + | |_________________^ help: consider using: `collect::()` | = note: `-D clippy::unnecessary-join` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_join)]` @@ -15,7 +15,7 @@ error: called `.collect::>().join("")` on an iterator LL | .collect::>() | __________^ LL | | .join(""); - | |_________________^ help: try using: `collect::()` + | |_________________^ help: consider using: `collect::()` error: aborting due to 2 previous errors diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 9d54c8d50e31f..f4409113a45fa 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -1,4 +1,4 @@ -error: use Vec::sort here instead +error: consider using `sort` --> $DIR/unnecessary_sort_by.rs:12:5 | LL | vec.sort_by(|a, b| a.cmp(b)); @@ -7,67 +7,67 @@ LL | vec.sort_by(|a, b| a.cmp(b)); = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_sort_by)]` -error: use Vec::sort here instead +error: consider using `sort` --> $DIR/unnecessary_sort_by.rs:13:5 | LL | vec.sort_unstable_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:14:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:15:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:18:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs()))` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:19:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b)))` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:29:5 | LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:30:5 | LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:89:9 | LL | args.sort_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:90:9 | LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:92:9 | LL | args.sort_by(|a, b| b.name().cmp(&a.name())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| std::cmp::Reverse(b.name()))` -error: use Vec::sort_by_key here instead +error: consider using `sort_by_key` --> $DIR/unnecessary_sort_by.rs:93:9 | LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 62aec6e9eafca..9974600dad5be 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -1,4 +1,5 @@ #![allow(dead_code, clippy::needless_pass_by_ref_mut)] +#![allow(clippy::redundant_pattern_matching)] #![warn(clippy::unused_io_amount)] extern crate futures; @@ -142,4 +143,90 @@ async fn undetected_bad_async_write(w: &mut W) { future.await.unwrap(); } +fn match_okay_underscore(s: &mut T) { + match s.write(b"test") { + //~^ ERROR: written amount is not handled + Ok(_) => todo!(), + //~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled + Err(_) => todo!(), + }; + + let mut buf = [0u8; 4]; + match s.read(&mut buf) { + //~^ ERROR: read amount is not handled + Ok(_) => todo!(), + //~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled + Err(_) => todo!(), + } +} + +fn match_okay_underscore_read_expr(s: &mut T) { + match s.read(&mut [0u8; 4]) { + //~^ ERROR: read amount is not handled + Ok(_) => todo!(), + //~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled + Err(_) => todo!(), + } +} + +fn match_okay_underscore_write_expr(s: &mut T) { + match s.write(b"test") { + //~^ ERROR: written amount is not handled + Ok(_) => todo!(), + //~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled + Err(_) => todo!(), + } +} + +fn returned_value_should_not_lint(s: &mut T) -> Result { + s.write(b"test") +} + +fn if_okay_underscore_read_expr(s: &mut T) { + if let Ok(_) = s.read(&mut [0u8; 4]) { + //~^ ERROR: read amount is not handled + todo!() + } +} + +fn if_okay_underscore_write_expr(s: &mut T) { + if let Ok(_) = s.write(b"test") { + //~^ ERROR: written amount is not handled + todo!() + } +} + +fn if_okay_dots_write_expr(s: &mut T) { + if let Ok(..) = s.write(b"test") { + //~^ ERROR: written amount is not handled + todo!() + } +} + +fn if_okay_underscore_write_expr_true_negative(s: &mut T) { + if let Ok(bound) = s.write(b"test") { + todo!() + } +} + +fn match_okay_underscore_true_neg(s: &mut T) { + match s.write(b"test") { + Ok(bound) => todo!(), + Err(_) => todo!(), + }; +} + +fn true_negative(s: &mut T) { + let mut buf = [0u8; 4]; + let read_amount = s.read(&mut buf).unwrap(); +} + +fn on_return_should_not_raise(s: &mut T) -> io::Result { + /// this is bad code because it goes around the problem of handling the read amount + /// by returning it, which makes it impossible to know this is a resonpose from the + /// correct account. + let mut buf = [0u8; 4]; + s.read(&mut buf) +} + fn main() {} diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index f9aef596a1c98..4af56d264bfa1 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -1,5 +1,5 @@ error: written amount is not handled - --> $DIR/unused_io_amount.rs:9:5 + --> $DIR/unused_io_amount.rs:10:5 | LL | s.write(b"test")?; | ^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | s.write(b"test")?; = help: to override `-D warnings` add `#[allow(clippy::unused_io_amount)]` error: read amount is not handled - --> $DIR/unused_io_amount.rs:12:5 + --> $DIR/unused_io_amount.rs:13:5 | LL | s.read(&mut buf)?; | ^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | s.read(&mut buf)?; = help: use `Read::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:18:5 + --> $DIR/unused_io_amount.rs:19:5 | LL | s.write(b"test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | s.write(b"test").unwrap(); = help: use `Write::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:21:5 + --> $DIR/unused_io_amount.rs:22:5 | LL | s.read(&mut buf).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,19 +33,19 @@ LL | s.read(&mut buf).unwrap(); = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:26:5 + --> $DIR/unused_io_amount.rs:27:5 | LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: written amount is not handled - --> $DIR/unused_io_amount.rs:28:5 + --> $DIR/unused_io_amount.rs:29:5 | LL | s.write_vectored(&[io::IoSlice::new(&[])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: read amount is not handled - --> $DIR/unused_io_amount.rs:36:5 + --> $DIR/unused_io_amount.rs:37:5 | LL | reader.read(&mut result).ok()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | reader.read(&mut result).ok()?; = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:46:5 + --> $DIR/unused_io_amount.rs:47:5 | LL | reader.read(&mut result).or_else(|err| Err(err))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL | reader.read(&mut result).or_else(|err| Err(err))?; = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:59:5 + --> $DIR/unused_io_amount.rs:60:5 | LL | reader.read(&mut result).or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL | reader.read(&mut result).or(Err(Error::Kind))?; = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:67:5 + --> $DIR/unused_io_amount.rs:68:5 | LL | / reader LL | | @@ -82,7 +82,7 @@ LL | | .expect("error"); = help: use `Read::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:77:5 + --> $DIR/unused_io_amount.rs:78:5 | LL | s.write(b"ok").is_ok(); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | s.write(b"ok").is_ok(); = help: use `Write::write_all` instead, or handle partial writes error: written amount is not handled - --> $DIR/unused_io_amount.rs:79:5 + --> $DIR/unused_io_amount.rs:80:5 | LL | s.write(b"err").is_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -98,7 +98,7 @@ LL | s.write(b"err").is_err(); = help: use `Write::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:82:5 + --> $DIR/unused_io_amount.rs:83:5 | LL | s.read(&mut buf).is_ok(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +106,7 @@ LL | s.read(&mut buf).is_ok(); = help: use `Read::read_exact` instead, or handle partial reads error: read amount is not handled - --> $DIR/unused_io_amount.rs:84:5 + --> $DIR/unused_io_amount.rs:85:5 | LL | s.read(&mut buf).is_err(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ LL | s.read(&mut buf).is_err(); = help: use `Read::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:89:5 + --> $DIR/unused_io_amount.rs:90:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | w.write(b"hello world").await.unwrap(); = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:95:5 + --> $DIR/unused_io_amount.rs:96:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -130,7 +130,15 @@ LL | r.read(&mut buf[..]).await.unwrap(); = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:109:9 + --> $DIR/unused_io_amount.rs:104:5 + | +LL | w.write(b"hello world"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `AsyncWriteExt::write_all` instead, or handle partial writes + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:110:9 | LL | w.write(b"hello world").await?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,7 +146,7 @@ LL | w.write(b"hello world").await?; = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:118:9 + --> $DIR/unused_io_amount.rs:119:9 | LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -146,7 +154,7 @@ LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:127:5 + --> $DIR/unused_io_amount.rs:128:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,12 +162,103 @@ LL | w.write(b"hello world").await.unwrap(); = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:133:5 + --> $DIR/unused_io_amount.rs:134:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: aborting due to 20 previous errors +error: written amount is not handled + --> $DIR/unused_io_amount.rs:147:11 + | +LL | match s.write(b"test") { + | ^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:149:9 + | +LL | Ok(_) => todo!(), + | ^^^^^^^^^^^^^^^^ + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:155:11 + | +LL | match s.read(&mut buf) { + | ^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:157:9 + | +LL | Ok(_) => todo!(), + | ^^^^^^^^^^^^^^^^ + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:164:11 + | +LL | match s.read(&mut [0u8; 4]) { + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:166:9 + | +LL | Ok(_) => todo!(), + | ^^^^^^^^^^^^^^^^ + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:173:11 + | +LL | match s.write(b"test") { + | ^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:175:9 + | +LL | Ok(_) => todo!(), + | ^^^^^^^^^^^^^^^^ + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:186:8 + | +LL | if let Ok(_) = s.read(&mut [0u8; 4]) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:186:12 + | +LL | if let Ok(_) = s.read(&mut [0u8; 4]) { + | ^^^^^ + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:193:8 + | +LL | if let Ok(_) = s.write(b"test") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:193:12 + | +LL | if let Ok(_) = s.write(b"test") { + | ^^^^^ + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:200:8 + | +LL | if let Ok(..) = s.write(b"test") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes +note: the result is consumed here, but the amount of I/O bytes remains unhandled + --> $DIR/unused_io_amount.rs:200:12 + | +LL | if let Ok(..) = s.write(b"test") { + | ^^^^^^ + +error: aborting due to 28 previous errors diff --git a/tests/ui/useless_asref.fixed b/tests/ui/useless_asref.fixed index 88b95095bc0f0..c98f2928e03cc 100644 --- a/tests/ui/useless_asref.fixed +++ b/tests/ui/useless_asref.fixed @@ -144,6 +144,42 @@ fn foo() { //~^ ERROR: this call to `as_ref.map(...)` does nothing } +mod issue12135 { + pub struct Struct { + field: Option, + } + + #[derive(Clone)] + pub struct Foo; + + #[derive(Clone)] + struct InnerStruct { + x: Foo, + } + + impl InnerStruct { + fn method(&self) -> &Foo { + &self.x + } + } + + pub fn f(x: &Struct) -> Option { + x.field.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + x.field.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + x.field.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + + // https://github.com/rust-lang/rust-clippy/pull/12136#discussion_r1451565223 + #[allow(clippy::clone_on_copy)] + Some(1).clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + + x.field.as_ref().map(|v| v.method().clone()) + } +} + fn main() { not_ok(); ok(); diff --git a/tests/ui/useless_asref.rs b/tests/ui/useless_asref.rs index 504dc1f5cbf90..f9d603f116de5 100644 --- a/tests/ui/useless_asref.rs +++ b/tests/ui/useless_asref.rs @@ -144,6 +144,42 @@ fn foo() { //~^ ERROR: this call to `as_ref.map(...)` does nothing } +mod issue12135 { + pub struct Struct { + field: Option, + } + + #[derive(Clone)] + pub struct Foo; + + #[derive(Clone)] + struct InnerStruct { + x: Foo, + } + + impl InnerStruct { + fn method(&self) -> &Foo { + &self.x + } + } + + pub fn f(x: &Struct) -> Option { + x.field.as_ref().map(|v| v.clone()); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + x.field.as_ref().map(Clone::clone); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + x.field.as_ref().map(|v| Clone::clone(v)); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + + // https://github.com/rust-lang/rust-clippy/pull/12136#discussion_r1451565223 + #[allow(clippy::clone_on_copy)] + Some(1).as_ref().map(|&x| x.clone()); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + + x.field.as_ref().map(|v| v.method().clone()) + } +} + fn main() { not_ok(); ok(); diff --git a/tests/ui/useless_asref.stderr b/tests/ui/useless_asref.stderr index deb5d90f2f62f..e158df2664d77 100644 --- a/tests/ui/useless_asref.stderr +++ b/tests/ui/useless_asref.stderr @@ -88,5 +88,29 @@ error: this call to `as_ref.map(...)` does nothing LL | let z = x.as_ref().map(|z| String::clone(z)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` -error: aborting due to 14 previous errors +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:167:9 + | +LL | x.field.as_ref().map(|v| v.clone()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()` + +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:169:9 + | +LL | x.field.as_ref().map(Clone::clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()` + +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:171:9 + | +LL | x.field.as_ref().map(|v| Clone::clone(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.field.clone()` + +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:176:9 + | +LL | Some(1).as_ref().map(|&x| x.clone()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(1).clone()` + +error: aborting due to 18 previous errors diff --git a/triagebot.toml b/triagebot.toml index a05765b398133..3eadc66f544f3 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -19,7 +19,6 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" -users_on_vacation = ["blyxyas"] [assign.owners] "/.github" = ["@flip1995"] From 42d13f8eb0b5ed893ffd92c81f10c43499cb3cad Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 20 Jan 2024 20:26:06 +0100 Subject: [PATCH 18/68] [`unconditional_recursion`]: compare by types instead of `DefId`s --- clippy_lints/src/unconditional_recursion.rs | 86 +++++++++++---------- tests/ui/unconditional_recursion.rs | 35 +++++++++ 2 files changed, 82 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index 209035804e43e..224ec475c5107 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -69,14 +69,6 @@ fn span_error(cx: &LateContext<'_>, method_span: Span, expr: &Expr<'_>) { ); } -fn get_ty_def_id(ty: Ty<'_>) -> Option { - match ty.peel_refs().kind() { - ty::Adt(adt, _) => Some(adt.did()), - ty::Foreign(def_id) => Some(*def_id), - _ => None, - } -} - fn get_hir_ty_def_id<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: rustc_hir::Ty<'tcx>) -> Option { let TyKind::Path(qpath) = hir_ty.kind else { return None }; match qpath { @@ -131,21 +123,49 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt } } -#[allow(clippy::unnecessary_def_path)] +/// When we have `x == y` where `x = &T` and `y = &T`, then that resolves to +/// `<&T as PartialEq<&T>>::eq`, which is not the same as `>::eq`, +/// however we still would want to treat it the same, because we know that it's a blanket impl +/// that simply delegates to the `PartialEq` impl with one reference removed. +/// +/// Still, we can't just do `lty.peel_refs() == rty.peel_refs()` because when we have `x = &T` and +/// `y = &&T`, this is not necessarily the same as `>::eq` +/// +/// So to avoid these FNs and FPs, we keep removing a layer of references from *both* sides +/// until both sides match the expected LHS and RHS type (or they don't). +fn matches_ty<'tcx>( + mut left: Ty<'tcx>, + mut right: Ty<'tcx>, + expected_left: Ty<'tcx>, + expected_right: Ty<'tcx>, +) -> bool { + while let (&ty::Ref(_, lty, _), &ty::Ref(_, rty, _)) = (left.kind(), right.kind()) { + if lty == expected_left && rty == expected_right { + return true; + } + left = lty; + right = rty; + } + false +} + fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) { - let args = cx - .tcx - .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(method_def_id).skip_binder()) - .inputs(); + let Some(sig) = cx + .typeck_results() + .liberated_fn_sigs() + .get(cx.tcx.local_def_id_to_hir_id(method_def_id)) + else { + return; + }; + // That has two arguments. - if let [self_arg, other_arg] = args - && let Some(self_arg) = get_ty_def_id(*self_arg) - && let Some(other_arg) = get_ty_def_id(*other_arg) + if let [self_arg, other_arg] = sig.inputs() + && let &ty::Ref(_, self_arg, _) = self_arg.kind() + && let &ty::Ref(_, other_arg, _) = other_arg.kind() // The two arguments are of the same type. - && self_arg == other_arg && let Some(trait_def_id) = get_impl_trait_def_id(cx, method_def_id) // The trait is `PartialEq`. - && Some(trait_def_id) == get_trait_def_id(cx, &["core", "cmp", "PartialEq"]) + && cx.tcx.is_diagnostic_item(sym::PartialEq, trait_def_id) { let to_check_op = if name.name == sym::eq { BinOpKind::Eq @@ -154,31 +174,19 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca }; let is_bad = match expr.kind { ExprKind::Binary(op, left, right) if op.node == to_check_op => { - // Then we check if the left-hand element is of the same type as `self`. - if let Some(left_ty) = cx.typeck_results().expr_ty_opt(left) - && let Some(left_id) = get_ty_def_id(left_ty) - && self_arg == left_id - && let Some(right_ty) = cx.typeck_results().expr_ty_opt(right) - && let Some(right_id) = get_ty_def_id(right_ty) - && other_arg == right_id - { - true - } else { - false - } + // Then we check if the LHS matches self_arg and RHS matches other_arg + let left_ty = cx.typeck_results().expr_ty_adjusted(left); + let right_ty = cx.typeck_results().expr_ty_adjusted(right); + matches_ty(left_ty, right_ty, self_arg, other_arg) }, - ExprKind::MethodCall(segment, receiver, &[_arg], _) if segment.ident.name == name.name => { - if let Some(ty) = cx.typeck_results().expr_ty_opt(receiver) - && let Some(ty_id) = get_ty_def_id(ty) - && self_arg != ty_id - { - // Since this called on a different type, the lint should not be - // triggered here. - return; - } + ExprKind::MethodCall(segment, receiver, [arg], _) if segment.ident.name == name.name => { + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver); + let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); + if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) && trait_id == trait_def_id + && matches_ty(receiver_ty, arg_ty, self_arg, other_arg) { true } else { diff --git a/tests/ui/unconditional_recursion.rs b/tests/ui/unconditional_recursion.rs index 7b898a6e0e784..2ad7c410da59d 100644 --- a/tests/ui/unconditional_recursion.rs +++ b/tests/ui/unconditional_recursion.rs @@ -288,4 +288,39 @@ impl PartialEq for S15<'_> { } } +mod issue12154 { + struct MyBox(T); + + impl std::ops::Deref for MyBox { + type Target = T; + fn deref(&self) -> &T { + &self.0 + } + } + + impl PartialEq for MyBox { + fn eq(&self, other: &Self) -> bool { + (**self).eq(&**other) + } + } + + // Not necessarily related to the issue but another FP from the http crate that was fixed with it: + // https://docs.rs/http/latest/src/http/header/name.rs.html#1424 + // We used to simply peel refs from the LHS and RHS, so we couldn't differentiate + // between `PartialEq for &T` and `PartialEq<&T> for T` impls. + #[derive(PartialEq)] + struct HeaderName; + impl<'a> PartialEq<&'a HeaderName> for HeaderName { + fn eq(&self, other: &&'a HeaderName) -> bool { + *self == **other + } + } + + impl<'a> PartialEq for &'a HeaderName { + fn eq(&self, other: &HeaderName) -> bool { + *other == *self + } + } +} + fn main() {} From 87a6300b22725bfea57f0e4fb95ccf70ed7d8cc8 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 21 Jan 2024 13:33:03 +0100 Subject: [PATCH 19/68] add a test for rust-lang/rust-clippy#12181 --- tests/ui/unconditional_recursion.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/ui/unconditional_recursion.rs b/tests/ui/unconditional_recursion.rs index 2ad7c410da59d..263fdf26d4d2f 100644 --- a/tests/ui/unconditional_recursion.rs +++ b/tests/ui/unconditional_recursion.rs @@ -321,6 +321,30 @@ mod issue12154 { *other == *self } } + + // Issue #12181 but also fixed by the same PR + struct Foo; + + impl Foo { + fn as_str(&self) -> &str { + "Foo" + } + } + + impl PartialEq for Foo { + fn eq(&self, other: &Self) -> bool { + self.as_str().eq(other.as_str()) + } + } + + impl PartialEq for Foo + where + for<'a> &'a str: PartialEq, + { + fn eq(&self, other: &T) -> bool { + (&self.as_str()).eq(other) + } + } } fn main() {} From fd3e966bdded5d556d01f1db5c3d6a78539661f8 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 26 Jan 2024 00:23:13 +0100 Subject: [PATCH 20/68] avoid linting on `#[track_caller]` functions in `redundant_closure` --- clippy_lints/src/eta_reduction.rs | 14 +++++++++++--- tests/ui/eta.fixed | 17 +++++++++++++++++ tests/ui/eta.rs | 17 +++++++++++++++++ tests/ui/eta.stderr | 2 +- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 450cee4007c72..1ea5b78980589 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -21,8 +21,8 @@ use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; declare_clippy_lint! { /// ### What it does /// Checks for closures which just call another function where - /// the function can be called directly. `unsafe` functions or calls where types - /// get adjusted are ignored. + /// the function can be called directly. `unsafe` functions, calls where types + /// get adjusted or where the callee is marked `#[track_caller]` are ignored. /// /// ### Why is this bad? /// Needlessly creating a closure adds code for no benefit @@ -136,7 +136,14 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { .map_or(callee_ty, |a| a.target.peel_refs()); let sig = match callee_ty_adjusted.kind() { - ty::FnDef(def, _) => cx.tcx.fn_sig(def).skip_binder().skip_binder(), + ty::FnDef(def, _) => { + // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location` + if cx.tcx.has_attr(*def, sym::track_caller) { + return; + } + + cx.tcx.fn_sig(def).skip_binder().skip_binder() + }, ty::FnPtr(sig) => sig.skip_binder(), ty::Closure(_, subs) => cx .tcx @@ -186,6 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { }, ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id) + && !cx.tcx.has_attr(method_def_id, sym::track_caller) && check_sig(cx, closure, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) { span_lint_and_then( diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 32c7499bf73f6..3aeb4dae30b26 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -346,6 +346,23 @@ fn angle_brackets_and_args() { dyn_opt.map(::method_on_dyn); } +// https://github.com/rust-lang/rust-clippy/issues/12199 +fn track_caller_fp() { + struct S; + impl S { + #[track_caller] + fn add_location(self) {} + } + + #[track_caller] + fn add_location() {} + + fn foo(_: fn()) {} + fn foo2(_: fn(S)) {} + foo(|| add_location()); + foo2(|s| s.add_location()); +} + fn _late_bound_to_early_bound_regions() { struct Foo<'a>(&'a u32); impl<'a> Foo<'a> { diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 25b7431ba8cd7..b9b3303b371de 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -346,6 +346,23 @@ fn angle_brackets_and_args() { dyn_opt.map(|d| d.method_on_dyn()); } +// https://github.com/rust-lang/rust-clippy/issues/12199 +fn track_caller_fp() { + struct S; + impl S { + #[track_caller] + fn add_location(self) {} + } + + #[track_caller] + fn add_location() {} + + fn foo(_: fn()) {} + fn foo2(_: fn(S)) {} + foo(|| add_location()); + foo2(|s| s.add_location()); +} + fn _late_bound_to_early_bound_regions() { struct Foo<'a>(&'a u32); impl<'a> Foo<'a> { diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 951e4ac749c25..7c4d2d7093edc 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -161,7 +161,7 @@ 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:389:19 + --> $DIR/eta.rs:406:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` From 9ce0b837ad1a18770d0f261093c68ee351b91cc7 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 26 Jan 2024 09:42:12 +0100 Subject: [PATCH 21/68] Clippy: Fix empty suggestion in from_over_into Co-authored-by: y21 <30553356+y21@users.noreply.github.com> --- clippy_lints/src/from_over_into.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 93527bcdf5cee..1933a00891b0b 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -181,9 +181,6 @@ fn convert_to_from( let from = snippet_opt(cx, self_ty.span)?; let into = snippet_opt(cx, target_ty.span)?; - let return_type = matches!(sig.decl.output, FnRetTy::Return(_)) - .then_some(String::from("Self")) - .unwrap_or_default(); let mut suggestions = vec![ // impl Into for U -> impl From for U // ~~~~ ~~~~ @@ -200,10 +197,13 @@ fn convert_to_from( // fn into([mut] self) -> T -> fn into([mut] v: T) -> T // ~~~~ ~~~~ (self_ident.span, format!("val: {from}")), + ]; + + if let FnRetTy::Return(_) = sig.decl.output { // fn into(self) -> T -> fn into(self) -> Self // ~ ~~~~ - (sig.decl.output.span(), return_type), - ]; + suggestions.push((sig.decl.output.span(), String::from("Self"))); + } let mut finder = SelfFinder { cx, From 14e15206ed1ce8b50c4d2038f10d2793faf87723 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 17 Jan 2024 15:56:53 +0100 Subject: [PATCH 22/68] Warn if an item coming from more recent version than MSRV is used --- CHANGELOG.md | 1 + clippy_config/src/msrvs.rs | 11 ++ clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/incompatible_msrv.rs | 133 ++++++++++++++++++ clippy_lints/src/lib.rs | 3 + .../min_rust_version/min_rust_version.fixed | 2 +- .../min_rust_version/min_rust_version.rs | 2 +- tests/ui/incompatible_msrv.rs | 23 +++ tests/ui/incompatible_msrv.stderr | 23 +++ 9 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/incompatible_msrv.rs create mode 100644 tests/ui/incompatible_msrv.rs create mode 100644 tests/ui/incompatible_msrv.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fa45ceeb39be..7db5d4c45dfde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5198,6 +5198,7 @@ Released 2018-09-13 [`implied_bounds_in_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#implied_bounds_in_impls [`impossible_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#impossible_comparisons [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops +[`incompatible_msrv`]: https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping [`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor [`incorrect_clone_impl_on_copy_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#incorrect_clone_impl_on_copy_type diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index 72d5b9aff28df..856644b85683d 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -3,6 +3,7 @@ use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::{sym, Symbol}; use serde::Deserialize; +use std::fmt; macro_rules! msrv_aliases { ($($major:literal,$minor:literal,$patch:literal { @@ -58,6 +59,16 @@ pub struct Msrv { stack: Vec, } +impl fmt::Display for Msrv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(msrv) = self.current() { + write!(f, "{msrv}") + } else { + f.write_str("1.0.0") + } + } +} + impl<'de> Deserialize<'de> for Msrv { fn deserialize(deserializer: D) -> Result where diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 639edd8da3012..5342a6722db33 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -212,6 +212,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO, crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO, + crate::incompatible_msrv::INCOMPATIBLE_MSRV_INFO, crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO, crate::indexing_slicing::INDEXING_SLICING_INFO, diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs new file mode 100644 index 0000000000000..f2f0e7d426628 --- /dev/null +++ b/clippy_lints/src/incompatible_msrv.rs @@ -0,0 +1,133 @@ +use clippy_config::msrvs::Msrv; +use clippy_utils::diagnostics::span_lint; +use rustc_attr::{StabilityLevel, StableSince}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; +use rustc_semver::RustcVersion; +use rustc_session::impl_lint_pass; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// + /// This lint checks that no function newer than the defined MSRV (minimum + /// supported rust version) is used in the crate. + /// + /// ### Why is this bad? + /// + /// It would prevent the crate to be actually used with the specified MSRV. + /// + /// ### Example + /// ```no_run + /// // MSRV of 1.3.0 + /// use std::thread::sleep; + /// use std::time::Duration; + /// + /// // Sleep was defined in `1.4.0`. + /// sleep(Duration::new(1, 0)); + /// ``` + /// + /// To fix this problem, either increase your MSRV or use another item + /// available in your current MSRV. + #[clippy::version = "1.77.0"] + pub INCOMPATIBLE_MSRV, + suspicious, + "ensures that all items used in the crate are available for the current MSRV" +} + +pub struct IncompatibleMsrv { + msrv: Msrv, + is_above_msrv: FxHashMap, +} + +impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]); + +impl IncompatibleMsrv { + pub fn new(msrv: Msrv) -> Self { + Self { + msrv, + is_above_msrv: FxHashMap::default(), + } + } + + #[allow(clippy::cast_lossless)] + fn get_def_id_version(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> RustcVersion { + if let Some(version) = self.is_above_msrv.get(&def_id) { + return *version; + } + let version = if let Some(version) = tcx + .lookup_stability(def_id) + .and_then(|stability| match stability.level { + StabilityLevel::Stable { + since: StableSince::Version(version), + .. + } => Some(RustcVersion::new( + version.major as _, + version.minor as _, + version.patch as _, + )), + _ => None, + }) { + version + } else if let Some(parent_def_id) = tcx.opt_parent(def_id) { + self.get_def_id_version(tcx, parent_def_id) + } else { + RustcVersion::new(1, 0, 0) + }; + self.is_above_msrv.insert(def_id, version); + version + } + + fn emit_lint_if_under_msrv(&mut self, cx: &LateContext<'_>, def_id: DefId, span: Span) { + if def_id.is_local() { + // We don't check local items since their MSRV is supposed to always be valid. + return; + } + let version = self.get_def_id_version(cx.tcx, def_id); + if self.msrv.meets(version) { + return; + } + self.emit_lint_for(cx, span, version); + } + + fn emit_lint_for(&self, cx: &LateContext<'_>, span: Span, version: RustcVersion) { + span_lint( + cx, + INCOMPATIBLE_MSRV, + span, + &format!( + "current MSRV (Minimum Supported Rust Version) is `{}` but this item is stable since `{version}`", + self.msrv + ), + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { + extract_msrv_attr!(LateContext); + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if self.msrv.current().is_none() { + // If there is no MSRV, then no need to check anything... + return; + } + match expr.kind { + ExprKind::MethodCall(_, _, _, span) => { + if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + self.emit_lint_if_under_msrv(cx, method_did, span); + } + }, + ExprKind::Call(call, [_]) => { + if let ExprKind::Path(qpath) = call.kind + && let Some(path_def_id) = cx.qpath_res(&qpath, call.hir_id).opt_def_id() + { + self.emit_lint_if_under_msrv(cx, path_def_id, call.span); + } + }, + _ => {}, + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index feb4d188f3978..907ff93829303 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -26,6 +26,7 @@ extern crate rustc_abi; extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; +extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; @@ -153,6 +154,7 @@ mod implicit_return; mod implicit_saturating_add; mod implicit_saturating_sub; mod implied_bounds_in_impls; +mod incompatible_msrv; mod inconsistent_struct_constructor; mod index_refutable_slice; mod indexing_slicing; @@ -1094,6 +1096,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| { Box::new(thread_local_initializer_can_be_made_const::ThreadLocalInitializerCanBeMadeConst::new(msrv())) }); + store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui-toml/min_rust_version/min_rust_version.fixed b/tests/ui-toml/min_rust_version/min_rust_version.fixed index 6c58e07d846ea..497f783087a10 100644 --- a/tests/ui-toml/min_rust_version/min_rust_version.fixed +++ b/tests/ui-toml/min_rust_version/min_rust_version.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::redundant_clone, clippy::unnecessary_operation)] +#![allow(clippy::redundant_clone, clippy::unnecessary_operation, clippy::incompatible_msrv)] #![warn(clippy::manual_non_exhaustive, clippy::borrow_as_ptr, clippy::manual_bits)] use std::mem::{size_of, size_of_val}; diff --git a/tests/ui-toml/min_rust_version/min_rust_version.rs b/tests/ui-toml/min_rust_version/min_rust_version.rs index e1dc3f4389c0b..6e7874108a34c 100644 --- a/tests/ui-toml/min_rust_version/min_rust_version.rs +++ b/tests/ui-toml/min_rust_version/min_rust_version.rs @@ -1,4 +1,4 @@ -#![allow(clippy::redundant_clone, clippy::unnecessary_operation)] +#![allow(clippy::redundant_clone, clippy::unnecessary_operation, clippy::incompatible_msrv)] #![warn(clippy::manual_non_exhaustive, clippy::borrow_as_ptr, clippy::manual_bits)] use std::mem::{size_of, size_of_val}; diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs new file mode 100644 index 0000000000000..a92017fb0f626 --- /dev/null +++ b/tests/ui/incompatible_msrv.rs @@ -0,0 +1,23 @@ +#![warn(clippy::incompatible_msrv)] +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.3.0"] + +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::thread::sleep; +use std::time::Duration; + +fn foo() { + let mut map: HashMap<&str, u32> = HashMap::new(); + assert_eq!(map.entry("poneyland").key(), &"poneyland"); + //~^ ERROR: is `1.3.0` but this item is stable since `1.10.0` + if let Entry::Vacant(v) = map.entry("poneyland") { + v.into_key(); + //~^ ERROR: is `1.3.0` but this item is stable since `1.12.0` + } + // Should warn for `sleep` but not for `Duration` (which was added in `1.3.0`). + sleep(Duration::new(1, 0)); + //~^ ERROR: is `1.3.0` but this item is stable since `1.4.0` +} + +fn main() {} diff --git a/tests/ui/incompatible_msrv.stderr b/tests/ui/incompatible_msrv.stderr new file mode 100644 index 0000000000000..bd5ecd6ed2ffb --- /dev/null +++ b/tests/ui/incompatible_msrv.stderr @@ -0,0 +1,23 @@ +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.10.0` + --> $DIR/incompatible_msrv.rs:12:39 + | +LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); + | ^^^^^ + | + = note: `-D clippy::incompatible-msrv` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::incompatible_msrv)]` + +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.12.0` + --> $DIR/incompatible_msrv.rs:15:11 + | +LL | v.into_key(); + | ^^^^^^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.4.0` + --> $DIR/incompatible_msrv.rs:19:5 + | +LL | sleep(Duration::new(1, 0)); + | ^^^^^ + +error: aborting due to 3 previous errors + From 1d94cc3895d5f2666eda129ba993840ec4f82852 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jan 2024 14:39:51 +0100 Subject: [PATCH 23/68] remove illegal_floating_point_literal_pattern lint --- tests/ui/expect_tool_lint_rfc_2383.rs | 16 ++++------------ tests/ui/expect_tool_lint_rfc_2383.stderr | 16 ++++++++-------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/tests/ui/expect_tool_lint_rfc_2383.rs b/tests/ui/expect_tool_lint_rfc_2383.rs index 3811421dc7158..72097bfabd726 100644 --- a/tests/ui/expect_tool_lint_rfc_2383.rs +++ b/tests/ui/expect_tool_lint_rfc_2383.rs @@ -20,12 +20,8 @@ mod rustc_ok { pub fn rustc_lints() { let x = 42.0; - #[expect(illegal_floating_point_literal_pattern)] - match x { - 5.0 => {} - 6.0 => {} - _ => {} - } + #[expect(invalid_nan_comparisons)] + let _b = x == f32::NAN; } } @@ -38,13 +34,9 @@ mod rustc_warn { pub fn rustc_lints() { let x = 42; - #[expect(illegal_floating_point_literal_pattern)] + #[expect(invalid_nan_comparisons)] //~^ ERROR: this lint expectation is unfulfilled - match x { - 5 => {} - 6 => {} - _ => {} - } + let _b = x == 5; } } diff --git a/tests/ui/expect_tool_lint_rfc_2383.stderr b/tests/ui/expect_tool_lint_rfc_2383.stderr index 3f8d0b7243620..2a418d8456635 100644 --- a/tests/ui/expect_tool_lint_rfc_2383.stderr +++ b/tests/ui/expect_tool_lint_rfc_2383.stderr @@ -1,5 +1,5 @@ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:35:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:31:14 | LL | #[expect(dead_code)] | ^^^^^^^^^ @@ -8,31 +8,31 @@ LL | #[expect(dead_code)] = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:41:18 + --> $DIR/expect_tool_lint_rfc_2383.rs:37:18 | -LL | #[expect(illegal_floating_point_literal_pattern)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[expect(invalid_nan_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:116:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:108:14 | LL | #[expect(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:124:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:116:14 | LL | #[expect(clippy::bytes_nth)] | ^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:130:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:122:14 | LL | #[expect(clippy::if_same_then_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:136:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:128:14 | LL | #[expect(clippy::overly_complex_bool_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From f58950de86f36ac41a7eed134529c76e4da3a552 Mon Sep 17 00:00:00 2001 From: Quinn Sinclair Date: Fri, 26 Jan 2024 23:48:47 +0100 Subject: [PATCH 24/68] correct lint case --- .../needless_return_with_question_mark.fixed | 18 ++++++++++++++++++ tests/ui/needless_return_with_question_mark.rs | 17 +++++++++++++++++ .../needless_return_with_question_mark.stderr | 8 +++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/ui/needless_return_with_question_mark.fixed b/tests/ui/needless_return_with_question_mark.fixed index 8a97d8604a559..2b0a835bc7f07 100644 --- a/tests/ui/needless_return_with_question_mark.fixed +++ b/tests/ui/needless_return_with_question_mark.fixed @@ -104,3 +104,21 @@ fn issue11982() { Ok(()) } } + +fn issue11982_no_conversion() { + mod bar { + pub struct Error; + pub fn foo(_: bool) -> Result<(), Error> { + Ok(()) + } + } + + fn foo(ok: bool) -> Result<(), bar::Error> { + if !ok { + bar::foo(ok).map(|_| Ok::<(), bar::Error>(()))?; + //~^ ERROR: unneeded `return` statement with `?` operator + }; + Ok(()) + } + +} diff --git a/tests/ui/needless_return_with_question_mark.rs b/tests/ui/needless_return_with_question_mark.rs index be15b000f5d5d..ecbf00254f06b 100644 --- a/tests/ui/needless_return_with_question_mark.rs +++ b/tests/ui/needless_return_with_question_mark.rs @@ -104,3 +104,20 @@ fn issue11982() { Ok(()) } } + +fn issue11982_no_conversion() { + mod bar { + pub struct Error; + pub fn foo(_: bool) -> Result<(), Error> { + Ok(()) + } + } + + fn foo(ok: bool) -> Result<(), bar::Error> { + if !ok { + return bar::foo(ok).map(|_| Ok::<(), bar::Error>(()))?; + //~^ ERROR: unneeded `return` statement with `?` operator + }; + Ok(()) + } +} diff --git a/tests/ui/needless_return_with_question_mark.stderr b/tests/ui/needless_return_with_question_mark.stderr index 17aa212ae8d7a..59c212bcbb341 100644 --- a/tests/ui/needless_return_with_question_mark.stderr +++ b/tests/ui/needless_return_with_question_mark.stderr @@ -13,5 +13,11 @@ error: unneeded `return` statement with `?` operator LL | return Err(())?; | ^^^^^^^ help: remove it -error: aborting due to 2 previous errors +error: unneeded `return` statement with `?` operator + --> $DIR/needless_return_with_question_mark.rs:118:13 + | +LL | return bar::foo(ok).map(|_| Ok::<(), bar::Error>(()))?; + | ^^^^^^^ help: remove it + +error: aborting due to 3 previous errors From 6d76d145653bbe05e3ac49ea12626f52a1582970 Mon Sep 17 00:00:00 2001 From: Andrew Banchich Date: Wed, 10 Jan 2024 18:52:20 -0500 Subject: [PATCH 25/68] add to_string_trait_impl lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/returns.rs | 15 +- clippy_lints/src/to_string_trait_impl.rs | 67 +++++++ tests/ui/format_args.fixed | 1 + tests/ui/format_args.rs | 1 + tests/ui/format_args.stderr | 50 +++--- tests/ui/to_string_trait_impl.rs | 31 ++++ tests/ui/to_string_trait_impl.stderr | 16 ++ tests/ui/unconditional_recursion.rs | 3 + tests/ui/unconditional_recursion.stderr | 14 +- tests/ui/unnecessary_to_owned.fixed | 3 + tests/ui/unnecessary_to_owned.rs | 3 + tests/ui/unnecessary_to_owned.stderr | 170 +++++++++--------- tests/ui/unnecessary_to_owned_on_split.fixed | 1 + tests/ui/unnecessary_to_owned_on_split.rs | 1 + tests/ui/unnecessary_to_owned_on_split.stderr | 18 +- 18 files changed, 265 insertions(+), 133 deletions(-) create mode 100644 clippy_lints/src/to_string_trait_impl.rs create mode 100644 tests/ui/to_string_trait_impl.rs create mode 100644 tests/ui/to_string_trait_impl.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 7db5d4c45dfde..927697a7c0f62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5623,6 +5623,7 @@ Released 2018-09-13 [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args +[`to_string_trait_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_trait_impl [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5342a6722db33..7d6f3f3458ed2 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -657,6 +657,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, crate::thread_local_initializer_can_be_made_const::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, + crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO, crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 907ff93829303..10fe5ffebf476 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -327,6 +327,7 @@ mod temporary_assignment; mod tests_outside_test_module; mod thread_local_initializer_can_be_made_const; mod to_digit_is_some; +mod to_string_trait_impl; mod trailing_empty_array; mod trait_bounds; mod transmute; @@ -1097,6 +1098,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { Box::new(thread_local_initializer_can_be_made_const::ThreadLocalInitializerCanBeMadeConst::new(msrv())) }); store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); + store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2293b53b42b91..bb49985c16256 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -18,6 +18,7 @@ use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{BytePos, Pos, Span}; use std::borrow::Cow; +use std::fmt::Display; declare_clippy_lint! { /// ### What it does @@ -146,14 +147,14 @@ impl<'tcx> RetReplacement<'tcx> { } } -impl<'tcx> ToString for RetReplacement<'tcx> { - fn to_string(&self) -> String { +impl<'tcx> Display for RetReplacement<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Empty => String::new(), - Self::Block => "{}".to_string(), - Self::Unit => "()".to_string(), - Self::IfSequence(inner, _) => format!("({inner})"), - Self::Expr(inner, _) => inner.to_string(), + Self::Empty => write!(f, ""), + Self::Block => write!(f, "{{}}"), + Self::Unit => write!(f, "()"), + Self::IfSequence(inner, _) => write!(f, "({inner})"), + Self::Expr(inner, _) => write!(f, "{inner}"), } } } diff --git a/clippy_lints/src/to_string_trait_impl.rs b/clippy_lints/src/to_string_trait_impl.rs new file mode 100644 index 0000000000000..e1cea99085fde --- /dev/null +++ b/clippy_lints/src/to_string_trait_impl.rs @@ -0,0 +1,67 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for direct implementations of `ToString`. + /// ### Why is this bad? + /// This trait is automatically implemented for any type which implements the `Display` trait. + /// As such, `ToString` shouldnโ€™t be implemented directly: `Display` should be implemented instead, + /// and you get the `ToString` implementation for free. + /// ### Example + /// ```no_run + /// struct Point { + /// x: usize, + /// y: usize, + /// } + /// + /// impl ToString for Point { + /// fn to_string(&self) -> String { + /// format!("({}, {})", self.x, self.y) + /// } + /// } + /// ``` + /// Use instead: + /// ```no_run + /// struct Point { + /// x: usize, + /// y: usize, + /// } + /// + /// impl std::fmt::Display for Point { + /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// write!(f, "({}, {})", self.x, self.y) + /// } + /// } + /// ``` + #[clippy::version = "1.77.0"] + pub TO_STRING_TRAIT_IMPL, + style, + "check for direct implementations of `ToString`" +} + +declare_lint_pass!(ToStringTraitImpl => [TO_STRING_TRAIT_IMPL]); + +impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl { + fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'tcx>) { + if let ItemKind::Impl(Impl { + of_trait: Some(trait_ref), + .. + }) = it.kind + && let Some(trait_did) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::ToString, trait_did) + { + span_lint_and_help( + cx, + TO_STRING_TRAIT_IMPL, + it.span, + "direct implementation of `ToString`", + None, + "prefer implementing `Display` instead", + ); + } + } +} diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed index ddd5976c408a3..cab20b11e0731 100644 --- a/tests/ui/format_args.fixed +++ b/tests/ui/format_args.fixed @@ -14,6 +14,7 @@ use std::panic::Location; struct Somewhere; +#[allow(clippy::to_string_trait_impl)] impl ToString for Somewhere { fn to_string(&self) -> String { String::from("somewhere") diff --git a/tests/ui/format_args.rs b/tests/ui/format_args.rs index 18e1bc1af67f1..bc3645cb2c2bc 100644 --- a/tests/ui/format_args.rs +++ b/tests/ui/format_args.rs @@ -14,6 +14,7 @@ use std::panic::Location; struct Somewhere; +#[allow(clippy::to_string_trait_impl)] impl ToString for Somewhere { fn to_string(&self) -> String { String::from("somewhere") diff --git a/tests/ui/format_args.stderr b/tests/ui/format_args.stderr index dcdfa668aff30..2f1714296d6c8 100644 --- a/tests/ui/format_args.stderr +++ b/tests/ui/format_args.stderr @@ -1,5 +1,5 @@ error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:76:72 + --> $DIR/format_args.rs:77:72 | LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this @@ -8,145 +8,145 @@ LL | let _ = format!("error: something failed at {}", Location::caller().to_ = help: to override `-D warnings` add `#[allow(clippy::to_string_in_format_args)]` error: `to_string` applied to a type that implements `Display` in `write!` args - --> $DIR/format_args.rs:80:27 + --> $DIR/format_args.rs:81:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `writeln!` args - --> $DIR/format_args.rs:85:27 + --> $DIR/format_args.rs:86:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:87:63 + --> $DIR/format_args.rs:88:63 | LL | print!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:88:65 + --> $DIR/format_args.rs:89:65 | LL | println!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprint!` args - --> $DIR/format_args.rs:89:64 + --> $DIR/format_args.rs:90:64 | LL | eprint!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprintln!` args - --> $DIR/format_args.rs:90:66 + --> $DIR/format_args.rs:91:66 | LL | eprintln!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format_args!` args - --> $DIR/format_args.rs:91:77 + --> $DIR/format_args.rs:92:77 | LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert!` args - --> $DIR/format_args.rs:92:70 + --> $DIR/format_args.rs:93:70 | LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_eq!` args - --> $DIR/format_args.rs:93:73 + --> $DIR/format_args.rs:94:73 | LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_ne!` args - --> $DIR/format_args.rs:94:73 + --> $DIR/format_args.rs:95:73 | LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `panic!` args - --> $DIR/format_args.rs:95:63 + --> $DIR/format_args.rs:96:63 | LL | panic!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:96:20 + --> $DIR/format_args.rs:97:20 | LL | println!("{}", X(1).to_string()); | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:97:20 + --> $DIR/format_args.rs:98:20 | LL | println!("{}", Y(&X(1)).to_string()); | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:98:24 + --> $DIR/format_args.rs:99:24 | LL | println!("{}", Z(1).to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:99:20 + --> $DIR/format_args.rs:100:20 | LL | println!("{}", x.to_string()); | ^^^^^^^^^^^^^ help: use this: `**x` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:100:20 + --> $DIR/format_args.rs:101:20 | LL | println!("{}", x_ref.to_string()); | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:102:39 + --> $DIR/format_args.rs:103:39 | LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:103:52 + --> $DIR/format_args.rs:104:52 | LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:104:39 + --> $DIR/format_args.rs:105:39 | LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:105:52 + --> $DIR/format_args.rs:106:52 | LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:117:37 + --> $DIR/format_args.rs:118:37 | LL | print!("{}", (Location::caller().to_string())); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:118:39 + --> $DIR/format_args.rs:119:39 | LL | print!("{}", ((Location::caller()).to_string())); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:146:38 + --> $DIR/format_args.rs:147:38 | LL | let x = format!("{} {}", a, b.to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:160:24 + --> $DIR/format_args.rs:161:24 | LL | println!("{}", original[..10].to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` diff --git a/tests/ui/to_string_trait_impl.rs b/tests/ui/to_string_trait_impl.rs new file mode 100644 index 0000000000000..b0731632d4504 --- /dev/null +++ b/tests/ui/to_string_trait_impl.rs @@ -0,0 +1,31 @@ +#![warn(clippy::to_string_trait_impl)] + +use std::fmt::{self, Display}; + +struct Point { + x: usize, + y: usize, +} + +impl ToString for Point { + fn to_string(&self) -> String { + format!("({}, {})", self.x, self.y) + } +} + +struct Foo; + +impl Display for Foo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Foo") + } +} + +struct Bar; + +impl Bar { + #[allow(clippy::inherent_to_string)] + fn to_string(&self) -> String { + String::from("Bar") + } +} diff --git a/tests/ui/to_string_trait_impl.stderr b/tests/ui/to_string_trait_impl.stderr new file mode 100644 index 0000000000000..55fa9f12c0e65 --- /dev/null +++ b/tests/ui/to_string_trait_impl.stderr @@ -0,0 +1,16 @@ +error: direct implementation of `ToString` + --> $DIR/to_string_trait_impl.rs:10:1 + | +LL | / impl ToString for Point { +LL | | fn to_string(&self) -> String { +LL | | format!("({}, {})", self.x, self.y) +LL | | } +LL | | } + | |_^ + | + = help: prefer implementing `Display` instead + = note: `-D clippy::to-string-trait-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::to_string_trait_impl)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/unconditional_recursion.rs b/tests/ui/unconditional_recursion.rs index 7b898a6e0e784..0a3acdae9d31b 100644 --- a/tests/ui/unconditional_recursion.rs +++ b/tests/ui/unconditional_recursion.rs @@ -206,6 +206,7 @@ impl PartialEq for S8 { struct S9; +#[allow(clippy::to_string_trait_impl)] impl std::string::ToString for S9 { fn to_string(&self) -> String { //~^ ERROR: function cannot return without recursing @@ -215,6 +216,7 @@ impl std::string::ToString for S9 { struct S10; +#[allow(clippy::to_string_trait_impl)] impl std::string::ToString for S10 { fn to_string(&self) -> String { //~^ ERROR: function cannot return without recursing @@ -225,6 +227,7 @@ impl std::string::ToString for S10 { struct S11; +#[allow(clippy::to_string_trait_impl)] impl std::string::ToString for S11 { fn to_string(&self) -> String { //~^ ERROR: function cannot return without recursing diff --git a/tests/ui/unconditional_recursion.stderr b/tests/ui/unconditional_recursion.stderr index 094b80d4586c8..93a5eac91d8e3 100644 --- a/tests/ui/unconditional_recursion.stderr +++ b/tests/ui/unconditional_recursion.stderr @@ -23,7 +23,7 @@ LL | self.eq(other) = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> $DIR/unconditional_recursion.rs:210:5 + --> $DIR/unconditional_recursion.rs:211:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -34,7 +34,7 @@ LL | self.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> $DIR/unconditional_recursion.rs:219:5 + --> $DIR/unconditional_recursion.rs:221:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -45,7 +45,7 @@ LL | x.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> $DIR/unconditional_recursion.rs:229:5 + --> $DIR/unconditional_recursion.rs:232:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -326,7 +326,7 @@ LL | mine == theirs | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> $DIR/unconditional_recursion.rs:244:5 + --> $DIR/unconditional_recursion.rs:247:5 | LL | / fn new() -> Self { LL | | @@ -335,13 +335,13 @@ LL | | } | |_____^ | note: recursive call site - --> $DIR/unconditional_recursion.rs:246:9 + --> $DIR/unconditional_recursion.rs:249:9 | LL | Self::default() | ^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> $DIR/unconditional_recursion.rs:283:5 + --> $DIR/unconditional_recursion.rs:286:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -352,7 +352,7 @@ LL | | } | |_____^ | note: recursive call site - --> $DIR/unconditional_recursion.rs:287:9 + --> $DIR/unconditional_recursion.rs:290:9 | LL | mine.eq(theirs) | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 2dd1d746626f3..7f01c981a9380 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -27,6 +27,7 @@ impl AsRef for X { } } +#[allow(clippy::to_string_trait_impl)] impl ToString for X { fn to_string(&self) -> String { self.0.to_string() @@ -265,6 +266,7 @@ mod issue_8507 { } } + #[allow(clippy::to_string_trait_impl)] impl ToString for Y { fn to_string(&self) -> String { self.0.to_string() @@ -338,6 +340,7 @@ mod issue_9317 { struct Bytes {} + #[allow(clippy::to_string_trait_impl)] impl ToString for Bytes { fn to_string(&self) -> String { "123".to_string() diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 17fad33402b58..a270ed1e1c21e 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -27,6 +27,7 @@ impl AsRef for X { } } +#[allow(clippy::to_string_trait_impl)] impl ToString for X { fn to_string(&self) -> String { self.0.to_string() @@ -265,6 +266,7 @@ mod issue_8507 { } } + #[allow(clippy::to_string_trait_impl)] impl ToString for Y { fn to_string(&self) -> String { self.0.to_string() @@ -338,6 +340,7 @@ mod issue_9317 { struct Bytes {} + #[allow(clippy::to_string_trait_impl)] impl ToString for Bytes { fn to_string(&self) -> String { "123".to_string() diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index ad6fa422b8cd2..95ff5f2ec2cff 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -1,11 +1,11 @@ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:154:64 + --> $DIR/unnecessary_to_owned.rs:155:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:154:20 + --> $DIR/unnecessary_to_owned.rs:155:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,55 +13,55 @@ LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()) = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` error: redundant clone - --> $DIR/unnecessary_to_owned.rs:155:40 + --> $DIR/unnecessary_to_owned.rs:156:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:155:21 + --> $DIR/unnecessary_to_owned.rs:156:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:156:48 + --> $DIR/unnecessary_to_owned.rs:157:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:156:19 + --> $DIR/unnecessary_to_owned.rs:157:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:157:35 + --> $DIR/unnecessary_to_owned.rs:158:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:157:18 + --> $DIR/unnecessary_to_owned.rs:158:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:158:39 + --> $DIR/unnecessary_to_owned.rs:159:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:158:20 + --> $DIR/unnecessary_to_owned.rs:159:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:63:36 + --> $DIR/unnecessary_to_owned.rs:64:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -70,415 +70,415 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:64:19 + --> $DIR/unnecessary_to_owned.rs:65:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> $DIR/unnecessary_to_owned.rs:66:20 + --> $DIR/unnecessary_to_owned.rs:67:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:67:38 + --> $DIR/unnecessary_to_owned.rs:68:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:68:20 + --> $DIR/unnecessary_to_owned.rs:69:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> $DIR/unnecessary_to_owned.rs:70:18 + --> $DIR/unnecessary_to_owned.rs:71:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:71:34 + --> $DIR/unnecessary_to_owned.rs:72:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:72:18 + --> $DIR/unnecessary_to_owned.rs:73:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:74:17 + --> $DIR/unnecessary_to_owned.rs:75:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:75:30 + --> $DIR/unnecessary_to_owned.rs:76:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:76:17 + --> $DIR/unnecessary_to_owned.rs:77:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:77:17 + --> $DIR/unnecessary_to_owned.rs:78:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:79:19 + --> $DIR/unnecessary_to_owned.rs:80:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:80:36 + --> $DIR/unnecessary_to_owned.rs:81:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:81:19 + --> $DIR/unnecessary_to_owned.rs:82:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:82:19 + --> $DIR/unnecessary_to_owned.rs:83:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:83:19 + --> $DIR/unnecessary_to_owned.rs:84:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:86:42 + --> $DIR/unnecessary_to_owned.rs:87:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:89:25 + --> $DIR/unnecessary_to_owned.rs:90:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:90:26 + --> $DIR/unnecessary_to_owned.rs:91:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:91:24 + --> $DIR/unnecessary_to_owned.rs:92:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:92:23 + --> $DIR/unnecessary_to_owned.rs:93:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:93:25 + --> $DIR/unnecessary_to_owned.rs:94:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:30 + --> $DIR/unnecessary_to_owned.rs:96:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:96:31 + --> $DIR/unnecessary_to_owned.rs:97:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:97:29 + --> $DIR/unnecessary_to_owned.rs:98:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:28 + --> $DIR/unnecessary_to_owned.rs:99:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:99:30 + --> $DIR/unnecessary_to_owned.rs:100:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:29 + --> $DIR/unnecessary_to_owned.rs:102:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:43 + --> $DIR/unnecessary_to_owned.rs:102:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:29 + --> $DIR/unnecessary_to_owned.rs:103:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:47 + --> $DIR/unnecessary_to_owned.rs:103:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:104:26 + --> $DIR/unnecessary_to_owned.rs:105:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:105:27 + --> $DIR/unnecessary_to_owned.rs:106:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:106:25 + --> $DIR/unnecessary_to_owned.rs:107:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:107:24 + --> $DIR/unnecessary_to_owned.rs:108:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:108:24 + --> $DIR/unnecessary_to_owned.rs:109:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:109:26 + --> $DIR/unnecessary_to_owned.rs:110:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:110:26 + --> $DIR/unnecessary_to_owned.rs:111:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:111:26 + --> $DIR/unnecessary_to_owned.rs:112:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:113:31 + --> $DIR/unnecessary_to_owned.rs:114:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:114:32 + --> $DIR/unnecessary_to_owned.rs:115:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:115:30 + --> $DIR/unnecessary_to_owned.rs:116:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:29 + --> $DIR/unnecessary_to_owned.rs:117:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:117:29 + --> $DIR/unnecessary_to_owned.rs:118:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:31 + --> $DIR/unnecessary_to_owned.rs:119:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:31 + --> $DIR/unnecessary_to_owned.rs:120:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:31 + --> $DIR/unnecessary_to_owned.rs:121:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:30 + --> $DIR/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:44 + --> $DIR/unnecessary_to_owned.rs:123:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:30 + --> $DIR/unnecessary_to_owned.rs:124:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:44 + --> $DIR/unnecessary_to_owned.rs:124:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:124:30 + --> $DIR/unnecessary_to_owned.rs:125:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:124:44 + --> $DIR/unnecessary_to_owned.rs:125:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:125:30 + --> $DIR/unnecessary_to_owned.rs:126:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:125:48 + --> $DIR/unnecessary_to_owned.rs:126:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:126:30 + --> $DIR/unnecessary_to_owned.rs:127:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:126:52 + --> $DIR/unnecessary_to_owned.rs:127:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:127:30 + --> $DIR/unnecessary_to_owned.rs:128:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:127:48 + --> $DIR/unnecessary_to_owned.rs:128:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:129:20 + --> $DIR/unnecessary_to_owned.rs:130:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:131:13 + --> $DIR/unnecessary_to_owned.rs:132:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:132:13 + --> $DIR/unnecessary_to_owned.rs:133:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:133:13 + --> $DIR/unnecessary_to_owned.rs:134:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:134:13 + --> $DIR/unnecessary_to_owned.rs:135:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:136:13 + --> $DIR/unnecessary_to_owned.rs:137:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:137:13 + --> $DIR/unnecessary_to_owned.rs:138:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:138:13 + --> $DIR/unnecessary_to_owned.rs:139:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:139:13 + --> $DIR/unnecessary_to_owned.rs:140:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:201:14 + --> $DIR/unnecessary_to_owned.rs:202:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -494,31 +494,31 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:224:14 + --> $DIR/unnecessary_to_owned.rs:225:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:229:14 + --> $DIR/unnecessary_to_owned.rs:230:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:276:24 + --> $DIR/unnecessary_to_owned.rs:278:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:384:12 + --> $DIR/unnecessary_to_owned.rs:387:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:527:37 + --> $DIR/unnecessary_to_owned.rs:530:37 | LL | IntoFuture::into_future(foo([].to_vec(), &0)); | ^^^^^^^^^^^ help: use: `[]` diff --git a/tests/ui/unnecessary_to_owned_on_split.fixed b/tests/ui/unnecessary_to_owned_on_split.fixed index f87c898f9b79a..e0ba216f41bfc 100644 --- a/tests/ui/unnecessary_to_owned_on_split.fixed +++ b/tests/ui/unnecessary_to_owned_on_split.fixed @@ -8,6 +8,7 @@ impl AsRef for Issue12068 { } } +#[allow(clippy::to_string_trait_impl)] impl ToString for Issue12068 { fn to_string(&self) -> String { String::new() diff --git a/tests/ui/unnecessary_to_owned_on_split.rs b/tests/ui/unnecessary_to_owned_on_split.rs index db5719e588095..70efc6ebba5da 100644 --- a/tests/ui/unnecessary_to_owned_on_split.rs +++ b/tests/ui/unnecessary_to_owned_on_split.rs @@ -8,6 +8,7 @@ impl AsRef for Issue12068 { } } +#[allow(clippy::to_string_trait_impl)] impl ToString for Issue12068 { fn to_string(&self) -> String { String::new() diff --git a/tests/ui/unnecessary_to_owned_on_split.stderr b/tests/ui/unnecessary_to_owned_on_split.stderr index 4cfaeed3384ab..9aea15b48bfbd 100644 --- a/tests/ui/unnecessary_to_owned_on_split.stderr +++ b/tests/ui/unnecessary_to_owned_on_split.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned_on_split.rs:18:13 + --> $DIR/unnecessary_to_owned_on_split.rs:19:13 | LL | let _ = "a".to_string().split('a').next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split('a')` @@ -8,49 +8,49 @@ LL | let _ = "a".to_string().split('a').next().unwrap(); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned_on_split.rs:20:13 + --> $DIR/unnecessary_to_owned_on_split.rs:21:13 | LL | let _ = "a".to_string().split("a").next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split("a")` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned_on_split.rs:22:13 + --> $DIR/unnecessary_to_owned_on_split.rs:23:13 | LL | let _ = "a".to_owned().split('a').next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split('a')` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned_on_split.rs:24:13 + --> $DIR/unnecessary_to_owned_on_split.rs:25:13 | LL | let _ = "a".to_owned().split("a").next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split("a")` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned_on_split.rs:26:13 + --> $DIR/unnecessary_to_owned_on_split.rs:27:13 | LL | let _ = Issue12068.to_string().split('a').next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Issue12068.as_ref().split('a')` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned_on_split.rs:29:13 + --> $DIR/unnecessary_to_owned_on_split.rs:30:13 | LL | let _ = [1].to_vec().split(|x| *x == 2).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned_on_split.rs:31:13 + --> $DIR/unnecessary_to_owned_on_split.rs:32:13 | LL | let _ = [1].to_vec().split(|x| *x == 2).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned_on_split.rs:33:13 + --> $DIR/unnecessary_to_owned_on_split.rs:34:13 | LL | let _ = [1].to_owned().split(|x| *x == 2).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned_on_split.rs:35:13 + --> $DIR/unnecessary_to_owned_on_split.rs:36:13 | LL | let _ = [1].to_owned().split(|x| *x == 2).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)` From a51fc2a80ec55721425c8ccf445d050e858a0800 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Fri, 12 May 2023 03:02:46 +0200 Subject: [PATCH 26/68] Fix `NonZero` clippy lints. --- .../src/operators/arithmetic_side_effects.rs | 55 +++++++++++-------- .../transmute/transmute_int_to_non_zero.rs | 51 +++++++++++------ 2 files changed, 64 insertions(+), 42 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 929efb6c574d5..96ea063aa74d6 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,11 +1,11 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_utils::consts::{constant, constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::type_diagnostic_name; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; @@ -88,37 +88,44 @@ impl ArithmeticSideEffects { } /// Verifies built-in types that have specific allowed operations - fn has_specific_allowed_type_and_operation( - cx: &LateContext<'_>, - lhs_ty: Ty<'_>, + fn has_specific_allowed_type_and_operation<'tcx>( + cx: &LateContext<'tcx>, + lhs_ty: Ty<'tcx>, op: &Spanned, - rhs_ty: Ty<'_>, + rhs_ty: Ty<'tcx>, ) -> bool { let is_div_or_rem = matches!(op.node, hir::BinOpKind::Div | hir::BinOpKind::Rem); - let is_non_zero_u = |symbol: Option| { - matches!( - symbol, - Some( - sym::NonZeroU128 - | sym::NonZeroU16 - | sym::NonZeroU32 - | sym::NonZeroU64 - | sym::NonZeroU8 - | sym::NonZeroUsize - ) - ) + let is_non_zero_u = |cx: &LateContext<'tcx>, ty: Ty<'tcx>| { + let tcx = cx.tcx; + + let ty::Adt(adt, substs) = ty.kind() else { return false }; + + if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { + return false; + }; + + let int_type = substs.type_at(0); + let unsigned_int_types = [ + tcx.types.u8, + tcx.types.u16, + tcx.types.u32, + tcx.types.u64, + tcx.types.u128, + tcx.types.usize, + ]; + + unsigned_int_types.contains(&int_type) }; let is_sat_or_wrap = |ty: Ty<'_>| { - let is_sat = type_diagnostic_name(cx, ty) == Some(sym::Saturating); - let is_wrap = type_diagnostic_name(cx, ty) == Some(sym::Wrapping); - is_sat || is_wrap + is_type_diagnostic_item(cx, ty, sym::Saturating) || is_type_diagnostic_item(cx, ty, sym::Wrapping) }; - // If the RHS is NonZeroU*, then division or module by zero will never occur - if is_non_zero_u(type_diagnostic_name(cx, rhs_ty)) && is_div_or_rem { + // If the RHS is `NonZero`, then division or module by zero will never occur. + if is_non_zero_u(cx, rhs_ty) && is_div_or_rem { return true; } - // `Saturation` and `Wrapping` can overflow if the RHS is zero in a division or module + + // `Saturation` and `Wrapping` can overflow if the RHS is zero in a division or module. if is_sat_or_wrap(lhs_ty) { return !is_div_or_rem; } diff --git a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 5df645491ff81..97068efd43cd8 100644 --- a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -16,40 +16,55 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, ) -> bool { - let (ty::Int(_) | ty::Uint(_), Some(to_ty_adt)) = (&from_ty.kind(), to_ty.ty_adt_def()) else { + let tcx = cx.tcx; + + let (ty::Int(_) | ty::Uint(_), ty::Adt(adt, substs)) = (&from_ty.kind(), to_ty.kind()) else { return false; }; - let Some(to_type_sym) = cx.tcx.get_diagnostic_name(to_ty_adt.did()) else { + + if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { return false; }; - if !matches!( - to_type_sym, - sym::NonZeroU8 - | sym::NonZeroU16 - | sym::NonZeroU32 - | sym::NonZeroU64 - | sym::NonZeroU128 - | sym::NonZeroI8 - | sym::NonZeroI16 - | sym::NonZeroI32 - | sym::NonZeroI64 - | sym::NonZeroI128 - ) { + // FIXME: This can be simplified once `NonZero` is stable. + let coercable_types = [ + ("NonZeroU8", tcx.types.u8), + ("NonZeroU16", tcx.types.u16), + ("NonZeroU32", tcx.types.u32), + ("NonZeroU64", tcx.types.u64), + ("NonZeroU128", tcx.types.u128), + ("NonZeroUsize", tcx.types.usize), + ("NonZeroI8", tcx.types.i8), + ("NonZeroI16", tcx.types.i16), + ("NonZeroI32", tcx.types.i32), + ("NonZeroI64", tcx.types.i64), + ("NonZeroI128", tcx.types.i128), + ("NonZeroIsize", tcx.types.isize), + ]; + + let int_type = substs.type_at(0); + + let Some(nonzero_alias) = coercable_types.iter().find_map(|(nonzero_alias, t)| { + if *t == int_type && *t == from_ty { + Some(nonzero_alias) + } else { + None + } + }) else { return false; - } + }; span_lint_and_then( cx, TRANSMUTE_INT_TO_NON_ZERO, e.span, - &format!("transmute from a `{from_ty}` to a `{to_type_sym}`"), + &format!("transmute from a `{from_ty}` to a `{nonzero_alias}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); diag.span_suggestion( e.span, "consider using", - format!("{to_type_sym}::{}({arg})", sym::new_unchecked), + format!("{nonzero_alias}::{}({arg})", sym::new_unchecked), Applicability::Unspecified, ); }, From ff5afac6167f1af9413bc8bc7a95626a91a489f2 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sat, 27 Jan 2024 17:07:10 +0100 Subject: [PATCH 27/68] [`never_loop`]: recognize `?` desugaring in try blocks --- clippy_lints/src/loops/never_loop.rs | 6 +++--- tests/ui/never_loop.rs | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 62bc663191f60..245a903f99826 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -201,12 +201,12 @@ fn never_loop_expr<'tcx>( }) }) }, - ExprKind::Block(b, l) => { - if l.is_some() { + ExprKind::Block(b, _) => { + if b.targeted_by_break { 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; + let jumped_to = b.targeted_by_break && local_labels.pop().unwrap().1; match ret { NeverLoopResult::Diverging if jumped_to => NeverLoopResult::Normal, _ => ret, diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index c67a6d4494e05..92f173d9db4a3 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -1,4 +1,4 @@ -#![feature(inline_const)] +#![feature(inline_const, try_blocks)] #![allow( clippy::eq_op, clippy::single_match, @@ -400,6 +400,15 @@ pub fn test32() { } } +pub fn issue12205() -> Option<()> { + loop { + let _: Option<_> = try { + None?; + return Some(()); + }; + } +} + fn main() { test1(); test2(); From 3aa2c279c8d1a64c8ecbf4c4e9f1b5bc8f9c4883 Mon Sep 17 00:00:00 2001 From: Quinn Sinclair Date: Sun, 28 Jan 2024 22:43:40 +0100 Subject: [PATCH 28/68] rewrote to match only Result::err cons --- clippy_lints/src/returns.rs | 42 +++++++------------ .../needless_return_with_question_mark.fixed | 15 ++++--- .../ui/needless_return_with_question_mark.rs | 14 +++++-- .../needless_return_with_question_mark.stderr | 8 +--- 4 files changed, 35 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2d4e7d269fd2c..1047793b82233 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -2,10 +2,14 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lin use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; -use clippy_utils::{fn_def_id, is_from_proc_macro, is_inside_let_else, path_to_local_id, span_find_starting_semi}; +use clippy_utils::{ + fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id, + span_find_starting_semi, +}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; +use rustc_hir::LangItem::ResultErr; use rustc_hir::{ Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt, StmtKind, @@ -176,37 +180,20 @@ fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { }) } -/// -/// The expression of the desugared `try` operator is a match over an expression with type: -/// `ControlFlow, B:Result<_, E'>>`, with final type `B`. -/// If E and E' are the same type, then there is no error conversion happening. -/// Error conversion happens when E can be transformed into E' via a `From` or `Into` conversion. -fn desugar_expr_performs_error_conversion(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - - if let ty::Adt(_, generics) = ty.kind() - && let Some(brk) = generics.first() - && let Some(cont) = generics.get(1) - && let Some(brk_type) = brk.as_type() - && let Some(cont_type) = cont.as_type() - && let ty::Adt(_, brk_generics) = brk_type.kind() - && let ty::Adt(_, cont_generics) = cont_type.kind() - && let Some(brk_err) = brk_generics.get(1) - && let Some(cont_err) = cont_generics.get(1) - && let Some(brk_err_type) = brk_err.as_type() - && let Some(cont_err_type) = cont_err.as_type() - { - return brk_err_type != cont_err_type; - } - false -} - impl<'tcx> LateLintPass<'tcx> for Return { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if !in_external_macro(cx.sess(), stmt.span) && let StmtKind::Semi(expr) = stmt.kind && let ExprKind::Ret(Some(ret)) = expr.kind - && let ExprKind::Match(match_expr, _, MatchSource::TryDesugar(..)) = ret.kind + // return Err(...)? desugars to a match + // over a Err(...).branch() + // which breaks down to a branch call, with the callee being + // the constructor of the Err variant + && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind + && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind + && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind + && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) + // Ensure this is not the final stmt, otherwise removing it would cause a compile error && let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id)) && let ItemKind::Fn(_, _, body) = item.kind @@ -217,7 +204,6 @@ impl<'tcx> LateLintPass<'tcx> for Return { && final_stmt.hir_id != stmt.hir_id && !is_from_proc_macro(cx, expr) && !stmt_needs_never_type(cx, stmt.hir_id) - && !desugar_expr_performs_error_conversion(cx, match_expr) { span_lint_and_sugg( cx, diff --git a/tests/ui/needless_return_with_question_mark.fixed b/tests/ui/needless_return_with_question_mark.fixed index 2b0a835bc7f07..9b7da85266316 100644 --- a/tests/ui/needless_return_with_question_mark.fixed +++ b/tests/ui/needless_return_with_question_mark.fixed @@ -78,9 +78,6 @@ fn issue11616() -> Result<(), ()> { Ok(()) } -/// This is a false positive that occurs because of the way `?` is handled. -/// The `?` operator is also doing a conversion from `Result` to `Result`. -/// In this case the conversion is needed, and thus the `?` operator is also needed. fn issue11982() { mod bar { pub struct Error; @@ -115,10 +112,18 @@ fn issue11982_no_conversion() { fn foo(ok: bool) -> Result<(), bar::Error> { if !ok { - bar::foo(ok).map(|_| Ok::<(), bar::Error>(()))?; - //~^ ERROR: unneeded `return` statement with `?` operator + return bar::foo(ok).map(|_| Ok::<(), bar::Error>(()))?; }; Ok(()) } +} +fn general_return() { + fn foo(ok: bool) -> Result<(), ()> { + let bar = Result::Ok(Result::<(), ()>::Ok(())); + if !ok { + return bar?; + }; + Ok(()) + } } diff --git a/tests/ui/needless_return_with_question_mark.rs b/tests/ui/needless_return_with_question_mark.rs index ecbf00254f06b..68e76d2b6402e 100644 --- a/tests/ui/needless_return_with_question_mark.rs +++ b/tests/ui/needless_return_with_question_mark.rs @@ -78,9 +78,6 @@ fn issue11616() -> Result<(), ()> { Ok(()) } -/// This is a false positive that occurs because of the way `?` is handled. -/// The `?` operator is also doing a conversion from `Result` to `Result`. -/// In this case the conversion is needed, and thus the `?` operator is also needed. fn issue11982() { mod bar { pub struct Error; @@ -116,7 +113,16 @@ fn issue11982_no_conversion() { fn foo(ok: bool) -> Result<(), bar::Error> { if !ok { return bar::foo(ok).map(|_| Ok::<(), bar::Error>(()))?; - //~^ ERROR: unneeded `return` statement with `?` operator + }; + Ok(()) + } +} + +fn general_return() { + fn foo(ok: bool) -> Result<(), ()> { + let bar = Result::Ok(Result::<(), ()>::Ok(())); + if !ok { + return bar?; }; Ok(()) } diff --git a/tests/ui/needless_return_with_question_mark.stderr b/tests/ui/needless_return_with_question_mark.stderr index 59c212bcbb341..17aa212ae8d7a 100644 --- a/tests/ui/needless_return_with_question_mark.stderr +++ b/tests/ui/needless_return_with_question_mark.stderr @@ -13,11 +13,5 @@ error: unneeded `return` statement with `?` operator LL | return Err(())?; | ^^^^^^^ help: remove it -error: unneeded `return` statement with `?` operator - --> $DIR/needless_return_with_question_mark.rs:118:13 - | -LL | return bar::foo(ok).map(|_| Ok::<(), bar::Error>(()))?; - | ^^^^^^^ help: remove it - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors From b3d53774e7ffcde40b18e18be700f63204ec96bd Mon Sep 17 00:00:00 2001 From: modelflat Date: Mon, 21 Aug 2023 14:39:42 +0200 Subject: [PATCH 29/68] Make `redundant_closure_for_method_calls` suggest relative paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #10854. Co-authored-by: Alejandra Gonzรกlez --- clippy_lints/src/eta_reduction.rs | 36 ++------ clippy_utils/src/lib.rs | 134 +++++++++++++++++++++++++++++- tests/ui/eta.fixed | 54 ++++++++++++ tests/ui/eta.rs | 54 ++++++++++++ tests/ui/eta.stderr | 26 +++++- 5 files changed, 271 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 1ea5b78980589..40be71a0e5d61 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,15 +3,14 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; +use clippy_utils::{get_path_from_caller_to_method_type, higher, is_adjusted, path_to_local, path_to_local_id}; use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, GenericArgsRef, - ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults, + self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, ImplPolarity, List, Region, RegionKind, + Ty, TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -203,11 +202,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { "redundant closure", |diag| { let args = typeck.node_args(body.value.hir_id); - let name = get_ufcs_type_name(cx, method_def_id, args); + let caller = self_.hir_id.owner.def_id; + let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args); diag.span_suggestion( expr.span, "replace the closure with the method itself", - format!("{}::{}", name, path.ident.name), + format!("{}::{}", type_name, path.ident.name), Applicability::MachineApplicable, ); }, @@ -309,27 +309,3 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<' .zip(to_sig.inputs_and_output) .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty)) } - -fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, args: GenericArgsRef<'tcx>) -> String { - let assoc_item = cx.tcx.associated_item(method_def_id); - let def_id = assoc_item.container_id(cx.tcx); - match assoc_item.container { - ty::TraitContainer => cx.tcx.def_path_str(def_id), - ty::ImplContainer => { - let ty = cx.tcx.type_of(def_id).instantiate_identity(); - match ty.kind() { - ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()), - ty::Array(..) - | ty::Dynamic(..) - | ty::Never - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Slice(_) - | ty::Tuple(_) => { - format!("<{}>", EarlyBinder::bind(ty).instantiate(cx.tcx, args)) - }, - _ => ty.to_string(), - } - }, - } -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 4e499ff4cc612..74284c959cbde 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -75,6 +75,7 @@ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; +use std::iter::{once, repeat}; use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; @@ -84,6 +85,7 @@ use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId, LOCAL_CRATE}; +use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; @@ -102,8 +104,8 @@ use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, FloatTy, IntTy, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeAndMut, - TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, ParamEnv, + ParamEnvAnd, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; @@ -3264,3 +3266,131 @@ pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option< }) } } + +/// Produces a path from a local caller to the type of the called method. Suitable for user +/// output/suggestions. +/// +/// Returned path can be either absolute (for methods defined non-locally), or relative (for local +/// methods). +pub fn get_path_from_caller_to_method_type<'tcx>( + tcx: TyCtxt<'tcx>, + from: LocalDefId, + method: DefId, + args: GenericArgsRef<'tcx>, +) -> String { + let assoc_item = tcx.associated_item(method); + let def_id = assoc_item.container_id(tcx); + match assoc_item.container { + rustc_ty::TraitContainer => get_path_to_callee(tcx, from, def_id), + rustc_ty::ImplContainer => { + let ty = tcx.type_of(def_id).instantiate_identity(); + get_path_to_ty(tcx, from, ty, args) + }, + } +} + +fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String { + match ty.kind() { + rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()), + // TODO these types need to be recursively resolved as well + rustc_ty::Array(..) + | rustc_ty::Dynamic(..) + | rustc_ty::Never + | rustc_ty::RawPtr(_) + | rustc_ty::Ref(..) + | rustc_ty::Slice(_) + | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)), + _ => ty.to_string(), + } +} + +/// Produce a path from some local caller to the callee. Suitable for user output/suggestions. +fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String { + // only search for a relative path if the call is fully local + if callee.is_local() { + let callee_path = tcx.def_path(callee); + let caller_path = tcx.def_path(from.to_def_id()); + maybe_get_relative_path(&caller_path, &callee_path, 2) + } else { + tcx.def_path_str(callee) + } +} + +/// Tries to produce a relative path from `from` to `to`; if such a path would contain more than +/// `max_super` `super` items, produces an absolute path instead. Both `from` and `to` should be in +/// the local crate. +/// +/// Suitable for user output/suggestions. +/// +/// This ignores use items, and assumes that the target path is visible from the source +/// path (which _should_ be a reasonable assumption since we in order to be able to use an object of +/// certain type T, T is required to be visible). +/// +/// TODO make use of `use` items. Maybe we should have something more sophisticated like +/// rust-analyzer does? +fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String { + use itertools::EitherOrBoth::{Both, Left, Right}; + + // 1. skip the segments common for both paths (regardless of their type) + let unique_parts = to + .data + .iter() + .zip_longest(from.data.iter()) + .skip_while(|el| matches!(el, Both(l, r) if l == r)) + .map(|el| match el { + Both(l, r) => Both(l.data, r.data), + Left(l) => Left(l.data), + Right(r) => Right(r.data), + }); + + // 2. for the remaning segments, construct relative path using only mod names and `super` + let mut go_up_by = 0; + let mut path = Vec::new(); + for el in unique_parts { + match el { + Both(l, r) => { + // consider: + // a::b::sym:: :: refers to + // c::d::e ::f::sym + // result should be super::super::c::d::e::f + // + // alternatively: + // a::b::c ::d::sym refers to + // e::f::sym:: :: + // result should be super::super::super::super::e::f + if let DefPathData::TypeNs(s) = l { + path.push(s.to_string()); + } + if let DefPathData::TypeNs(_) = r { + go_up_by += 1; + } + }, + // consider: + // a::b::sym:: :: refers to + // c::d::e ::f::sym + // when looking at `f` + Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()), + // consider: + // a::b::c ::d::sym refers to + // e::f::sym:: :: + // when looking at `d` + Right(DefPathData::TypeNs(_)) => go_up_by += 1, + _ => {}, + } + } + + if go_up_by > max_super { + // `super` chain would be too long, just use the absolute path instead + once(String::from("crate")) + .chain(to.data.iter().filter_map(|el| { + if let DefPathData::TypeNs(sym) = el.data { + Some(sym.to_string()) + } else { + None + } + })) + .join("::") + } else { + repeat(String::from("super")).take(go_up_by).chain(path).join("::") + } +} diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 3aeb4dae30b26..da28ec2e653a7 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -417,3 +417,57 @@ fn _closure_with_types() { let _ = f2(|x: u32| f(x)); let _ = f2(|x| -> u32 { f(x) }); } + +/// https://github.com/rust-lang/rust-clippy/issues/10854 +/// This is to verify that redundant_closure_for_method_calls resolves suggested paths to relative. +mod issue_10854 { + pub mod test_mod { + pub struct Test; + + impl Test { + pub fn method(self) -> i32 { + 0 + } + } + + pub fn calls_test(test: Option) -> Option { + test.map(Test::method) + } + + pub fn calls_outer(test: Option) -> Option { + test.map(super::Outer::method) + } + } + + pub struct Outer; + + impl Outer { + pub fn method(self) -> i32 { + 0 + } + } + + pub fn calls_into_mod(test: Option) -> Option { + test.map(test_mod::Test::method) + } + + mod a { + pub mod b { + pub mod c { + pub fn extreme_nesting(test: Option) -> Option { + test.map(crate::issue_10854::d::Test::method) + } + } + } + } + + mod d { + pub struct Test; + + impl Test { + pub fn method(self) -> i32 { + 0 + } + } + } +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index b9b3303b371de..f924100f8f419 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -417,3 +417,57 @@ fn _closure_with_types() { let _ = f2(|x: u32| f(x)); let _ = f2(|x| -> u32 { f(x) }); } + +/// https://github.com/rust-lang/rust-clippy/issues/10854 +/// This is to verify that redundant_closure_for_method_calls resolves suggested paths to relative. +mod issue_10854 { + pub mod test_mod { + pub struct Test; + + impl Test { + pub fn method(self) -> i32 { + 0 + } + } + + pub fn calls_test(test: Option) -> Option { + test.map(|t| t.method()) + } + + pub fn calls_outer(test: Option) -> Option { + test.map(|t| t.method()) + } + } + + pub struct Outer; + + impl Outer { + pub fn method(self) -> i32 { + 0 + } + } + + pub fn calls_into_mod(test: Option) -> Option { + test.map(|t| t.method()) + } + + mod a { + pub mod b { + pub mod c { + pub fn extreme_nesting(test: Option) -> Option { + test.map(|t| t.method()) + } + } + } + } + + mod d { + pub struct Test; + + impl Test { + pub fn method(self) -> i32 { + 0 + } + } + } +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 7c4d2d7093edc..945de466d8322 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -166,5 +166,29 @@ error: redundant closure LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` -error: aborting due to 27 previous errors +error: redundant closure + --> $DIR/eta.rs:434:22 + | +LL | test.map(|t| t.method()) + | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method` + +error: redundant closure + --> $DIR/eta.rs:438:22 + | +LL | test.map(|t| t.method()) + | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method` + +error: redundant closure + --> $DIR/eta.rs:451:18 + | +LL | test.map(|t| t.method()) + | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method` + +error: redundant closure + --> $DIR/eta.rs:458:30 + | +LL | test.map(|t| t.method()) + | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method` + +error: aborting due to 31 previous errors From b2f2080942819a9cc5f384b6ecf49772ed39fab1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 29 Jan 2024 14:53:29 +0100 Subject: [PATCH 30/68] Add regression test for #2371 --- tests/ui/unnecessary_fold.fixed | 6 +++++ tests/ui/unnecessary_fold.rs | 6 +++++ tests/ui/unnecessary_fold.stderr | 41 +++++++++++++++++++------------- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/tests/ui/unnecessary_fold.fixed b/tests/ui/unnecessary_fold.fixed index c884d26eb6176..c5bc11b55ab5c 100644 --- a/tests/ui/unnecessary_fold.fixed +++ b/tests/ui/unnecessary_fold.fixed @@ -1,9 +1,15 @@ #![allow(dead_code)] +fn is_any(acc: bool, x: usize) -> bool { + acc || x > 2 +} + /// Calls which should trigger the `UNNECESSARY_FOLD` lint fn unnecessary_fold() { // Can be replaced by .any let _ = (0..3).any(|x| x > 2); + // Can be replaced by .any (checking suggestion) + let _ = (0..3).fold(false, is_any); // Can be replaced by .all let _ = (0..3).all(|x| x > 2); // Can be replaced by .sum diff --git a/tests/ui/unnecessary_fold.rs b/tests/ui/unnecessary_fold.rs index 2e6d6ba52eb91..3a5136eeeaeb7 100644 --- a/tests/ui/unnecessary_fold.rs +++ b/tests/ui/unnecessary_fold.rs @@ -1,9 +1,15 @@ #![allow(dead_code)] +fn is_any(acc: bool, x: usize) -> bool { + acc || x > 2 +} + /// Calls which should trigger the `UNNECESSARY_FOLD` lint fn unnecessary_fold() { // Can be replaced by .any let _ = (0..3).fold(false, |acc, x| acc || x > 2); + // Can be replaced by .any (checking suggestion) + let _ = (0..3).fold(false, |acc, x| is_any(acc, x)); // Can be replaced by .all let _ = (0..3).fold(true, |acc, x| acc && x > 2); // Can be replaced by .sum diff --git a/tests/ui/unnecessary_fold.stderr b/tests/ui/unnecessary_fold.stderr index f0d0396384214..123d4a3be7557 100644 --- a/tests/ui/unnecessary_fold.stderr +++ b/tests/ui/unnecessary_fold.stderr @@ -1,5 +1,5 @@ error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:6:20 + --> $DIR/unnecessary_fold.rs:10:20 | LL | let _ = (0..3).fold(false, |acc, x| acc || x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` @@ -7,89 +7,98 @@ LL | let _ = (0..3).fold(false, |acc, x| acc || x > 2); = note: `-D clippy::unnecessary-fold` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_fold)]` +error: redundant closure + --> $DIR/unnecessary_fold.rs:12:32 + | +LL | let _ = (0..3).fold(false, |acc, x| is_any(acc, x)); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `is_any` + | + = note: `-D clippy::redundant-closure` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` + error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:8:20 + --> $DIR/unnecessary_fold.rs:14:20 | LL | let _ = (0..3).fold(true, |acc, x| acc && x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `all(|x| x > 2)` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:10:25 + --> $DIR/unnecessary_fold.rs:16:25 | LL | let _: i32 = (0..3).fold(0, |acc, x| acc + x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:12:25 + --> $DIR/unnecessary_fold.rs:18:25 | LL | let _: i32 = (0..3).fold(1, |acc, x| acc * x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:17:41 + --> $DIR/unnecessary_fold.rs:23:41 | LL | let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:47:10 + --> $DIR/unnecessary_fold.rs:53:10 | LL | .fold(false, |acc, x| acc || x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:58:33 + --> $DIR/unnecessary_fold.rs:64:33 | LL | assert_eq!(map.values().fold(0, |x, y| x + y), 0); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:61:30 + --> $DIR/unnecessary_fold.rs:67:30 | LL | let _ = map.values().fold(0, |x, y| x + y); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:62:30 + --> $DIR/unnecessary_fold.rs:68:30 | LL | let _ = map.values().fold(1, |x, y| x * y); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:63:35 + --> $DIR/unnecessary_fold.rs:69:35 | LL | let _: i32 = map.values().fold(0, |x, y| x + y); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:64:35 + --> $DIR/unnecessary_fold.rs:70:35 | LL | let _: i32 = map.values().fold(1, |x, y| x * y); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:65:31 + --> $DIR/unnecessary_fold.rs:71:31 | LL | anything(map.values().fold(0, |x, y| x + y)); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:66:31 + --> $DIR/unnecessary_fold.rs:72:31 | LL | anything(map.values().fold(1, |x, y| x * y)); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:67:26 + --> $DIR/unnecessary_fold.rs:73:26 | LL | num(map.values().fold(0, |x, y| x + y)); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()` error: this `.fold` can be written more succinctly using another method - --> $DIR/unnecessary_fold.rs:68:26 + --> $DIR/unnecessary_fold.rs:74:26 | LL | num(map.values().fold(1, |x, y| x * y)); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product()` -error: aborting due to 15 previous errors +error: aborting due to 16 previous errors From ce8b4b6ac021e842916a101dfe5dbc74c4bc33b7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 29 Jan 2024 14:54:28 +0100 Subject: [PATCH 31/68] Remove fixed FIXME --- clippy_lints/src/methods/unnecessary_fold.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index f3577ef6082bf..2046692bbd0bd 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -99,7 +99,6 @@ fn check_fold_with_op( cx, UNNECESSARY_FOLD, fold_span.with_hi(expr.span.hi()), - // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f) "this `.fold` can be written more succinctly using another method", "try", sugg, From 73706e8358dd8d8a792c6d596ddbda4080a0c227 Mon Sep 17 00:00:00 2001 From: Vinh Tran Date: Thu, 25 Jan 2024 19:08:43 -0500 Subject: [PATCH 32/68] Makes clippy-driver check for --sysroot in arg files Fixes https://github.com/rust-lang/rust-clippy/issues/12201 --- .github/driver.sh | 15 +++++++++++++++ src/driver.rs | 25 +++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) mode change 100644 => 100755 .github/driver.sh diff --git a/.github/driver.sh b/.github/driver.sh old mode 100644 new mode 100755 index c05c6ecc1151c..40a2aad0f5379 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -11,9 +11,16 @@ if [[ ${OS} == "Windows" ]]; then else desired_sysroot=/tmp fi +# Set --sysroot in command line sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot) test "$sysroot" = $desired_sysroot +# Set --sysroot in arg_file.txt and pass @arg_file.txt to command line +echo "--sysroot=$desired_sysroot" > arg_file.txt +sysroot=$(./target/debug/clippy-driver @arg_file.txt --print sysroot) +test "$sysroot" = $desired_sysroot + +# Setting SYSROOT in command line sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot) test "$sysroot" = $desired_sysroot @@ -24,6 +31,14 @@ test "$sysroot" = $desired_sysroot SYSROOT=/tmp RUSTFLAGS="--sysroot=$(rustc --print sysroot)" ../target/debug/cargo-clippy clippy --verbose ) +# Check that the --sysroot argument is only passed once via arg_file.txt (SYSROOT is ignored) +( + echo "fn main() {}" > target/driver_test.rs + echo "--sysroot="$(./target/debug/clippy-driver --print sysroot)"" > arg_file.txt + echo "--verbose" >> arg_file.txt + SYSROOT=/tmp ./target/debug/clippy-driver @arg_file.txt ./target/driver_test.rs +) + # Make sure this isn't set - clippy-driver should cope without it unset CARGO_MANIFEST_DIR diff --git a/src/driver.rs b/src/driver.rs index b944a299256c2..f5e52f787ab41 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -22,9 +22,11 @@ use rustc_session::EarlyDiagCtxt; use rustc_span::symbol::Symbol; use std::env; +use std::fs::read_to_string; use std::ops::Deref; use std::path::Path; use std::process::exit; +use std::string::ToString; use anstream::println; @@ -188,12 +190,31 @@ pub fn main() { exit(rustc_driver::catch_with_exit_code(move || { let mut orig_args: Vec = env::args().collect(); - let has_sysroot_arg = arg_value(&orig_args, "--sysroot", |_| true).is_some(); + + let has_sysroot_arg = |args: &mut [String]| -> bool { + if arg_value(args, "--sysroot", |_| true).is_some() { + return true; + } + // https://doc.rust-lang.org/rustc/command-line-arguments.html#path-load-command-line-flags-from-a-path + // Beside checking for existence of `--sysroot` on the command line, we need to + // check for the arg files that are prefixed with @ as well to be consistent with rustc + for arg in args.iter() { + if let Some(arg_file_path) = arg.strip_prefix('@') { + if let Ok(arg_file) = read_to_string(arg_file_path) { + let split_arg_file: Vec = arg_file.lines().map(ToString::to_string).collect(); + if arg_value(&split_arg_file, "--sysroot", |_| true).is_some() { + return true; + } + } + } + } + false + }; let sys_root_env = std::env::var("SYSROOT").ok(); let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { if let Some(sys_root) = sys_root_env { - if !has_sysroot_arg { + if !has_sysroot_arg(args) { args.extend(vec!["--sysroot".into(), sys_root]); } }; From d02df12bd5e9ad2016328786d6a117afe0f1604a Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Mon, 18 Dec 2023 17:57:22 +0800 Subject: [PATCH 33/68] add configuration for [`wildcard_imports`] to ignore certain imports --- clippy_config/src/conf.rs | 5 +++++ clippy_lints/src/lib.rs | 8 +++++++- clippy_lints/src/wildcard_imports.rs | 18 ++++++++++++++++-- .../toml_unknown_key/conf_unknown_key.stderr | 2 ++ .../wildcard_imports_whitelist/clippy.toml | 1 + .../wildcard_imports.fixed | 18 ++++++++++++++++++ .../wildcard_imports.rs | 18 ++++++++++++++++++ .../wildcard_imports.stderr | 11 +++++++++++ 8 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 tests/ui-toml/wildcard_imports_whitelist/clippy.toml create mode 100644 tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed create mode 100644 tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs create mode 100644 tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 8b45f250b102e..e4e512482ac03 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -571,6 +571,11 @@ define_Conf! { /// /// Don't lint when comparing the result of a modulo operation to zero. (allow_comparison_to_zero: bool = true), + /// Lint: WILDCARD_IMPORTS. + /// + /// List of path segments to ignore when checking wildcard imports, + /// could get overrided by `warn_on_all_wildcard_imports`. + (ignored_wildcard_imports: Vec = Vec::new()), } /// Search for the configuration file. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7da916ade2ab1..678058a5411a4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -545,6 +545,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { excessive_nesting_threshold, future_size_threshold, ref ignore_interior_mutability, + ref ignored_wildcard_imports, large_error_threshold, literal_representation_threshold, matches_for_let_else, @@ -876,7 +877,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { )) }); store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); - store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); + store.register_late_pass(move |_| { + Box::new(wildcard_imports::WildcardImports::new( + warn_on_all_wildcard_imports, + ignored_wildcard_imports.clone(), + )) + }); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(|_| Box::>::default()); diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index b82bd1d7e7c89..4eb45e6f8da52 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_test_module_or_function; use clippy_utils::source::{snippet, snippet_with_applicability}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind, PathSegment, UseKind}; @@ -100,13 +101,15 @@ declare_clippy_lint! { pub struct WildcardImports { warn_on_all: bool, test_modules_deep: u32, + ignored_segments: Vec, } impl WildcardImports { - pub fn new(warn_on_all: bool) -> Self { + pub fn new(warn_on_all: bool, ignored_wildcard_imports: Vec) -> Self { Self { warn_on_all, test_modules_deep: 0, + ignored_segments: ignored_wildcard_imports, } } } @@ -190,6 +193,7 @@ impl WildcardImports { item.span.from_expansion() || is_prelude_import(segments) || (is_super_only_import(segments) && self.test_modules_deep > 0) + || is_ignored_via_config(segments, &self.ignored_segments) } } @@ -198,10 +202,20 @@ impl WildcardImports { fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { segments .iter() - .any(|ps| ps.ident.name.as_str().contains(sym::prelude.as_str())) + .any(|ps| ps.ident.as_str().contains(sym::prelude.as_str())) } // Allow "super::*" imports in tests. fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { segments.len() == 1 && segments[0].ident.name == kw::Super } + +// Allow skipping imports containing user configured segments, +// i.e. "...::utils::...::*" if user put `ignored-wildcard-imports = ["utils"]` in `Clippy.toml` +fn is_ignored_via_config(segments: &[PathSegment<'_>], ignored_segments: &[String]) -> bool { + let segments_set: FxHashSet<&str> = segments.iter().map(|s| s.ident.as_str()).collect(); + let ignored_set: FxHashSet<&str> = ignored_segments.iter().map(String::as_str).collect(); + // segment matching need to be exact instead of using 'contains', in case user unintentionaly put + // a single character in the config thus skipping most of the warnings. + segments_set.intersection(&ignored_set).next().is_some() +} 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 24fdfd945bd68..b1e000abec8ef 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -39,6 +39,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability + ignored-wildcard-imports large-error-threshold literal-representation-threshold matches-for-let-else @@ -117,6 +118,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability + ignored-wildcard-imports large-error-threshold literal-representation-threshold matches-for-let-else diff --git a/tests/ui-toml/wildcard_imports_whitelist/clippy.toml b/tests/ui-toml/wildcard_imports_whitelist/clippy.toml new file mode 100644 index 0000000000000..1d28cac588baa --- /dev/null +++ b/tests/ui-toml/wildcard_imports_whitelist/clippy.toml @@ -0,0 +1 @@ +ignored-wildcard-imports = ["utils"] diff --git a/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed new file mode 100644 index 0000000000000..5ef30b054b3d3 --- /dev/null +++ b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed @@ -0,0 +1,18 @@ +#![warn(clippy::wildcard_imports)] + +mod utils { + pub fn print() {} +} + +mod utils_plus { + pub fn do_something() {} +} + +use utils::*; +use utils_plus::do_something; +//~^ ERROR: usage of wildcard import + +fn main() { + print(); + do_something(); +} diff --git a/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs new file mode 100644 index 0000000000000..5ea91a8d70aea --- /dev/null +++ b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs @@ -0,0 +1,18 @@ +#![warn(clippy::wildcard_imports)] + +mod utils { + pub fn print() {} +} + +mod utils_plus { + pub fn do_something() {} +} + +use utils::*; +use utils_plus::*; +//~^ ERROR: usage of wildcard import + +fn main() { + print(); + do_something(); +} diff --git a/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr new file mode 100644 index 0000000000000..15f522cb5af1c --- /dev/null +++ b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr @@ -0,0 +1,11 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:12:5 + | +LL | use utils_plus::*; + | ^^^^^^^^^^^^^ help: try: `utils_plus::do_something` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` + +error: aborting due to 1 previous error + From 314bddee95f88a405ec4b44db5576e3745ee1699 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Mon, 29 Jan 2024 11:29:15 +0800 Subject: [PATCH 34/68] add more test cases & improve docs & replace `Vec` with `FxHashSet` for segments --- clippy_config/src/conf.rs | 17 +++++++++++++--- clippy_lints/src/wildcard_imports.rs | 10 ++++------ tests/ui-toml/wildcard_imports/clippy.toml | 3 +++ .../wildcard_imports/wildcard_imports.fixed | 19 ++++++++++++++++++ .../wildcard_imports/wildcard_imports.rs | 19 ++++++++++++++++++ .../wildcard_imports/wildcard_imports.stderr | 20 +++++++++++++++---- .../wildcard_imports.fixed | 8 ++++++++ .../wildcard_imports.rs | 8 ++++++++ .../wildcard_imports.stderr | 2 +- 9 files changed, 92 insertions(+), 14 deletions(-) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index e4e512482ac03..d03f805e2fc96 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -573,9 +573,20 @@ define_Conf! { (allow_comparison_to_zero: bool = true), /// Lint: WILDCARD_IMPORTS. /// - /// List of path segments to ignore when checking wildcard imports, - /// could get overrided by `warn_on_all_wildcard_imports`. - (ignored_wildcard_imports: Vec = Vec::new()), + /// List of path segments to ignore when checking wildcard imports. + /// + /// #### Example + /// + /// ```toml + /// ignored-wildcard-imports = [ "utils", "common" ] + /// ``` + /// + /// #### Noteworthy + /// + /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. + /// 2. Paths with any segment that containing the word 'prelude' + /// are already ignored by default. + (ignored_wildcard_imports: FxHashSet = FxHashSet::default()), } /// Search for the configuration file. diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 4eb45e6f8da52..7318ffc958751 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -101,11 +101,11 @@ declare_clippy_lint! { pub struct WildcardImports { warn_on_all: bool, test_modules_deep: u32, - ignored_segments: Vec, + ignored_segments: FxHashSet, } impl WildcardImports { - pub fn new(warn_on_all: bool, ignored_wildcard_imports: Vec) -> Self { + pub fn new(warn_on_all: bool, ignored_wildcard_imports: FxHashSet) -> Self { Self { warn_on_all, test_modules_deep: 0, @@ -212,10 +212,8 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { // Allow skipping imports containing user configured segments, // i.e. "...::utils::...::*" if user put `ignored-wildcard-imports = ["utils"]` in `Clippy.toml` -fn is_ignored_via_config(segments: &[PathSegment<'_>], ignored_segments: &[String]) -> bool { - let segments_set: FxHashSet<&str> = segments.iter().map(|s| s.ident.as_str()).collect(); - let ignored_set: FxHashSet<&str> = ignored_segments.iter().map(String::as_str).collect(); +fn is_ignored_via_config(segments: &[PathSegment<'_>], ignored_segments: &FxHashSet) -> bool { // segment matching need to be exact instead of using 'contains', in case user unintentionaly put // a single character in the config thus skipping most of the warnings. - segments_set.intersection(&ignored_set).next().is_some() + segments.iter().any(|seg| ignored_segments.contains(seg.ident.as_str())) } diff --git a/tests/ui-toml/wildcard_imports/clippy.toml b/tests/ui-toml/wildcard_imports/clippy.toml index 875aaeef6c935..56a945e48bb21 100644 --- a/tests/ui-toml/wildcard_imports/clippy.toml +++ b/tests/ui-toml/wildcard_imports/clippy.toml @@ -1 +1,4 @@ warn-on-all-wildcard-imports = true + +# This should be ignored since `warn-on-all-wildcard-imports` has higher precedence +ignored-wildcard-imports = ["utils"] diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed index 1752f48856c2b..af72d6be0e096 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed @@ -3,9 +3,28 @@ mod prelude { pub const FOO: u8 = 1; } + +mod utils { + pub const BAR: u8 = 1; + pub fn print() {} +} + +mod my_crate { + pub mod utils { + pub fn my_util_fn() {} + } +} + +use utils::{BAR, print}; +//~^ ERROR: usage of wildcard import +use my_crate::utils::my_util_fn; +//~^ ERROR: usage of wildcard import use prelude::FOO; //~^ ERROR: usage of wildcard import fn main() { let _ = FOO; + let _ = BAR; + print(); + my_util_fn(); } diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/tests/ui-toml/wildcard_imports/wildcard_imports.rs index 331c2c59c222f..91009dd8835f8 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.rs +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.rs @@ -3,9 +3,28 @@ mod prelude { pub const FOO: u8 = 1; } + +mod utils { + pub const BAR: u8 = 1; + pub fn print() {} +} + +mod my_crate { + pub mod utils { + pub fn my_util_fn() {} + } +} + +use utils::*; +//~^ ERROR: usage of wildcard import +use my_crate::utils::*; +//~^ ERROR: usage of wildcard import use prelude::*; //~^ ERROR: usage of wildcard import fn main() { let _ = FOO; + let _ = BAR; + print(); + my_util_fn(); } diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr index f11fda6a0c6e1..a733d786d0e64 100644 --- a/tests/ui-toml/wildcard_imports/wildcard_imports.stderr +++ b/tests/ui-toml/wildcard_imports/wildcard_imports.stderr @@ -1,11 +1,23 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:6:5 + --> $DIR/wildcard_imports.rs:18:5 | -LL | use prelude::*; - | ^^^^^^^^^^ help: try: `prelude::FOO` +LL | use utils::*; + | ^^^^^^^^ help: try: `utils::{BAR, print}` | = note: `-D clippy::wildcard-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]` -error: aborting due to 1 previous error +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:20:5 + | +LL | use my_crate::utils::*; + | ^^^^^^^^^^^^^^^^^^ help: try: `my_crate::utils::my_util_fn` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:22:5 + | +LL | use prelude::*; + | ^^^^^^^^^^ help: try: `prelude::FOO` + +error: aborting due to 3 previous errors diff --git a/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed index 5ef30b054b3d3..d539525c45ed6 100644 --- a/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed +++ b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed @@ -8,11 +8,19 @@ mod utils_plus { pub fn do_something() {} } +mod my_crate { + pub mod utils { + pub fn my_util_fn() {} + } +} + +use my_crate::utils::*; use utils::*; use utils_plus::do_something; //~^ ERROR: usage of wildcard import fn main() { print(); + my_util_fn(); do_something(); } diff --git a/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs index 5ea91a8d70aea..9169d16a08be1 100644 --- a/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs +++ b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs @@ -8,11 +8,19 @@ mod utils_plus { pub fn do_something() {} } +mod my_crate { + pub mod utils { + pub fn my_util_fn() {} + } +} + +use my_crate::utils::*; use utils::*; use utils_plus::*; //~^ ERROR: usage of wildcard import fn main() { print(); + my_util_fn(); do_something(); } diff --git a/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr index 15f522cb5af1c..12c2f9cbada70 100644 --- a/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr +++ b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:12:5 + --> $DIR/wildcard_imports.rs:19:5 | LL | use utils_plus::*; | ^^^^^^^^^^^^^ help: try: `utils_plus::do_something` From c0f49a9995d82163cbd6282a3dd38b990e71ccce Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 25 Jan 2024 19:16:38 +0300 Subject: [PATCH 35/68] hir: Simplify `hir_owner_nodes` query The query accept arbitrary DefIds, not just owner DefIds. The return can be an `Option` because if there are no nodes, then it doesn't matter whether it's due to NonOwner or Phantom. Also rename the query to `opt_hir_owner_nodes`. --- clippy_lints/src/min_ident_chars.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 34b8e0dbe6a7e..2b0063f62d943 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -93,7 +93,7 @@ impl Visitor<'_> for IdentVisitor<'_, '_> { // reimplement it even if we wanted to cx.tcx.opt_hir_node(hir_id) } else { - let Some(owner) = cx.tcx.hir_owner_nodes(hir_id.owner).as_owner() else { + let Some(owner) = cx.tcx.opt_hir_owner_nodes(hir_id.owner) else { return; }; owner.nodes.get(hir_id.local_id).copied().flatten().map(|p| p.node) From 7539054b565f7a21355f16f85b9722fadcd28c4c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 25 Jan 2024 19:34:49 +0300 Subject: [PATCH 36/68] hir: Add non-optional `hir_owner_nodes` for real `OwnerId`s --- clippy_lints/src/min_ident_chars.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 2b0063f62d943..41168230752a8 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -93,9 +93,7 @@ impl Visitor<'_> for IdentVisitor<'_, '_> { // reimplement it even if we wanted to cx.tcx.opt_hir_node(hir_id) } else { - let Some(owner) = cx.tcx.opt_hir_owner_nodes(hir_id.owner) else { - return; - }; + let owner = cx.tcx.hir_owner_nodes(hir_id.owner); owner.nodes.get(hir_id.local_id).copied().flatten().map(|p| p.node) }; let Some(node) = node else { From 233c8c9df98dd319fd647804b7062e61e95d6ed9 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 25 Jan 2024 20:47:03 +0300 Subject: [PATCH 37/68] hir: Remove `hir::Map::{owner,expect_owner}` --- clippy_lints/src/methods/iter_nth_zero.rs | 2 +- clippy_lints/src/returns.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index e1f950d5a4a5f..262a57ab591a6 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -11,7 +11,7 @@ use rustc_span::sym; use super::ITER_NTH_ZERO; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { - if let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id)) + if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id)) && let def_id = item.owner_id.to_def_id() && is_trait_method(cx, expr, sym::Iterator) && let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2293b53b42b91..e01750465873d 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { && let ExprKind::Ret(Some(ret)) = expr.kind && let ExprKind::Match(.., MatchSource::TryDesugar(_)) = ret.kind // Ensure this is not the final stmt, otherwise removing it would cause a compile error - && let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id)) + && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id)) && let ItemKind::Fn(_, _, body) = item.kind && let block = cx.tcx.hir().body(body).value && let ExprKind::Block(block, _) = block.kind From 3106219e24c9d52a739ffaf38c10f0c1357f30f3 Mon Sep 17 00:00:00 2001 From: Bruno Andreotti Date: Tue, 30 Jan 2024 16:17:02 -0300 Subject: [PATCH 38/68] Don't lint slice type annotations for byte strings --- clippy_lints/src/redundant_type_annotations.rs | 11 ++++++++++- tests/ui/redundant_type_annotations.rs | 7 ++++++- tests/ui/redundant_type_annotations.stderr | 4 ++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/redundant_type_annotations.rs b/clippy_lints/src/redundant_type_annotations.rs index 07fcb69afbc4c..c8352c052659b 100644 --- a/clippy_lints/src/redundant_type_annotations.rs +++ b/clippy_lints/src/redundant_type_annotations.rs @@ -188,7 +188,6 @@ impl LateLintPass<'_> for RedundantTypeAnnotations { match init_lit.node { // In these cases the annotation is redundant LitKind::Str(..) - | LitKind::ByteStr(..) | LitKind::Byte(..) | LitKind::Char(..) | LitKind::Bool(..) @@ -202,6 +201,16 @@ impl LateLintPass<'_> for RedundantTypeAnnotations { } }, LitKind::Err => (), + LitKind::ByteStr(..) => { + // We only lint if the type annotation is an array type (e.g. &[u8; 4]). + // If instead it is a slice (e.g. &[u8]) it may not be redundant, so we + // don't lint. + if let hir::TyKind::Ref(_, mut_ty) = ty.kind + && matches!(mut_ty.ty.kind, hir::TyKind::Array(..)) + { + span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation"); + } + }, } }, _ => (), diff --git a/tests/ui/redundant_type_annotations.rs b/tests/ui/redundant_type_annotations.rs index acf53fea2bb2c..dc9b073ffba85 100644 --- a/tests/ui/redundant_type_annotations.rs +++ b/tests/ui/redundant_type_annotations.rs @@ -196,13 +196,18 @@ fn test_simple_types() { let _var: &str = "test"; //~^ ERROR: redundant type annotation - let _var: &[u8] = b"test"; + let _var: &[u8; 4] = b"test"; //~^ ERROR: redundant type annotation let _var: bool = false; //~^ ERROR: redundant type annotation } +fn issue12212() { + // This should not be linted + let _var: &[u8] = b"test"; +} + fn issue11190() {} fn main() {} diff --git a/tests/ui/redundant_type_annotations.stderr b/tests/ui/redundant_type_annotations.stderr index d1f26f1832e39..48df465ad4971 100644 --- a/tests/ui/redundant_type_annotations.stderr +++ b/tests/ui/redundant_type_annotations.stderr @@ -94,8 +94,8 @@ LL | let _var: &str = "test"; error: redundant type annotation --> $DIR/redundant_type_annotations.rs:199:5 | -LL | let _var: &[u8] = b"test"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _var: &[u8; 4] = b"test"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation --> $DIR/redundant_type_annotations.rs:202:5 From ae0f0fd6552cfbaafff10f15f75046c8ce37adbf Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 30 Jan 2024 16:57:29 +1100 Subject: [PATCH 39/68] Don't hash lints differently to non-lints. `Diagnostic::keys`, which is used for hashing and equating diagnostics, has a surprising behaviour: it ignores children, but only for lints. This was added in #88493 to fix some duplicated diagnostics, but it doesn't seem necessary any more. This commit removes the special case and only four tests have changed output, with additional errors. And those additional errors aren't exact duplicates, they're just similar. For example, in src/tools/clippy/tests/ui/same_name_method.rs we currently have this error: ``` error: method's name is the same as an existing method in a trait --> $DIR/same_name_method.rs:75:13 | LL | fn foo() {} | ^^^^^^^^^^^ | note: existing `foo` defined here --> $DIR/same_name_method.rs:79:9 | LL | impl T1 for S {} | ^^^^^^^^^^^^^^^^ ``` and with this change we also get this error: ``` error: method's name is the same as an existing method in a trait --> $DIR/same_name_method.rs:75:13 | LL | fn foo() {} | ^^^^^^^^^^^ | note: existing `foo` defined here --> $DIR/same_name_method.rs:81:9 | LL | impl T2 for S {} | ^^^^^^^^^^^^^^^^ ``` I think printing this second argument is reasonable, possibly even preferable to hiding it. And the other cases are similar. --- tests/ui/same_name_method.rs | 1 + tests/ui/same_name_method.stderr | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/ui/same_name_method.rs b/tests/ui/same_name_method.rs index 1c166a19b0ad7..26b1a299ba1cf 100644 --- a/tests/ui/same_name_method.rs +++ b/tests/ui/same_name_method.rs @@ -74,6 +74,7 @@ mod should_lint { impl S { fn foo() {} //~^ ERROR: method's name is the same as an existing method in a trait + //~| ERROR: method's name is the same as an existing method in a trait } impl T1 for S {} diff --git a/tests/ui/same_name_method.stderr b/tests/ui/same_name_method.stderr index 3c5c4a53ad1f6..82f5ef6a9e827 100644 --- a/tests/ui/same_name_method.stderr +++ b/tests/ui/same_name_method.stderr @@ -56,10 +56,22 @@ LL | fn foo() {} | ^^^^^^^^^^^ | note: existing `foo` defined here - --> $DIR/same_name_method.rs:79:9 + --> $DIR/same_name_method.rs:80:9 | LL | impl T1 for S {} | ^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: method's name is the same as an existing method in a trait + --> $DIR/same_name_method.rs:75:13 + | +LL | fn foo() {} + | ^^^^^^^^^^^ + | +note: existing `foo` defined here + --> $DIR/same_name_method.rs:82:9 + | +LL | impl T2 for S {} + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors From 6619e8c27da89c8fc7972eecc410b39ddc250bd7 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 31 Jan 2024 18:33:41 +0000 Subject: [PATCH 40/68] Add `lint_groups_priority` lint Warns when a lint group in Cargo.toml's `[lints]` section shares the same priority as a lint --- CHANGELOG.md | 1 + .../src/cargo/lint_groups_priority.rs | 168 ++++++++++++++++++ clippy_lints/src/cargo/mod.rs | 43 ++++- clippy_lints/src/declared_lints.rs | 1 + .../lint_groups_priority/fail/Cargo.stderr | 45 +++++ .../lint_groups_priority/fail/Cargo.toml | 20 +++ .../lint_groups_priority/fail/src/lib.rs | 1 + .../lint_groups_priority/pass/Cargo.toml | 10 ++ .../lint_groups_priority/pass/src/lib.rs | 1 + 9 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/cargo/lint_groups_priority.rs create mode 100644 tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr create mode 100644 tests/ui-cargo/lint_groups_priority/fail/Cargo.toml create mode 100644 tests/ui-cargo/lint_groups_priority/fail/src/lib.rs create mode 100644 tests/ui-cargo/lint_groups_priority/pass/Cargo.toml create mode 100644 tests/ui-cargo/lint_groups_priority/pass/src/lib.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 471163499e521..e19a1ae02625c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5277,6 +5277,7 @@ Released 2018-09-13 [`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore [`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist +[`lint_groups_priority`]: https://rust-lang.github.io/rust-clippy/master/index.html#lint_groups_priority [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs new file mode 100644 index 0000000000000..a39b972b56a23 --- /dev/null +++ b/clippy_lints/src/cargo/lint_groups_priority.rs @@ -0,0 +1,168 @@ +use super::LINT_GROUPS_PRIORITY; +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_lint::{unerased_lint_store, LateContext}; +use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::ops::Range; +use std::path::Path; +use toml::Spanned; + +#[derive(Deserialize, Serialize, Debug)] +struct LintConfigTable { + level: String, + priority: Option, +} + +#[derive(Deserialize, Debug)] +#[serde(untagged)] +enum LintConfig { + Level(String), + Table(LintConfigTable), +} + +impl LintConfig { + fn level(&self) -> &str { + match self { + LintConfig::Level(level) => level, + LintConfig::Table(table) => &table.level, + } + } + + fn priority(&self) -> i64 { + match self { + LintConfig::Level(_) => 0, + LintConfig::Table(table) => table.priority.unwrap_or(0), + } + } + + fn is_implicit(&self) -> bool { + if let LintConfig::Table(table) = self { + table.priority.is_none() + } else { + true + } + } +} + +type LintTable = BTreeMap, Spanned>; + +#[derive(Deserialize, Debug)] +struct Lints { + #[serde(default)] + rust: LintTable, + #[serde(default)] + clippy: LintTable, +} + +#[derive(Deserialize, Debug)] +struct CargoToml { + lints: Lints, +} + +#[derive(Default, Debug)] +struct LintsAndGroups { + lints: Vec>, + groups: Vec<(Spanned, Spanned)>, +} + +fn toml_span(range: Range, file: &SourceFile) -> Span { + Span::new( + file.start_pos + BytePos::from_usize(range.start), + file.start_pos + BytePos::from_usize(range.end), + SyntaxContext::root(), + None, + ) +} + +fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, file: &SourceFile) { + let mut by_priority = BTreeMap::<_, LintsAndGroups>::new(); + for (name, config) in table { + let lints_and_groups = by_priority.entry(config.as_ref().priority()).or_default(); + if groups.contains(name.get_ref().as_str()) { + lints_and_groups.groups.push((name, config)); + } else { + lints_and_groups.lints.push(name); + } + } + let low_priority = by_priority + .iter() + .find(|(_, lints_and_groups)| !lints_and_groups.lints.is_empty()) + .map_or(-1, |(&lowest_lint_priority, _)| lowest_lint_priority - 1); + + for (priority, LintsAndGroups { lints, groups }) in by_priority { + let Some(last_lint_alphabetically) = lints.last() else { + continue; + }; + + for (group, config) in groups { + span_lint_and_then( + cx, + LINT_GROUPS_PRIORITY, + toml_span(group.span(), file), + &format!( + "lint group `{}` has the same priority ({priority}) as a lint", + group.as_ref() + ), + |diag| { + let config_span = toml_span(config.span(), file); + if config.as_ref().is_implicit() { + diag.span_label(config_span, "has an implicit priority of 0"); + } + // add the label to next lint after this group that has the same priority + let lint = lints + .iter() + .filter(|lint| lint.span().start > group.span().start) + .min_by_key(|lint| lint.span().start) + .unwrap_or(last_lint_alphabetically); + diag.span_label(toml_span(lint.span(), file), "has the same priority as this lint"); + diag.note("the order of the lints in the table is ignored by Cargo"); + let mut suggestion = String::new(); + Serialize::serialize( + &LintConfigTable { + level: config.as_ref().level().into(), + priority: Some(low_priority), + }, + toml::ser::ValueSerializer::new(&mut suggestion), + ) + .unwrap(); + diag.span_suggestion_verbose( + config_span, + format!( + "to have lints override the group set `{}` to a lower priority", + group.as_ref() + ), + suggestion, + Applicability::MaybeIncorrect, + ); + }, + ); + } + } +} + +pub fn check(cx: &LateContext<'_>) { + if let Ok(file) = cx.tcx.sess.source_map().load_file(Path::new("Cargo.toml")) + && let Some(src) = file.src.as_deref() + && let Ok(cargo_toml) = toml::from_str::(src) + { + let mut rustc_groups = FxHashSet::default(); + let mut clippy_groups = FxHashSet::default(); + for (group, ..) in unerased_lint_store(cx.tcx.sess).get_lint_groups() { + match group.split_once("::") { + None => { + rustc_groups.insert(group); + }, + Some(("clippy", group)) => { + clippy_groups.insert(group); + }, + _ => {}, + } + } + + check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file); + check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file); + } +} diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index d8107f61f371c..95d5449781b47 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -1,5 +1,6 @@ mod common_metadata; mod feature_name; +mod lint_groups_priority; mod multiple_crate_versions; mod wildcard_dependencies; @@ -165,6 +166,43 @@ declare_clippy_lint! { "wildcard dependencies being used" } +declare_clippy_lint! { + /// ### What it does + /// Checks for lint groups with the same priority as lints in the `Cargo.toml` + /// [`[lints]` table](https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section). + /// + /// This lint will be removed once [cargo#12918](https://github.com/rust-lang/cargo/issues/12918) + /// is resolved. + /// + /// ### Why is this bad? + /// The order of lints in the `[lints]` is ignored, to have a lint override a group the + /// `priority` field needs to be used, otherwise the sort order is undefined. + /// + /// ### Known problems + /// Does not check lints inherited using `lints.workspace = true` + /// + /// ### Example + /// ```toml + /// # Passed as `--allow=clippy::similar_names --warn=clippy::pedantic` + /// # which results in `similar_names` being `warn` + /// [lints.clippy] + /// pedantic = "warn" + /// similar_names = "allow" + /// ``` + /// Use instead: + /// ```toml + /// # Passed as `--warn=clippy::pedantic --allow=clippy::similar_names` + /// # which results in `similar_names` being `allow` + /// [lints.clippy] + /// pedantic = { level = "warn", priority = -1 } + /// similar_names = "allow" + /// ``` + #[clippy::version = "1.76.0"] + pub LINT_GROUPS_PRIORITY, + correctness, + "a lint group in `Cargo.toml` at the same priority as a lint" +} + pub struct Cargo { pub allowed_duplicate_crates: FxHashSet, pub ignore_publish: bool, @@ -175,7 +213,8 @@ impl_lint_pass!(Cargo => [ REDUNDANT_FEATURE_NAMES, NEGATIVE_FEATURE_NAMES, MULTIPLE_CRATE_VERSIONS, - WILDCARD_DEPENDENCIES + WILDCARD_DEPENDENCIES, + LINT_GROUPS_PRIORITY, ]); impl LateLintPass<'_> for Cargo { @@ -188,6 +227,8 @@ impl LateLintPass<'_> for Cargo { ]; static WITH_DEPS_LINTS: &[&Lint] = &[MULTIPLE_CRATE_VERSIONS]; + lint_groups_priority::check(cx); + if !NO_DEPS_LINTS .iter() .all(|&lint| is_lint_allowed(cx, lint, CRATE_HIR_ID)) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 23d0983151a96..b96a7af907007 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -71,6 +71,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::borrow_deref_ref::BORROW_DEREF_REF_INFO, crate::box_default::BOX_DEFAULT_INFO, crate::cargo::CARGO_COMMON_METADATA_INFO, + crate::cargo::LINT_GROUPS_PRIORITY_INFO, crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO, crate::cargo::NEGATIVE_FEATURE_NAMES_INFO, crate::cargo::REDUNDANT_FEATURE_NAMES_INFO, diff --git a/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr b/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr new file mode 100644 index 0000000000000..103e60d84844c --- /dev/null +++ b/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr @@ -0,0 +1,45 @@ +error: lint group `rust_2018_idioms` has the same priority (0) as a lint + --> Cargo.toml:7:1 + | +7 | rust_2018_idioms = "warn" + | ^^^^^^^^^^^^^^^^ ------ has an implicit priority of 0 +8 | bare_trait_objects = "allow" + | ------------------ has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo + = note: `#[deny(clippy::lint_groups_priority)]` on by default +help: to have lints override the group set `rust_2018_idioms` to a lower priority + | +7 | rust_2018_idioms = { level = "warn", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: lint group `unused` has the same priority (0) as a lint + --> Cargo.toml:10:1 + | +10 | unused = { level = "deny" } + | ^^^^^^ ------------------ has an implicit priority of 0 +11 | unused_braces = { level = "allow", priority = 1 } +12 | unused_attributes = { level = "allow" } + | ----------------- has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo +help: to have lints override the group set `unused` to a lower priority + | +10 | unused = { level = "deny", priority = -1 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: lint group `pedantic` has the same priority (-1) as a lint + --> Cargo.toml:19:1 + | +19 | pedantic = { level = "warn", priority = -1 } + | ^^^^^^^^ +20 | similar_names = { level = "allow", priority = -1 } + | ------------- has the same priority as this lint + | + = note: the order of the lints in the table is ignored by Cargo +help: to have lints override the group set `pedantic` to a lower priority + | +19 | pedantic = { level = "warn", priority = -2 } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: could not compile `fail` (lib) due to 3 previous errors diff --git a/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml b/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml new file mode 100644 index 0000000000000..4ce41f7817118 --- /dev/null +++ b/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "fail" +version = "0.1.0" +publish = false + +[lints.rust] +rust_2018_idioms = "warn" +bare_trait_objects = "allow" + +unused = { level = "deny" } +unused_braces = { level = "allow", priority = 1 } +unused_attributes = { level = "allow" } + +# `warnings` is not a group so the order it is passed does not matter +warnings = "deny" +deprecated = "allow" + +[lints.clippy] +pedantic = { level = "warn", priority = -1 } +similar_names = { level = "allow", priority = -1 } diff --git a/tests/ui-cargo/lint_groups_priority/fail/src/lib.rs b/tests/ui-cargo/lint_groups_priority/fail/src/lib.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/lint_groups_priority/fail/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml b/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml new file mode 100644 index 0000000000000..e9fcf803d9369 --- /dev/null +++ b/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "pass" +version = "0.1.0" +publish = false + +[lints.clippy] +pedantic = { level = "warn", priority = -1 } +style = { level = "warn", priority = 1 } +similar_names = "allow" +dbg_macro = { level = "warn", priority = 2 } diff --git a/tests/ui-cargo/lint_groups_priority/pass/src/lib.rs b/tests/ui-cargo/lint_groups_priority/pass/src/lib.rs new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/ui-cargo/lint_groups_priority/pass/src/lib.rs @@ -0,0 +1 @@ + From fe8c2e24bd5697d7bf4f0b55bf2e88aa9096d4bb Mon Sep 17 00:00:00 2001 From: Quinn Sinclair Date: Tue, 30 Jan 2024 21:41:26 +0100 Subject: [PATCH 41/68] Fixed FP in `unused_io_amount` for `Ok(lit)`, `unrachable!` We introduce the following rules for match exprs. - `panic!` and `unreachable!` are treated as consumption. - guard expressions in any arm imply consumption. For match exprs: - Lint only if exacrtly 2 non-consuming arms exist - Lint only if one arm is an `Ok(_)` and the other is `Err(_)` Added additional requirement that for a block return expression that is a match, the source must be `Normal`. changelog: FP [`unused_io_amount`] when matching Ok(literal) --- clippy_lints/src/unused_io_amount.rs | 101 ++++++++++++++++++++------- tests/ui/unused_io_amount.rs | 43 ++++++++++++ tests/ui/unused_io_amount.stderr | 8 +-- 3 files changed, 123 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index adc66e15ff501..6b3ea7700b735 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths}; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks}; use hir::{ExprKind, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -82,37 +83,72 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } if let Some(exp) = block.expr - && matches!(exp.kind, hir::ExprKind::If(_, _, _) | hir::ExprKind::Match(_, _, _)) + && matches!( + exp.kind, + hir::ExprKind::If(_, _, _) | hir::ExprKind::Match(_, _, hir::MatchSource::Normal) + ) { check_expr(cx, exp); } } } +fn non_consuming_err_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool { + // if there is a guard, we consider the result to be consumed + if arm.guard.is_some() { + return false; + } + if is_unreachable_or_panic(cx, arm.body) { + // if the body is unreachable or there is a panic, + // we consider the result to be consumed + return false; + } + + if let PatKind::TupleStruct(ref path, [inner_pat], _) = arm.pat.kind { + return is_res_lang_ctor(cx, cx.qpath_res(path, inner_pat.hir_id), hir::LangItem::ResultErr); + } + + false +} + +fn non_consuming_ok_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool { + // if there is a guard, we consider the result to be consumed + if arm.guard.is_some() { + return false; + } + if is_unreachable_or_panic(cx, arm.body) { + // if the body is unreachable or there is a panic, + // we consider the result to be consumed + return false; + } + + if is_ok_wild_or_dotdot_pattern(cx, arm.pat) { + return true; + } + false +} + fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { match expr.kind { hir::ExprKind::If(cond, _, _) if let ExprKind::Let(hir::Let { pat, init, .. }) = cond.kind - && pattern_is_ignored_ok(cx, pat) + && is_ok_wild_or_dotdot_pattern(cx, pat) && let Some(op) = should_lint(cx, init) => { emit_lint(cx, cond.span, op, &[pat.span]); }, - hir::ExprKind::Match(expr, arms, hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => { - let found_arms: Vec<_> = arms - .iter() - .filter_map(|arm| { - if pattern_is_ignored_ok(cx, arm.pat) { - Some(arm.span) - } else { - None - } - }) - .collect(); - if !found_arms.is_empty() { - emit_lint(cx, expr.span, op, found_arms.as_slice()); + // we will capture only the case where the match is Ok( ) or Err( ) + // prefer to match the minimum possible, and expand later if needed + // to avoid false positives on something as used as this + hir::ExprKind::Match(expr, [arm1, arm2], hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => { + if non_consuming_ok_arm(cx, arm1) && non_consuming_err_arm(cx, arm2) { + emit_lint(cx, expr.span, op, &[arm1.pat.span]); + } + if non_consuming_ok_arm(cx, arm2) && non_consuming_err_arm(cx, arm1) { + emit_lint(cx, expr.span, op, &[arm2.pat.span]); } }, + hir::ExprKind::Match(_, _, hir::MatchSource::Normal) => {}, _ if let Some(op) = should_lint(cx, expr) => { emit_lint(cx, expr.span, op, &[]); }, @@ -130,25 +166,40 @@ fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option check_io_mode(cx, inner) } -fn pattern_is_ignored_ok(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> bool { +fn is_ok_wild_or_dotdot_pattern<'a>(cx: &LateContext<'a>, pat: &hir::Pat<'a>) -> bool { // the if checks whether we are in a result Ok( ) pattern // and the return checks whether it is unhandled - if let PatKind::TupleStruct(ref path, inner_pat, ddp) = pat.kind + if let PatKind::TupleStruct(ref path, inner_pat, _) = pat.kind // we check against Result::Ok to avoid linting on Err(_) or something else. && is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk) { - return match (inner_pat, ddp.as_opt_usize()) { - // Ok(_) pattern - ([inner_pat], None) if matches!(inner_pat.kind, PatKind::Wild) => true, - // Ok(..) pattern - ([], Some(0)) => true, - _ => false, - }; + if matches!(inner_pat, []) { + return true; + } + + if let [cons_pat] = inner_pat + && matches!(cons_pat.kind, PatKind::Wild) + { + return true; + } + return false; } false } +// this is partially taken from panic_unimplemented +fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + let expr = peel_blocks(expr); + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return false; + }; + if is_panic(cx, macro_call.def_id) { + return !cx.tcx.hir().is_inside_const_context(expr.hir_id); + } + matches!(cx.tcx.item_name(macro_call.def_id).as_str(), "unreachable") +} + fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { while let hir::ExprKind::MethodCall(path, receiver, ..) = expr.kind { if matches!( diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 9974600dad5be..7e5a10c911bf1 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -229,4 +229,47 @@ fn on_return_should_not_raise(s: &mut T) -> io::Result< s.read(&mut buf) } +pub fn unwrap_in_block(rdr: &mut dyn std::io::Read) -> std::io::Result { + let read = { rdr.read(&mut [0])? }; + Ok(read) +} + +pub fn consumed_example(rdr: &mut dyn std::io::Read) { + match rdr.read(&mut [0]) { + Ok(0) => println!("EOF"), + Ok(_) => println!("fully read"), + Err(_) => println!("fail"), + }; + match rdr.read(&mut [0]) { + Ok(0) => println!("EOF"), + Ok(_) => println!("fully read"), + Err(_) => println!("fail"), + } +} + +pub fn unreachable_or_panic(rdr: &mut dyn std::io::Read) { + { + match rdr.read(&mut [0]) { + Ok(_) => unreachable!(), + Err(_) => println!("expected"), + } + } + + { + match rdr.read(&mut [0]) { + Ok(_) => panic!(), + Err(_) => println!("expected"), + } + } +} + +pub fn wildcards(rdr: &mut dyn std::io::Read) { + { + match rdr.read(&mut [0]) { + Ok(1) => todo!(), + _ => todo!(), + } + } +} + fn main() {} diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index 4af56d264bfa1..1aab56966a88c 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -180,7 +180,7 @@ note: the result is consumed here, but the amount of I/O bytes remains unhandled --> $DIR/unused_io_amount.rs:149:9 | LL | Ok(_) => todo!(), - | ^^^^^^^^^^^^^^^^ + | ^^^^^ error: read amount is not handled --> $DIR/unused_io_amount.rs:155:11 @@ -193,7 +193,7 @@ note: the result is consumed here, but the amount of I/O bytes remains unhandled --> $DIR/unused_io_amount.rs:157:9 | LL | Ok(_) => todo!(), - | ^^^^^^^^^^^^^^^^ + | ^^^^^ error: read amount is not handled --> $DIR/unused_io_amount.rs:164:11 @@ -206,7 +206,7 @@ note: the result is consumed here, but the amount of I/O bytes remains unhandled --> $DIR/unused_io_amount.rs:166:9 | LL | Ok(_) => todo!(), - | ^^^^^^^^^^^^^^^^ + | ^^^^^ error: written amount is not handled --> $DIR/unused_io_amount.rs:173:11 @@ -219,7 +219,7 @@ note: the result is consumed here, but the amount of I/O bytes remains unhandled --> $DIR/unused_io_amount.rs:175:9 | LL | Ok(_) => todo!(), - | ^^^^^^^^^^^^^^^^ + | ^^^^^ error: read amount is not handled --> $DIR/unused_io_amount.rs:186:8 From 46dd8263a08a09e522d6990d11a3aa5e58702a50 Mon Sep 17 00:00:00 2001 From: J-ZhengLi Date: Fri, 2 Feb 2024 09:22:42 +0800 Subject: [PATCH 42/68] rename conf option to `allowed_wildcard_imports` --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 22 +++++++++++++++++++ clippy_config/src/conf.rs | 8 +++---- clippy_lints/src/lib.rs | 4 ++-- clippy_lints/src/wildcard_imports.rs | 14 ++++++------ .../toml_unknown_key/conf_unknown_key.stderr | 5 +++-- tests/ui-toml/wildcard_imports/clippy.toml | 2 +- .../wildcard_imports_whitelist/clippy.toml | 2 +- 8 files changed, 41 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 471163499e521..c4cddd0b4b888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5823,4 +5823,5 @@ Released 2018-09-13 [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior [`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero +[`allowed-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-wildcard-imports diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 1b321b8bcb81a..669cdb0735dcc 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -838,3 +838,25 @@ Don't lint when comparing the result of a modulo operation to zero. * [`modulo_arithmetic`](https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic) +## `allowed-wildcard-imports` +List of path segments allowed to have wildcard imports. + +#### Example + +```toml +allowed-wildcard-imports = [ "utils", "common" ] +``` + +#### Noteworthy + +1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. +2. Paths with any segment that containing the word 'prelude' +are already allowed by default. + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) + + diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index d03f805e2fc96..a7a81b6f4219e 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -573,20 +573,20 @@ define_Conf! { (allow_comparison_to_zero: bool = true), /// Lint: WILDCARD_IMPORTS. /// - /// List of path segments to ignore when checking wildcard imports. + /// List of path segments allowed to have wildcard imports. /// /// #### Example /// /// ```toml - /// ignored-wildcard-imports = [ "utils", "common" ] + /// allowed-wildcard-imports = [ "utils", "common" ] /// ``` /// /// #### Noteworthy /// /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. /// 2. Paths with any segment that containing the word 'prelude' - /// are already ignored by default. - (ignored_wildcard_imports: FxHashSet = FxHashSet::default()), + /// are already allowed by default. + (allowed_wildcard_imports: FxHashSet = FxHashSet::default()), } /// Search for the configuration file. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 678058a5411a4..5636f46b22fee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -524,6 +524,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { ref allowed_dotfiles, ref allowed_idents_below_min_chars, ref allowed_scripts, + ref allowed_wildcard_imports, ref arithmetic_side_effects_allowed_binary, ref arithmetic_side_effects_allowed_unary, ref arithmetic_side_effects_allowed, @@ -545,7 +546,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { excessive_nesting_threshold, future_size_threshold, ref ignore_interior_mutability, - ref ignored_wildcard_imports, large_error_threshold, literal_representation_threshold, matches_for_let_else, @@ -880,7 +880,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| { Box::new(wildcard_imports::WildcardImports::new( warn_on_all_wildcard_imports, - ignored_wildcard_imports.clone(), + allowed_wildcard_imports.clone(), )) }); store.register_late_pass(|_| Box::::default()); diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 7318ffc958751..5410e8ac11781 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -101,15 +101,15 @@ declare_clippy_lint! { pub struct WildcardImports { warn_on_all: bool, test_modules_deep: u32, - ignored_segments: FxHashSet, + allowed_segments: FxHashSet, } impl WildcardImports { - pub fn new(warn_on_all: bool, ignored_wildcard_imports: FxHashSet) -> Self { + pub fn new(warn_on_all: bool, allowed_wildcard_imports: FxHashSet) -> Self { Self { warn_on_all, test_modules_deep: 0, - ignored_segments: ignored_wildcard_imports, + allowed_segments: allowed_wildcard_imports, } } } @@ -193,7 +193,7 @@ impl WildcardImports { item.span.from_expansion() || is_prelude_import(segments) || (is_super_only_import(segments) && self.test_modules_deep > 0) - || is_ignored_via_config(segments, &self.ignored_segments) + || is_allowed_via_config(segments, &self.allowed_segments) } } @@ -211,9 +211,9 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { } // Allow skipping imports containing user configured segments, -// i.e. "...::utils::...::*" if user put `ignored-wildcard-imports = ["utils"]` in `Clippy.toml` -fn is_ignored_via_config(segments: &[PathSegment<'_>], ignored_segments: &FxHashSet) -> bool { +// i.e. "...::utils::...::*" if user put `allowed-wildcard-imports = ["utils"]` in `Clippy.toml` +fn is_allowed_via_config(segments: &[PathSegment<'_>], allowed_segments: &FxHashSet) -> bool { // segment matching need to be exact instead of using 'contains', in case user unintentionaly put // a single character in the config thus skipping most of the warnings. - segments.iter().any(|seg| ignored_segments.contains(seg.ident.as_str())) + segments.iter().any(|seg| allowed_segments.contains(seg.ident.as_str())) } 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 b1e000abec8ef..f097d2503e163 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -15,6 +15,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allowed-duplicate-crates allowed-idents-below-min-chars allowed-scripts + allowed-wildcard-imports arithmetic-side-effects-allowed arithmetic-side-effects-allowed-binary arithmetic-side-effects-allowed-unary @@ -39,7 +40,6 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability - ignored-wildcard-imports large-error-threshold literal-representation-threshold matches-for-let-else @@ -94,6 +94,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allowed-duplicate-crates allowed-idents-below-min-chars allowed-scripts + allowed-wildcard-imports arithmetic-side-effects-allowed arithmetic-side-effects-allowed-binary arithmetic-side-effects-allowed-unary @@ -118,7 +119,6 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect excessive-nesting-threshold future-size-threshold ignore-interior-mutability - ignored-wildcard-imports large-error-threshold literal-representation-threshold matches-for-let-else @@ -173,6 +173,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allowed-duplicate-crates allowed-idents-below-min-chars allowed-scripts + allowed-wildcard-imports arithmetic-side-effects-allowed arithmetic-side-effects-allowed-binary arithmetic-side-effects-allowed-unary diff --git a/tests/ui-toml/wildcard_imports/clippy.toml b/tests/ui-toml/wildcard_imports/clippy.toml index 56a945e48bb21..68815c1756ac2 100644 --- a/tests/ui-toml/wildcard_imports/clippy.toml +++ b/tests/ui-toml/wildcard_imports/clippy.toml @@ -1,4 +1,4 @@ warn-on-all-wildcard-imports = true # This should be ignored since `warn-on-all-wildcard-imports` has higher precedence -ignored-wildcard-imports = ["utils"] +allowed-wildcard-imports = ["utils"] diff --git a/tests/ui-toml/wildcard_imports_whitelist/clippy.toml b/tests/ui-toml/wildcard_imports_whitelist/clippy.toml index 1d28cac588baa..6b7882e64a8bb 100644 --- a/tests/ui-toml/wildcard_imports_whitelist/clippy.toml +++ b/tests/ui-toml/wildcard_imports_whitelist/clippy.toml @@ -1 +1 @@ -ignored-wildcard-imports = ["utils"] +allowed-wildcard-imports = ["utils"] From abced206d7b9f07f39c1324913f498851e948799 Mon Sep 17 00:00:00 2001 From: Seo Sanghyeon Date: Thu, 1 Feb 2024 06:09:55 +0900 Subject: [PATCH 43/68] Avoid deleting labeled blocks --- clippy_lints/src/no_effect.rs | 2 +- tests/ui/unnecessary_operation.fixed | 7 +++++++ tests/ui/unnecessary_operation.rs | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 0d234f7f9b52c..580160efeb701 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -357,7 +357,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option { - if block.stmts.is_empty() { + if block.stmts.is_empty() && !block.targeted_by_break { block.expr.as_ref().and_then(|e| { match block.rules { BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None, diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index 463412daec08d..11761c6c90e36 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -106,4 +106,11 @@ fn main() { // Issue #11885 Cout << 16; + + // Issue #11575 + // Bad formatting is required to trigger the bug + #[rustfmt::skip] + 'label: { + break 'label + }; } diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index f0d28e2890291..de0081289ac3d 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -110,4 +110,11 @@ fn main() { // Issue #11885 Cout << 16; + + // Issue #11575 + // Bad formatting is required to trigger the bug + #[rustfmt::skip] + 'label: { + break 'label + }; } From be47e32e9e6e27ce8ff5c674e4d2104761b5c875 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 4 Feb 2024 16:18:31 +0100 Subject: [PATCH 44/68] Update version attributes for 1.76 lints --- clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/iter_over_hash_type.rs | 2 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/repeat_vec_with_capacity.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index ba452775015dd..2b4ce6ddfaaa3 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -226,7 +226,7 @@ declare_clippy_lint! { /// unimplemented!(); /// } /// ``` - #[clippy::version = "1.40.0"] + #[clippy::version = "1.76.0"] pub TEST_ATTR_IN_DOCTEST, suspicious, "presence of `#[test]` in code examples" diff --git a/clippy_lints/src/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index 8110c1970d9b1..6c6eff9ba48bd 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// let value = &my_map[key]; /// } /// ``` - #[clippy::version = "1.75.0"] + #[clippy::version = "1.76.0"] pub ITER_OVER_HASH_TYPE, restriction, "iterating over unordered hash-based types (`HashMap` and `HashSet`)" diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 3c9bde86bb6f8..b5e39b33c6a15 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -672,7 +672,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.75.0"] + #[clippy::version = "1.76.0"] pub INFINITE_LOOP, restriction, "possibly unintended infinite loop" diff --git a/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs index 5a4933a3fceb6..fcb79f6d694ee 100644 --- a/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/clippy_lints/src/repeat_vec_with_capacity.rs @@ -42,7 +42,7 @@ declare_clippy_lint! { /// // ^^^ this closure executes 123 times /// // and the vecs will have the expected capacity /// ``` - #[clippy::version = "1.74.0"] + #[clippy::version = "1.76.0"] pub REPEAT_VEC_WITH_CAPACITY, suspicious, "repeating a `Vec::with_capacity` expression which does not retain capacity" From aa1de4d5566afe00473e29083a84ad0a8fdf01b0 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 4 Feb 2024 16:15:38 +0100 Subject: [PATCH 45/68] Changelog for Clippy 1.76 :cat2: --- CHANGELOG.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6080fd3663b30..4bc038fb2dae7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,65 @@ document. ## Unreleased / Beta / In Rust Nightly -[09ac14c9...master](https://github.com/rust-lang/rust-clippy/compare/09ac14c9...master) +[a859e5cc...master](https://github.com/rust-lang/rust-clippy/compare/a859e5cc...master) + +## Rust 1.76 + +Current stable, released 2023-02-08 + +[View all 85 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-11-02T20%3A23%3A40Z..2023-12-16T13%3A11%3A08Z+base%3Amaster) + +### New Lints + +- [`infinite_loop`] + [#11829](https://github.com/rust-lang/rust-clippy/pull/11829) +- [`ineffective_open_options`] + [#11902](https://github.com/rust-lang/rust-clippy/pull/11902) +- [`uninhabited_references`] + [#11878](https://github.com/rust-lang/rust-clippy/pull/11878) +- [`repeat_vec_with_capacity`] + [#11597](https://github.com/rust-lang/rust-clippy/pull/11597) +- [`test_attr_in_doctest`] + [#11872](https://github.com/rust-lang/rust-clippy/pull/11872) +- [`option_map_or_err_ok`] + [#11864](https://github.com/rust-lang/rust-clippy/pull/11864) +- [`join_absolute_paths`] + [#11453](https://github.com/rust-lang/rust-clippy/pull/11453) +- [`impl_hash_borrow_with_str_and_bytes`] + [#11781](https://github.com/rust-lang/rust-clippy/pull/11781) +- [`iter_over_hash_type`] + [#11791](https://github.com/rust-lang/rust-clippy/pull/11791) + +### Moves and Deprecations + +- Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`] + [#11853](https://github.com/rust-lang/rust-clippy/pull/11853) +- Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default) + [#11867](https://github.com/rust-lang/rust-clippy/pull/11867) +- Moved [`if_same_then_else`] to `style` (Now warn-by-default) + [#11809](https://github.com/rust-lang/rust-clippy/pull/11809) + +### Enhancements + +- [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]: + Added the [`check-private-items`] configuration to enable lints on private items + [#11842](https://github.com/rust-lang/rust-clippy/pull/11842) + +### ICE Fixes + +- [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters + [#11804](https://github.com/rust-lang/rust-clippy/pull/11804) +- [`unused_enumerate_index`]: No longer crashes on empty tuples + [#11756](https://github.com/rust-lang/rust-clippy/pull/11756) + +### Others + +- Clippy now respects the `CARGO` environment value + [#11944](https://github.com/rust-lang/rust-clippy/pull/11944) ## Rust 1.75 -Current stable, released 2023-12-28 +Released 2023-12-28 [View all 69 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-09-25T11%3A47%3A47Z..2023-11-02T16%3A41%3A59Z+base%3Amaster) From a3baebcb3147773c9673e4ea166e3419201e8b9f Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Tue, 2 Jan 2024 22:54:17 +0100 Subject: [PATCH 46/68] Add ref_as_ptr lint Author: Marcin Serwin --- CHANGELOG.md | 1 + README.md | 2 +- book/src/README.md | 2 +- clippy_config/src/msrvs.rs | 1 + clippy_lints/src/casts/mod.rs | 30 ++- clippy_lints/src/casts/ref_as_ptr.rs | 55 +++++ clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/only_used_in_recursion.rs | 5 +- tests/ui/borrow_as_ptr.fixed | 1 + tests/ui/borrow_as_ptr.rs | 1 + tests/ui/borrow_as_ptr.stderr | 4 +- tests/ui/borrow_as_ptr_no_std.fixed | 1 + tests/ui/borrow_as_ptr_no_std.rs | 1 + tests/ui/borrow_as_ptr_no_std.stderr | 4 +- tests/ui/ref_as_ptr.fixed | 110 +++++++++ tests/ui/ref_as_ptr.rs | 110 +++++++++ tests/ui/ref_as_ptr.stderr | 269 +++++++++++++++++++++ 17 files changed, 588 insertions(+), 10 deletions(-) create mode 100644 clippy_lints/src/casts/ref_as_ptr.rs create mode 100644 tests/ui/ref_as_ptr.fixed create mode 100644 tests/ui/ref_as_ptr.rs create mode 100644 tests/ui/ref_as_ptr.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index e6c081ca94f1b..a03878c54c7a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5522,6 +5522,7 @@ Released 2018-09-13 [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations +[`ref_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref diff --git a/README.md b/README.md index 5d490645d8979..0450b54121df4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/book/src/README.md b/book/src/README.md index 486ea3df70420..e7972b0db19ce 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index 3507e106fab2e..0032a66365c8e 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -16,6 +16,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { + 1,76,0 { PTR_FROM_REF } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index e05b8f66d8618..14f2f4a7f59da 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -18,6 +18,7 @@ mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; mod ptr_as_ptr; mod ptr_cast_constness; +mod ref_as_ptr; mod unnecessary_cast; mod utils; mod zero_ptr; @@ -689,6 +690,30 @@ declare_clippy_lint! { "using `0 as *{const, mut} T`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of references to pointer using `as` + /// and suggests `std::ptr::from_ref` and `std::ptr::from_mut` instead. + /// + /// ### Why is this bad? + /// Using `as` casts may result in silently changing mutability or type. + /// + /// ### Example + /// ```no_run + /// let a_ref = &1; + /// let a_ptr = a_ref as *const _; + /// ``` + /// Use instead: + /// ```no_run + /// let a_ref = &1; + /// let a_ptr = std::ptr::from_ref(a_ref); + /// ``` + #[clippy::version = "1.77.0"] + pub REF_AS_PTR, + pedantic, + "using `as` to cast a reference to pointer" +} + pub struct Casts { msrv: Msrv, } @@ -724,6 +749,7 @@ impl_lint_pass!(Casts => [ AS_PTR_CAST_MUT, CAST_NAN_TO_INT, ZERO_PTR, + REF_AS_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -771,7 +797,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { as_underscore::check(cx, expr, cast_to_hir); - if self.msrv.meets(msrvs::BORROW_AS_PTR) { + if self.msrv.meets(msrvs::PTR_FROM_REF) { + ref_as_ptr::check(cx, expr, cast_expr, cast_to_hir); + } else if self.msrv.meets(msrvs::BORROW_AS_PTR) { borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir); } } diff --git a/clippy_lints/src/casts/ref_as_ptr.rs b/clippy_lints/src/casts/ref_as_ptr.rs new file mode 100644 index 0000000000000..d600d2aec1b5e --- /dev/null +++ b/clippy_lints/src/casts/ref_as_ptr.rs @@ -0,0 +1,55 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_no_std_crate; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability, Ty, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, TypeAndMut}; + +use super::REF_AS_PTR; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to_hir_ty: &Ty<'_>) { + let (cast_from, cast_to) = ( + cx.typeck_results().expr_ty(cast_expr), + cx.typeck_results().expr_ty(expr), + ); + + if matches!(cast_from.kind(), ty::Ref(..)) + && let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind() + { + let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; + let fn_name = match to_mutbl { + Mutability::Not => "from_ref", + Mutability::Mut => "from_mut", + }; + + let mut app = Applicability::MachineApplicable; + let turbofish = match &cast_to_hir_ty.kind { + TyKind::Infer => String::new(), + TyKind::Ptr(mut_ty) => { + if matches!(mut_ty.ty.kind, TyKind::Infer) { + String::new() + } else { + format!( + "::<{}>", + snippet_with_applicability(cx, mut_ty.ty.span, "/* type */", &mut app) + ) + } + }, + _ => return, + }; + + let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app); + + span_lint_and_sugg( + cx, + REF_AS_PTR, + expr.span, + "reference as raw pointer", + "try", + format!("{core_or_std}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"), + app, + ); + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index f6c9ffea9fcd5..ccb7b25066dd0 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -96,6 +96,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, + crate::casts::REF_AS_PTR_INFO, crate::casts::UNNECESSARY_CAST_INFO, crate::casts::ZERO_PTR_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index d621051ef1653..ae14016f4823a 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -252,7 +252,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { { ( trait_item_id, - FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.args) as *const _ as usize), + FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_regions(trait_ref.args)) as usize), usize::from(sig.decl.implicit_self.has_implicit_self()), ) } else { @@ -390,7 +390,6 @@ fn has_matching_args(kind: FnKind, args: GenericArgsRef<'_>) -> bool { GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx), GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx), }), - #[allow(trivial_casts)] - FnKind::ImplTraitFn(expected_args) => args as *const _ as usize == expected_args, + FnKind::ImplTraitFn(expected_args) => std::ptr::from_ref(args) as usize == expected_args, } } diff --git a/tests/ui/borrow_as_ptr.fixed b/tests/ui/borrow_as_ptr.fixed index 6c0de96d65e36..289a5ef38b8dd 100644 --- a/tests/ui/borrow_as_ptr.fixed +++ b/tests/ui/borrow_as_ptr.fixed @@ -5,6 +5,7 @@ fn a() -> i32 { 0 } +#[clippy::msrv = "1.75"] fn main() { let val = 1; let _p = std::ptr::addr_of!(val); diff --git a/tests/ui/borrow_as_ptr.rs b/tests/ui/borrow_as_ptr.rs index c37c5357c82c0..b5328cb22dcdb 100644 --- a/tests/ui/borrow_as_ptr.rs +++ b/tests/ui/borrow_as_ptr.rs @@ -5,6 +5,7 @@ fn a() -> i32 { 0 } +#[clippy::msrv = "1.75"] fn main() { let val = 1; let _p = &val as *const i32; diff --git a/tests/ui/borrow_as_ptr.stderr b/tests/ui/borrow_as_ptr.stderr index 43a7a6bf5b57e..b98618059050c 100644 --- a/tests/ui/borrow_as_ptr.stderr +++ b/tests/ui/borrow_as_ptr.stderr @@ -1,5 +1,5 @@ error: borrow as raw pointer - --> $DIR/borrow_as_ptr.rs:10:14 + --> $DIR/borrow_as_ptr.rs:11:14 | LL | let _p = &val as *const i32; | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of!(val)` @@ -8,7 +8,7 @@ LL | let _p = &val as *const i32; = help: to override `-D warnings` add `#[allow(clippy::borrow_as_ptr)]` error: borrow as raw pointer - --> $DIR/borrow_as_ptr.rs:17:18 + --> $DIR/borrow_as_ptr.rs:18:18 | LL | let _p_mut = &mut val_mut as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)` diff --git a/tests/ui/borrow_as_ptr_no_std.fixed b/tests/ui/borrow_as_ptr_no_std.fixed index a361a36474de5..f66554de30005 100644 --- a/tests/ui/borrow_as_ptr_no_std.fixed +++ b/tests/ui/borrow_as_ptr_no_std.fixed @@ -2,6 +2,7 @@ #![feature(lang_items, start, libc)] #![no_std] +#[clippy::msrv = "1.75"] #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { let val = 1; diff --git a/tests/ui/borrow_as_ptr_no_std.rs b/tests/ui/borrow_as_ptr_no_std.rs index b3fe01442b745..1fc254aafa775 100644 --- a/tests/ui/borrow_as_ptr_no_std.rs +++ b/tests/ui/borrow_as_ptr_no_std.rs @@ -2,6 +2,7 @@ #![feature(lang_items, start, libc)] #![no_std] +#[clippy::msrv = "1.75"] #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { let val = 1; diff --git a/tests/ui/borrow_as_ptr_no_std.stderr b/tests/ui/borrow_as_ptr_no_std.stderr index 2f258bcf966ad..1ef0a948a32a1 100644 --- a/tests/ui/borrow_as_ptr_no_std.stderr +++ b/tests/ui/borrow_as_ptr_no_std.stderr @@ -1,5 +1,5 @@ error: borrow as raw pointer - --> $DIR/borrow_as_ptr_no_std.rs:8:14 + --> $DIR/borrow_as_ptr_no_std.rs:9:14 | LL | let _p = &val as *const i32; | ^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of!(val)` @@ -8,7 +8,7 @@ LL | let _p = &val as *const i32; = help: to override `-D warnings` add `#[allow(clippy::borrow_as_ptr)]` error: borrow as raw pointer - --> $DIR/borrow_as_ptr_no_std.rs:11:18 + --> $DIR/borrow_as_ptr_no_std.rs:12:18 | LL | let _p_mut = &mut val_mut as *mut i32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of_mut!(val_mut)` diff --git a/tests/ui/ref_as_ptr.fixed b/tests/ui/ref_as_ptr.fixed new file mode 100644 index 0000000000000..7a946393f259b --- /dev/null +++ b/tests/ui/ref_as_ptr.fixed @@ -0,0 +1,110 @@ +#![warn(clippy::ref_as_ptr)] +#![allow(clippy::unnecessary_mut_passed)] + +fn main() { + let _ = std::ptr::from_ref(&1u8); + let _ = std::ptr::from_ref::(&2u32); + let _ = std::ptr::from_ref::(&3.0f64); + + let _ = std::ptr::from_ref(&4) as *const f32; + let _ = std::ptr::from_ref::(&5.0f32) as *const u32; + + let _ = std::ptr::from_ref(&mut 6u8); + let _ = std::ptr::from_ref::(&mut 7u32); + let _ = std::ptr::from_ref::(&mut 8.0f64); + + let _ = std::ptr::from_ref(&mut 9) as *const f32; + let _ = std::ptr::from_ref::(&mut 10.0f32) as *const u32; + + let _ = std::ptr::from_mut(&mut 11u8); + let _ = std::ptr::from_mut::(&mut 12u32); + let _ = std::ptr::from_mut::(&mut 13.0f64); + + let _ = std::ptr::from_mut(&mut 14) as *const f32; + let _ = std::ptr::from_mut::(&mut 15.0f32) as *const u32; + + let _ = std::ptr::from_ref(&1u8); + let _ = std::ptr::from_ref::(&2u32); + let _ = std::ptr::from_ref::(&3.0f64); + + let _ = std::ptr::from_ref(&4) as *const f32; + let _ = std::ptr::from_ref::(&5.0f32) as *const u32; + + let val = 1; + let _ = std::ptr::from_ref(&val); + let _ = std::ptr::from_ref::(&val); + + let _ = std::ptr::from_ref(&val) as *const f32; + let _ = std::ptr::from_ref::(&val) as *const f64; + + let mut val: u8 = 2; + let _ = std::ptr::from_mut::(&mut val); + let _ = std::ptr::from_mut(&mut val); + + let _ = std::ptr::from_ref::(&mut val); + let _ = std::ptr::from_ref(&mut val); + + let _ = std::ptr::from_ref::(&mut val) as *const f64; + let _: *const Option = std::ptr::from_ref(&mut val) as *const _; + + let _ = std::ptr::from_ref::<[usize; 7]>(&std::array::from_fn(|i| i * i)); + let _ = std::ptr::from_ref::<[usize; 8]>(&mut std::array::from_fn(|i| i * i)); + let _ = std::ptr::from_mut::<[usize; 9]>(&mut std::array::from_fn(|i| i * i)); +} + +#[clippy::msrv = "1.75"] +fn _msrv_1_75() { + let val = &42_i32; + let mut_val = &mut 42_i32; + + // `std::ptr::from_{ref, mut}` was stabilized in 1.76. Do not lint this + let _ = val as *const i32; + let _ = mut_val as *mut i32; +} + +#[clippy::msrv = "1.76"] +fn _msrv_1_76() { + let val = &42_i32; + let mut_val = &mut 42_i32; + + let _ = std::ptr::from_ref::(val); + let _ = std::ptr::from_mut::(mut_val); +} + +fn foo(val: &[u8]) { + let _ = std::ptr::from_ref(val); + let _ = std::ptr::from_ref::<[u8]>(val); +} + +fn bar(val: &mut str) { + let _ = std::ptr::from_mut(val); + let _ = std::ptr::from_mut::(val); +} + +struct X<'a>(&'a i32); + +impl<'a> X<'a> { + fn foo(&self) -> *const i64 { + std::ptr::from_ref(self.0) as *const _ + } + + fn bar(&mut self) -> *const i64 { + std::ptr::from_ref(self.0) as *const _ + } +} + +struct Y<'a>(&'a mut i32); + +impl<'a> Y<'a> { + fn foo(&self) -> *const i64 { + std::ptr::from_ref(self.0) as *const _ + } + + fn bar(&mut self) -> *const i64 { + std::ptr::from_ref(self.0) as *const _ + } + + fn baz(&mut self) -> *const i64 { + std::ptr::from_mut(self.0) as *mut _ + } +} diff --git a/tests/ui/ref_as_ptr.rs b/tests/ui/ref_as_ptr.rs new file mode 100644 index 0000000000000..6f745505b4695 --- /dev/null +++ b/tests/ui/ref_as_ptr.rs @@ -0,0 +1,110 @@ +#![warn(clippy::ref_as_ptr)] +#![allow(clippy::unnecessary_mut_passed)] + +fn main() { + let _ = &1u8 as *const _; + let _ = &2u32 as *const u32; + let _ = &3.0f64 as *const f64; + + let _ = &4 as *const _ as *const f32; + let _ = &5.0f32 as *const f32 as *const u32; + + let _ = &mut 6u8 as *const _; + let _ = &mut 7u32 as *const u32; + let _ = &mut 8.0f64 as *const f64; + + let _ = &mut 9 as *const _ as *const f32; + let _ = &mut 10.0f32 as *const f32 as *const u32; + + let _ = &mut 11u8 as *mut _; + let _ = &mut 12u32 as *mut u32; + let _ = &mut 13.0f64 as *mut f64; + + let _ = &mut 14 as *mut _ as *const f32; + let _ = &mut 15.0f32 as *mut f32 as *const u32; + + let _ = &1u8 as *const _; + let _ = &2u32 as *const u32; + let _ = &3.0f64 as *const f64; + + let _ = &4 as *const _ as *const f32; + let _ = &5.0f32 as *const f32 as *const u32; + + let val = 1; + let _ = &val as *const _; + let _ = &val as *const i32; + + let _ = &val as *const _ as *const f32; + let _ = &val as *const i32 as *const f64; + + let mut val: u8 = 2; + let _ = &mut val as *mut u8; + let _ = &mut val as *mut _; + + let _ = &mut val as *const u8; + let _ = &mut val as *const _; + + let _ = &mut val as *const u8 as *const f64; + let _: *const Option = &mut val as *const _ as *const _; + + let _ = &std::array::from_fn(|i| i * i) as *const [usize; 7]; + let _ = &mut std::array::from_fn(|i| i * i) as *const [usize; 8]; + let _ = &mut std::array::from_fn(|i| i * i) as *mut [usize; 9]; +} + +#[clippy::msrv = "1.75"] +fn _msrv_1_75() { + let val = &42_i32; + let mut_val = &mut 42_i32; + + // `std::ptr::from_{ref, mut}` was stabilized in 1.76. Do not lint this + let _ = val as *const i32; + let _ = mut_val as *mut i32; +} + +#[clippy::msrv = "1.76"] +fn _msrv_1_76() { + let val = &42_i32; + let mut_val = &mut 42_i32; + + let _ = val as *const i32; + let _ = mut_val as *mut i32; +} + +fn foo(val: &[u8]) { + let _ = val as *const _; + let _ = val as *const [u8]; +} + +fn bar(val: &mut str) { + let _ = val as *mut _; + let _ = val as *mut str; +} + +struct X<'a>(&'a i32); + +impl<'a> X<'a> { + fn foo(&self) -> *const i64 { + self.0 as *const _ as *const _ + } + + fn bar(&mut self) -> *const i64 { + self.0 as *const _ as *const _ + } +} + +struct Y<'a>(&'a mut i32); + +impl<'a> Y<'a> { + fn foo(&self) -> *const i64 { + self.0 as *const _ as *const _ + } + + fn bar(&mut self) -> *const i64 { + self.0 as *const _ as *const _ + } + + fn baz(&mut self) -> *const i64 { + self.0 as *mut _ as *mut _ + } +} diff --git a/tests/ui/ref_as_ptr.stderr b/tests/ui/ref_as_ptr.stderr new file mode 100644 index 0000000000000..371d42df528b9 --- /dev/null +++ b/tests/ui/ref_as_ptr.stderr @@ -0,0 +1,269 @@ +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:5:13 + | +LL | let _ = &1u8 as *const _; + | ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&1u8)` + | + = note: `-D clippy::ref-as-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::ref_as_ptr)]` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:6:13 + | +LL | let _ = &2u32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&2u32)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:7:13 + | +LL | let _ = &3.0f64 as *const f64; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&3.0f64)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:9:13 + | +LL | let _ = &4 as *const _ as *const f32; + | ^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&4)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:10:13 + | +LL | let _ = &5.0f32 as *const f32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&5.0f32)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:12:13 + | +LL | let _ = &mut 6u8 as *const _; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&mut 6u8)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:13:13 + | +LL | let _ = &mut 7u32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&mut 7u32)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:14:13 + | +LL | let _ = &mut 8.0f64 as *const f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&mut 8.0f64)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:16:13 + | +LL | let _ = &mut 9 as *const _ as *const f32; + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&mut 9)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:17:13 + | +LL | let _ = &mut 10.0f32 as *const f32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&mut 10.0f32)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:19:13 + | +LL | let _ = &mut 11u8 as *mut _; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(&mut 11u8)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:20:13 + | +LL | let _ = &mut 12u32 as *mut u32; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::(&mut 12u32)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:21:13 + | +LL | let _ = &mut 13.0f64 as *mut f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::(&mut 13.0f64)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:23:13 + | +LL | let _ = &mut 14 as *mut _ as *const f32; + | ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(&mut 14)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:24:13 + | +LL | let _ = &mut 15.0f32 as *mut f32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::(&mut 15.0f32)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:26:13 + | +LL | let _ = &1u8 as *const _; + | ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&1u8)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:27:13 + | +LL | let _ = &2u32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&2u32)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:28:13 + | +LL | let _ = &3.0f64 as *const f64; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&3.0f64)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:30:13 + | +LL | let _ = &4 as *const _ as *const f32; + | ^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&4)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:31:13 + | +LL | let _ = &5.0f32 as *const f32 as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&5.0f32)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:34:13 + | +LL | let _ = &val as *const _; + | ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:35:13 + | +LL | let _ = &val as *const i32; + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:37:13 + | +LL | let _ = &val as *const _ as *const f32; + | ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:38:13 + | +LL | let _ = &val as *const i32 as *const f64; + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:41:13 + | +LL | let _ = &mut val as *mut u8; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::(&mut val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:42:13 + | +LL | let _ = &mut val as *mut _; + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(&mut val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:44:13 + | +LL | let _ = &mut val as *const u8; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&mut val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:45:13 + | +LL | let _ = &mut val as *const _; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&mut val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:47:13 + | +LL | let _ = &mut val as *const u8 as *const f64; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(&mut val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:48:32 + | +LL | let _: *const Option = &mut val as *const _ as *const _; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&mut val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:50:13 + | +LL | let _ = &std::array::from_fn(|i| i * i) as *const [usize; 7]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<[usize; 7]>(&std::array::from_fn(|i| i * i))` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:51:13 + | +LL | let _ = &mut std::array::from_fn(|i| i * i) as *const [usize; 8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<[usize; 8]>(&mut std::array::from_fn(|i| i * i))` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:52:13 + | +LL | let _ = &mut std::array::from_fn(|i| i * i) as *mut [usize; 9]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::<[usize; 9]>(&mut std::array::from_fn(|i| i * i))` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:70:13 + | +LL | let _ = val as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::(val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:71:13 + | +LL | let _ = mut_val as *mut i32; + | ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::(mut_val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:75:13 + | +LL | let _ = val as *const _; + | ^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:76:13 + | +LL | let _ = val as *const [u8]; + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<[u8]>(val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:80:13 + | +LL | let _ = val as *mut _; + | ^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:81:13 + | +LL | let _ = val as *mut str; + | ^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::(val)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:88:9 + | +LL | self.0 as *const _ as *const _ + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(self.0)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:92:9 + | +LL | self.0 as *const _ as *const _ + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(self.0)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:100:9 + | +LL | self.0 as *const _ as *const _ + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(self.0)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:104:9 + | +LL | self.0 as *const _ as *const _ + | ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(self.0)` + +error: reference as raw pointer + --> $DIR/ref_as_ptr.rs:108:9 + | +LL | self.0 as *mut _ as *mut _ + | ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(self.0)` + +error: aborting due to 44 previous errors + From 7c3908f86c61547e1a15873c156be5be8c012a06 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 4 Feb 2024 20:12:15 +0100 Subject: [PATCH 47/68] [`redundant_locals`]: take by-value closure captures into account --- clippy_lints/src/redundant_locals.rs | 30 ++++++++++++++- tests/ui/redundant_locals.rs | 36 ++++++++++++++++++ tests/ui/redundant_locals.stderr | 56 ++++++++++++++-------------- 3 files changed, 93 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index 2c511ee0bc029..b0040bfa58208 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -2,10 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_from_proc_macro; use clippy_utils::ty::needs_ordered_drop; use rustc_ast::Mutability; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; +use rustc_hir_typeck::expr_use_visitor::PlaceBase; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::UpvarCapture; use rustc_session::declare_lint_pass; use rustc_span::symbol::Ident; use rustc_span::DesugaringKind; @@ -69,6 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { // the local is user-controlled && !in_external_macro(cx.sess(), local.span) && !is_from_proc_macro(cx, expr) + && !is_closure_capture(cx, local.hir_id, binding_id) { span_lint_and_help( cx, @@ -82,6 +85,31 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { } } +/// Checks if the enclosing body is a closure and if the given local is captured by value. +/// +/// In those cases, the redefinition may be necessary to force a move: +/// ``` +/// fn assert_static(_: T) {} +/// +/// let v = String::new(); +/// let closure = || { +/// let v = v; // <- removing this redefinition makes `closure` no longer `'static` +/// dbg!(&v); +/// }; +/// assert_static(closure); +/// ``` +fn is_closure_capture(cx: &LateContext<'_>, redefinition: HirId, root_variable: HirId) -> bool { + let body = cx.tcx.hir().enclosing_body_owner(redefinition); + if let DefKind::Closure = cx.tcx.def_kind(body) { + cx.tcx.closure_captures(body).iter().any(|c| { + matches!(c.info.capture_kind, UpvarCapture::ByValue) + && matches!(c.place.base, PlaceBase::Upvar(upvar) if upvar.var_path.hir_id == root_variable) + }) + } else { + false + } +} + /// Find the annotation of a binding introduced by a pattern, or `None` if it's not introduced. fn find_binding(pat: &Pat<'_>, name: Ident) -> Option { let mut ret = None; diff --git a/tests/ui/redundant_locals.rs b/tests/ui/redundant_locals.rs index 182d067a5e9fa..40bd89bc59529 100644 --- a/tests/ui/redundant_locals.rs +++ b/tests/ui/redundant_locals.rs @@ -1,6 +1,7 @@ //@aux-build:proc_macros.rs #![allow(unused, clippy::no_effect, clippy::needless_pass_by_ref_mut)] #![warn(clippy::redundant_locals)] +#![feature(async_closure)] extern crate proc_macros; use proc_macros::{external, with_span}; @@ -163,3 +164,38 @@ fn drop_compose() { let b = ComposeDrop { d: WithDrop(1) }; let a = a; } + +fn issue12225() { + fn assert_static(_: T) {} + + let v1 = String::new(); + let v2 = String::new(); + let v3 = String::new(); + let v4 = String::new(); + + assert_static(|| { + let v1 = v1; + dbg!(&v1); + }); + assert_static(async { + let v2 = v2; + dbg!(&v2); + }); + assert_static(|| async { + let v3 = v3; + dbg!(&v3); + }); + assert_static(async || { + let v4 = v4; + dbg!(&v4); + }); + + fn foo(a: &str, b: &str) {} + + let do_not_move = String::new(); + let things_to_move = vec!["a".to_string(), "b".to_string()]; + let futures = things_to_move.into_iter().map(|move_me| async { + let move_me = move_me; + foo(&do_not_move, &move_me) + }); +} diff --git a/tests/ui/redundant_locals.stderr b/tests/ui/redundant_locals.stderr index 30ab4aa2ea915..610d587ddadce 100644 --- a/tests/ui/redundant_locals.stderr +++ b/tests/ui/redundant_locals.stderr @@ -1,11 +1,11 @@ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:12:5 + --> $DIR/redundant_locals.rs:13:5 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:11:9 + --> $DIR/redundant_locals.rs:12:9 | LL | let x = 1; | ^ @@ -13,157 +13,157 @@ LL | let x = 1; = help: to override `-D warnings` add `#[allow(clippy::redundant_locals)]` error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:17:5 + --> $DIR/redundant_locals.rs:18:5 | LL | let mut x = x; | ^^^^^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:16:9 + --> $DIR/redundant_locals.rs:17:9 | LL | let mut x = 1; | ^^^^^ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:47:5 + --> $DIR/redundant_locals.rs:48:5 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:46:14 + --> $DIR/redundant_locals.rs:47:14 | LL | fn parameter(x: i32) { | ^ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:52:5 + --> $DIR/redundant_locals.rs:53:5 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:51:9 + --> $DIR/redundant_locals.rs:52:9 | LL | let x = 1; | ^ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:53:5 + --> $DIR/redundant_locals.rs:54:5 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:52:9 + --> $DIR/redundant_locals.rs:53:9 | LL | let x = x; | ^ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:54:5 + --> $DIR/redundant_locals.rs:55:5 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:53:9 + --> $DIR/redundant_locals.rs:54:9 | LL | let x = x; | ^ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:55:5 + --> $DIR/redundant_locals.rs:56:5 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:54:9 + --> $DIR/redundant_locals.rs:55:9 | LL | let x = x; | ^ error: redundant redefinition of a binding `a` - --> $DIR/redundant_locals.rs:61:5 + --> $DIR/redundant_locals.rs:62:5 | LL | let a = a; | ^^^^^^^^^^ | help: `a` is initially defined here - --> $DIR/redundant_locals.rs:59:9 + --> $DIR/redundant_locals.rs:60:9 | LL | let a = 1; | ^ error: redundant redefinition of a binding `b` - --> $DIR/redundant_locals.rs:62:5 + --> $DIR/redundant_locals.rs:63:5 | LL | let b = b; | ^^^^^^^^^^ | help: `b` is initially defined here - --> $DIR/redundant_locals.rs:60:9 + --> $DIR/redundant_locals.rs:61:9 | LL | let b = 2; | ^ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:68:9 + --> $DIR/redundant_locals.rs:69:9 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:67:13 + --> $DIR/redundant_locals.rs:68:13 | LL | let x = 1; | ^ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:75:9 + --> $DIR/redundant_locals.rs:76:9 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:74:13 + --> $DIR/redundant_locals.rs:75:13 | LL | let x = 1; | ^ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:78:9 + --> $DIR/redundant_locals.rs:79:9 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:77:6 + --> $DIR/redundant_locals.rs:78:6 | LL | |x: i32| { | ^ error: redundant redefinition of a binding `x` - --> $DIR/redundant_locals.rs:97:9 + --> $DIR/redundant_locals.rs:98:9 | LL | let x = x; | ^^^^^^^^^^ | help: `x` is initially defined here - --> $DIR/redundant_locals.rs:94:9 + --> $DIR/redundant_locals.rs:95:9 | LL | let x = 1; | ^ error: redundant redefinition of a binding `a` - --> $DIR/redundant_locals.rs:152:5 + --> $DIR/redundant_locals.rs:153:5 | LL | let a = a; | ^^^^^^^^^^ | help: `a` is initially defined here - --> $DIR/redundant_locals.rs:150:9 + --> $DIR/redundant_locals.rs:151:9 | LL | let a = WithoutDrop(1); | ^ From 77fb5406842d6e349b4d67289eba5a9a4a571a9a Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Mon, 5 Feb 2024 13:38:57 +0100 Subject: [PATCH 48/68] extend docs for `predicate_must_hold_considering_regions` --- .../src/traits/query/evaluate_obligation.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 4c0c57377e02a..a050b30317a05 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -41,7 +41,28 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// not entirely accurate if inference variables are involved. /// /// This version may conservatively fail when outlives obligations - /// are required. + /// are required. Therefore, this version should only be used for + /// optimizations or diagnostics and be treated as if it can always + /// return `false`. + /// + /// # Example + /// + /// ``` + /// # #![allow(dead_code)] + /// trait Trait {} + /// + /// fn check() {} + /// + /// fn foo() + /// where + /// &'static T: Trait, + /// { + /// // Evaluating `&'?0 T: Trait` adds a `'?0: 'static` outlives obligation, + /// // which means that `predicate_must_hold_considering_regions` will return + /// // `false`. + /// check::<&'_ T>(); + /// } + /// ``` fn predicate_must_hold_considering_regions( &self, obligation: &PredicateObligation<'tcx>, From 0c1f401d98c17372d4f305602a43e53cd2ef78a6 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Mon, 5 Feb 2024 14:54:24 +0100 Subject: [PATCH 49/68] old solver: improve normalization of `Pointee::Metadata` --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 6 +-- .../src/interpret/terminator.rs | 7 +-- compiler/rustc_middle/src/ty/sty.rs | 48 +++++++++++------ .../src/traits/project.rs | 37 ++++++++----- tests/ui/traits/pointee-normalize-equate.rs | 54 +++++++++++++++++++ 5 files changed, 113 insertions(+), 39 deletions(-) create mode 100644 tests/ui/traits/pointee-normalize-equate.rs diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index b4512af38e37b..e3e48ecb3aa5f 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1985,10 +1985,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( match in_elem.kind() { ty::RawPtr(p) => { - let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| { + let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); - assert!(!check_sized); // we are in codegen, so we shouldn't see these types require!( metadata.is_unit(), InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem } @@ -2000,10 +1999,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } match out_elem.kind() { ty::RawPtr(p) => { - let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| { + let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); - assert!(!check_sized); // we are in codegen, so we shouldn't see these types require!( metadata.is_unit(), InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index b7ffb4a16fc31..eaaf56baf3b9f 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -377,12 +377,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // to fields, which can yield non-normalized types. So we need to provide a // normalization function. let normalize = |ty| self.tcx.normalize_erasing_regions(self.param_env, ty); - let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, normalize); - assert!( - !only_if_sized, - "there should be no more 'maybe has that metadata' types during interpretation" - ); - meta + ty.ptr_metadata_ty(*self.tcx, normalize) }; return Ok(meta_ty(caller) == meta_ty(callee)); } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index f5fdf210592e8..6e944d44a8bf4 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1957,12 +1957,12 @@ impl<'tcx> Ty<'tcx> { } /// Returns the type of metadata for (potentially fat) pointers to this type, - /// and a boolean signifying if this is conditional on this type being `Sized`. - pub fn ptr_metadata_ty( + /// or the struct tail if the metadata type cannot be determined. + pub fn ptr_metadata_ty_or_tail( self, tcx: TyCtxt<'tcx>, normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, - ) -> (Ty<'tcx>, bool) { + ) -> Result, Ty<'tcx>> { let tail = tcx.struct_tail_with_normalize(self, normalize, || {}); match tail.kind() { // Sized types @@ -1984,31 +1984,47 @@ impl<'tcx> Ty<'tcx> { | ty::Error(_) // Extern types have metadata = (). | ty::Foreign(..) - // `dyn*` has no metadata + // `dyn*` has metadata = (). | ty::Dynamic(_, _, ty::DynStar) - // If returned by `struct_tail_without_normalization` this is a unit struct + // If returned by `struct_tail_with_normalize` this is a unit struct // without any fields, or not a struct, and therefore is Sized. | ty::Adt(..) - // If returned by `struct_tail_without_normalization` this is the empty tuple, + // If returned by `struct_tail_with_normalize` this is the empty tuple, // a.k.a. unit type, which is Sized - | ty::Tuple(..) => (tcx.types.unit, false), + | ty::Tuple(..) => Ok(tcx.types.unit), + + ty::Str | ty::Slice(_) => Ok(tcx.types.usize), - ty::Str | ty::Slice(_) => (tcx.types.usize, false), ty::Dynamic(_, _, ty::Dyn) => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None); - (tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false) - }, + Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()])) + } - // type parameters only have unit metadata if they're sized, so return true - // to make sure we double check this during confirmation - ty::Param(_) | ty::Alias(..) => (tcx.types.unit, true), + // We don't know the metadata of `self`, but it must be equal to the + // metadata of `tail`. + ty::Param(_) | ty::Alias(..) => Err(tail), ty::Infer(ty::TyVar(_)) | ty::Bound(..) | ty::Placeholder(..) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("`ptr_metadata_ty` applied to unexpected type: {:?} (tail = {:?})", self, tail) - } + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!( + "`ptr_metadata_ty_or_tail` applied to unexpected type: {self:?} (tail = {tail:?})" + ), + } + } + + /// Returns the type of metadata for (potentially fat) pointers to this type. + /// Causes an ICE if the metadata type cannot be determined. + pub fn ptr_metadata_ty( + self, + tcx: TyCtxt<'tcx>, + normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, + ) -> Ty<'tcx> { + match self.ptr_metadata_ty_or_tail(tcx, normalize) { + Ok(metadata) => metadata, + Err(tail) => bug!( + "`ptr_metadata_ty` failed to get metadata for type: {self:?} (tail = {tail:?})" + ), } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index abbc2066eac16..33298c4b750d9 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1916,10 +1916,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // Integers and floats are always Sized, and so have unit type metadata. | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, - // type parameters, opaques, and unnormalized projections have pointer - // metadata if they're known (e.g. by the param_env) to be sized + // We normalize from `Wrapper::Metadata` to `Tail::Metadata` if able. + // Otherwise, type parameters, opaques, and unnormalized projections have + // unit metadata if they're known (e.g. by the param_env) to be sized. ty::Param(_) | ty::Alias(..) - if selcx.infcx.predicate_must_hold_modulo_regions( + if self_ty != tail || selcx.infcx.predicate_must_hold_modulo_regions( &obligation.with( selcx.tcx(), ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]), @@ -2289,7 +2290,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>( assert_eq!(metadata_def_id, item_def_id); let mut obligations = Vec::new(); - let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| { + let normalize = |ty| { normalize_with_depth_to( selcx, obligation.param_env, @@ -2298,16 +2299,26 @@ fn confirm_builtin_candidate<'cx, 'tcx>( ty, &mut obligations, ) + }; + let metadata_ty = self_ty.ptr_metadata_ty_or_tail(tcx, normalize).unwrap_or_else(|tail| { + if tail == self_ty { + // This is the fallback case for type parameters, unnormalizable projections + // and opaque types. + // If the `self_ty` is `Sized`, then the metadata is `()`. + let sized_predicate = ty::TraitRef::from_lang_item( + tcx, + LangItem::Sized, + obligation.cause.span(), + [self_ty], + ); + obligations.push(obligation.with(tcx, sized_predicate)); + tcx.types.unit + } else { + // We know that `self_ty` has the same metadata as `tail`. This allows us + // to prove predicates like `Wrapper::Metadata == Tail::Metadata`. + Ty::new_projection(tcx, metadata_def_id, [tail]) + } }); - if check_is_sized { - let sized_predicate = ty::TraitRef::from_lang_item( - tcx, - LangItem::Sized, - obligation.cause.span(), - [self_ty], - ); - obligations.push(obligation.with(tcx, sized_predicate)); - } (metadata_ty.into(), obligations) } else { bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate); diff --git a/tests/ui/traits/pointee-normalize-equate.rs b/tests/ui/traits/pointee-normalize-equate.rs new file mode 100644 index 0000000000000..d70ab4d881444 --- /dev/null +++ b/tests/ui/traits/pointee-normalize-equate.rs @@ -0,0 +1,54 @@ +// check-pass + +#![feature(ptr_metadata)] + +use std::ptr::{self, Pointee}; + +fn cast_same_meta(ptr: *const T) -> *const U +where + T: Pointee::Metadata>, +{ + let (thin, meta) = ptr.to_raw_parts(); + ptr::from_raw_parts(thin, meta) +} + +struct Wrapper(T); + +// normalize `Wrapper::Metadata` -> `T::Metadata` +fn wrapper_to_tail(ptr: *const T) -> *const Wrapper { + cast_same_meta(ptr) +} + +// normalize `Wrapper::Metadata` -> `T::Metadata` -> `()` +fn wrapper_to_unit(ptr: *const ()) -> *const Wrapper { + cast_same_meta(ptr) +} + +trait Project { + type Assoc: ?Sized; +} + +struct WrapperProject(T::Assoc); + +// normalize `WrapperProject::Metadata` -> `T::Assoc::Metadata` +fn wrapper_project_tail(ptr: *const T::Assoc) -> *const WrapperProject { + cast_same_meta(ptr) +} + +// normalize `WrapperProject::Metadata` -> `T::Assoc::Metadata` -> `()` +fn wrapper_project_unit(ptr: *const ()) -> *const WrapperProject +where + T::Assoc: Sized, +{ + cast_same_meta(ptr) +} + +// normalize `<[T] as Pointee>::Metadata` -> `usize`, even if `[T]: Sized` +fn sized_slice(ptr: *const [T]) -> *const str +where + [T]: Sized, +{ + cast_same_meta(ptr) +} + +fn main() {} From 6807977153d9af737bac95693ccc211206538ec7 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:41:59 +0100 Subject: [PATCH 50/68] also check for coroutines --- clippy_lints/src/redundant_locals.rs | 16 +++++++--------- tests/ui/redundant_locals.rs | 12 +++++++++++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index b0040bfa58208..700a5dd4a8511 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_from_proc_macro; use clippy_utils::ty::needs_ordered_drop; use rustc_ast::Mutability; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::Res; use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; use rustc_hir_typeck::expr_use_visitor::PlaceBase; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { // the local is user-controlled && !in_external_macro(cx.sess(), local.span) && !is_from_proc_macro(cx, expr) - && !is_closure_capture(cx, local.hir_id, binding_id) + && !is_by_value_closure_capture(cx, local.hir_id, binding_id) { span_lint_and_help( cx, @@ -98,16 +98,14 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { /// }; /// assert_static(closure); /// ``` -fn is_closure_capture(cx: &LateContext<'_>, redefinition: HirId, root_variable: HirId) -> bool { - let body = cx.tcx.hir().enclosing_body_owner(redefinition); - if let DefKind::Closure = cx.tcx.def_kind(body) { - cx.tcx.closure_captures(body).iter().any(|c| { +fn is_by_value_closure_capture(cx: &LateContext<'_>, redefinition: HirId, root_variable: HirId) -> bool { + let closure_def_id = cx.tcx.hir().enclosing_body_owner(redefinition); + + cx.tcx.is_closure_or_coroutine(closure_def_id.to_def_id()) + && cx.tcx.closure_captures(closure_def_id).iter().any(|c| { matches!(c.info.capture_kind, UpvarCapture::ByValue) && matches!(c.place.base, PlaceBase::Upvar(upvar) if upvar.var_path.hir_id == root_variable) }) - } else { - false - } } /// Find the annotation of a binding introduced by a pattern, or `None` if it's not introduced. diff --git a/tests/ui/redundant_locals.rs b/tests/ui/redundant_locals.rs index 40bd89bc59529..f6909828aa9a7 100644 --- a/tests/ui/redundant_locals.rs +++ b/tests/ui/redundant_locals.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![allow(unused, clippy::no_effect, clippy::needless_pass_by_ref_mut)] #![warn(clippy::redundant_locals)] -#![feature(async_closure)] +#![feature(async_closure, coroutines)] extern crate proc_macros; use proc_macros::{external, with_span}; @@ -172,6 +172,8 @@ fn issue12225() { let v2 = String::new(); let v3 = String::new(); let v4 = String::new(); + let v5 = String::new(); + let v6 = String::new(); assert_static(|| { let v1 = v1; @@ -189,6 +191,14 @@ fn issue12225() { let v4 = v4; dbg!(&v4); }); + assert_static(static || { + let v5 = v5; + yield; + }); + assert_static(|| { + let v6 = v6; + yield; + }); fn foo(a: &str, b: &str) {} From 42cc1d2f974f1db27608356c556ec5286400a58a Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Mon, 5 Feb 2024 15:14:16 +0100 Subject: [PATCH 51/68] new solver: improve normalization of `Pointee::Metadata` --- .../src/solve/normalizes_to/mod.rs | 24 +++++-------------- tests/ui/traits/pointee-normalize-equate.rs | 2 ++ 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 9f1b4a09a20ba..571963fa735f7 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -378,6 +378,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); + let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); + assert_eq!(metadata_def_id, goal.predicate.def_id()); ecx.probe_misc_candidate("builtin pointee").enter(|ecx| { let metadata_ty = match goal.predicate.self_ty().kind() { ty::Bool @@ -422,30 +424,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { None => tcx.types.unit, - Some(field_def) => { - let self_ty = field_def.ty(tcx, args); - // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? - ecx.add_goal( - GoalSource::Misc, - goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)), - ); - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + Some(tail_def) => { + let tail_ty = tail_def.ty(tcx, args); + Ty::new_projection(tcx, metadata_def_id, [tail_ty]) } }, ty::Adt(_, _) => tcx.types.unit, ty::Tuple(elements) => match elements.last() { None => tcx.types.unit, - Some(&self_ty) => { - // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? - ecx.add_goal( - GoalSource::Misc, - goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)), - ); - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); - } + Some(&tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]), }, ty::Infer( diff --git a/tests/ui/traits/pointee-normalize-equate.rs b/tests/ui/traits/pointee-normalize-equate.rs index d70ab4d881444..2e75933aca0cf 100644 --- a/tests/ui/traits/pointee-normalize-equate.rs +++ b/tests/ui/traits/pointee-normalize-equate.rs @@ -1,4 +1,6 @@ // check-pass +// revisions: old next +//[next] compile-flags: -Znext-solver #![feature(ptr_metadata)] From 7f80b449f570613aea32f30f64ec5bb3189e1d26 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Sun, 3 Dec 2023 23:41:01 +0100 Subject: [PATCH 52/68] new lint: `manual_c_str_literals` --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 2 +- clippy_config/src/msrvs.rs | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/methods/manual_c_str_literals.rs | 197 ++++++++++++++++++ clippy_lints/src/methods/mod.rs | 37 ++++ tests/ui/manual_c_str_literals.fixed | 60 ++++++ tests/ui/manual_c_str_literals.rs | 60 ++++++ tests/ui/manual_c_str_literals.stderr | 83 ++++++++ tests/ui/strlen_on_c_strings.fixed | 2 +- tests/ui/strlen_on_c_strings.rs | 2 +- 12 files changed, 444 insertions(+), 3 deletions(-) create mode 100644 clippy_lints/src/methods/manual_c_str_literals.rs create mode 100644 tests/ui/manual_c_str_literals.fixed create mode 100644 tests/ui/manual_c_str_literals.rs create mode 100644 tests/ui/manual_c_str_literals.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b68c659ea729..e88f6df853a2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5340,6 +5340,7 @@ Released 2018-09-13 [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits +[`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 669cdb0735dcc..f2357e2b5de92 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -151,6 +151,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold) * [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one) * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map) +* [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals) ## `cognitive-complexity-threshold` diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index a7a81b6f4219e..9741b94d50413 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -260,7 +260,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS. /// /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = ""] diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index dc6df9d82ed57..f4389db627d8b 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -17,6 +17,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { + 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 4182524581b4f..0a5baabd973ea 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -387,6 +387,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::ITER_SKIP_ZERO_INFO, crate::methods::ITER_WITH_DRAIN_INFO, crate::methods::JOIN_ABSOLUTE_PATHS_INFO, + crate::methods::MANUAL_C_STR_LITERALS_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, crate::methods::MANUAL_FIND_MAP_INFO, crate::methods::MANUAL_IS_VARIANT_AND_INFO, diff --git a/clippy_lints/src/methods/manual_c_str_literals.rs b/clippy_lints/src/methods/manual_c_str_literals.rs new file mode 100644 index 0000000000000..cb9fb373c10a6 --- /dev/null +++ b/clippy_lints/src/methods/manual_c_str_literals.rs @@ -0,0 +1,197 @@ +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::get_parent_expr; +use clippy_utils::source::snippet; +use rustc_ast::{LitKind, StrStyle}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind}; +use rustc_lint::LateContext; +use rustc_span::{sym, Span, Symbol}; + +use super::MANUAL_C_STR_LITERALS; + +/// Checks: +/// - `b"...".as_ptr()` +/// - `b"...".as_ptr().cast()` +/// - `"...".as_ptr()` +/// - `"...".as_ptr().cast()` +/// +/// Iff the parent call of `.cast()` isn't `CStr::from_ptr`, to avoid linting twice. +pub(super) fn check_as_ptr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + receiver: &'tcx Expr<'tcx>, + msrv: &Msrv, +) { + if let ExprKind::Lit(lit) = receiver.kind + && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node + && let casts_removed = peel_ptr_cast_ancestors(cx, expr) + && !get_parent_expr(cx, casts_removed).is_some_and( + |parent| matches!(parent.kind, ExprKind::Call(func, _) if is_c_str_function(cx, func).is_some()), + ) + && let Some(sugg) = rewrite_as_cstr(cx, lit.span) + && msrv.meets(msrvs::C_STR_LITERALS) + { + span_lint_and_sugg( + cx, + MANUAL_C_STR_LITERALS, + receiver.span, + "manually constructing a nul-terminated string", + r#"use a `c""` literal"#, + sugg, + // an additional cast may be needed, since the type of `CStr::as_ptr` and + // `"".as_ptr()` can differ and is platform dependent + Applicability::HasPlaceholders, + ); + } +} + +/// Checks if the callee is a "relevant" `CStr` function considered by this lint. +/// Returns the function name. +fn is_c_str_function(cx: &LateContext<'_>, func: &Expr<'_>) -> Option { + if let ExprKind::Path(QPath::TypeRelative(cstr, fn_name)) = &func.kind + && let TyKind::Path(QPath::Resolved(_, ty_path)) = &cstr.kind + && cx.tcx.lang_items().c_str() == ty_path.res.opt_def_id() + { + Some(fn_name.ident.name) + } else { + None + } +} + +/// Checks calls to the `CStr` constructor functions: +/// - `CStr::from_bytes_with_nul(..)` +/// - `CStr::from_bytes_with_nul_unchecked(..)` +/// - `CStr::from_ptr(..)` +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>], msrv: &Msrv) { + if let Some(fn_name) = is_c_str_function(cx, func) + && let [arg] = args + && msrv.meets(msrvs::C_STR_LITERALS) + { + match fn_name.as_str() { + name @ ("from_bytes_with_nul" | "from_bytes_with_nul_unchecked") + if !arg.span.from_expansion() + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node => + { + check_from_bytes(cx, expr, arg, name); + }, + "from_ptr" => check_from_ptr(cx, expr, arg), + _ => {}, + } + } +} + +/// Checks `CStr::from_ptr(b"foo\0".as_ptr().cast())` +fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) { + if let ExprKind::MethodCall(method, lit, ..) = peel_ptr_cast(arg).kind + && method.ident.name == sym::as_ptr + && !lit.span.from_expansion() + && let ExprKind::Lit(lit) = lit.kind + && let LitKind::ByteStr(_, StrStyle::Cooked) = lit.node + && let Some(sugg) = rewrite_as_cstr(cx, lit.span) + { + span_lint_and_sugg( + cx, + MANUAL_C_STR_LITERALS, + expr.span, + "calling `CStr::from_ptr` with a byte string literal", + r#"use a `c""` literal"#, + sugg, + Applicability::MachineApplicable, + ); + } +} +/// Checks `CStr::from_bytes_with_nul(b"foo\0")` +fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: &str) { + let (span, applicability) = if let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(method, ..) = parent.kind + && [sym::unwrap, sym::expect].contains(&method.ident.name) + { + (parent.span, Applicability::MachineApplicable) + } else if method == "from_bytes_with_nul_unchecked" { + // `*_unchecked` returns `&CStr` directly, nothing needs to be changed + (expr.span, Applicability::MachineApplicable) + } else { + // User needs to remove error handling, can't be machine applicable + (expr.span, Applicability::HasPlaceholders) + }; + + let Some(sugg) = rewrite_as_cstr(cx, arg.span) else { + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_C_STR_LITERALS, + span, + "calling `CStr::new` with a byte string literal", + r#"use a `c""` literal"#, + sugg, + applicability, + ); +} + +/// Rewrites a byte string literal to a c-str literal. +/// `b"foo\0"` -> `c"foo"` +/// +/// Returns `None` if it doesn't end in a NUL byte. +fn rewrite_as_cstr(cx: &LateContext<'_>, span: Span) -> Option { + let mut sugg = String::from("c") + snippet(cx, span.source_callsite(), "..").trim_start_matches('b'); + + // NUL byte should always be right before the closing quote. + if let Some(quote_pos) = sugg.rfind('"') { + // Possible values right before the quote: + // - literal NUL value + if sugg.as_bytes()[quote_pos - 1] == b'\0' { + sugg.remove(quote_pos - 1); + } + // - \x00 + else if sugg[..quote_pos].ends_with("\\x00") { + sugg.replace_range(quote_pos - 4..quote_pos, ""); + } + // - \0 + else if sugg[..quote_pos].ends_with("\\0") { + sugg.replace_range(quote_pos - 2..quote_pos, ""); + } + // No known suffix, so assume it's not a C-string. + else { + return None; + } + } + + Some(sugg) +} + +fn get_cast_target<'tcx>(e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + match &e.kind { + ExprKind::MethodCall(method, receiver, [], _) if method.ident.as_str() == "cast" => Some(receiver), + ExprKind::Cast(expr, _) => Some(expr), + _ => None, + } +} + +/// `x.cast()` -> `x` +/// `x as *const _` -> `x` +/// `x` -> `x` (returns the same expression for non-cast exprs) +fn peel_ptr_cast<'tcx>(e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + get_cast_target(e).map_or(e, peel_ptr_cast) +} + +/// Same as `peel_ptr_cast`, but the other way around, by walking up the ancestor cast expressions: +/// +/// `foo(x.cast() as *const _)` +/// ^ given this `x` expression, returns the `foo(...)` expression +fn peel_ptr_cast_ancestors<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + let mut prev = e; + for (_, node) in cx.tcx.hir().parent_iter(e.hir_id) { + if let Node::Expr(e) = node + && get_cast_target(e).is_some() + { + prev = e; + } else { + break; + } + } + prev +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bac72fd255a21..e8a7a321bf4b7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -51,6 +51,7 @@ mod iter_skip_zero; mod iter_with_drain; mod iterator_step_by_zero; mod join_absolute_paths; +mod manual_c_str_literals; mod manual_is_variant_and; mod manual_next_back; mod manual_ok_or; @@ -3977,6 +3978,39 @@ declare_clippy_lint! { "making no use of the \"map closure\" when calling `.map_or_else(|err| handle_error(err), |n| n)`" } +declare_clippy_lint! { + /// Checks for the manual creation of C strings (a string with a `NUL` byte at the end), either + /// through one of the `CStr` constructor functions, or more plainly by calling `.as_ptr()` + /// on a (byte) string literal with a hardcoded `\0` byte at the end. + /// + /// ### Why is this bad? + /// This can be written more concisely using `c"str"` literals and is also less error-prone, + /// because the compiler checks for interior `NUL` bytes and the terminating `NUL` byte is inserted automatically. + /// + /// ### Example + /// ```no_run + /// # use std::ffi::CStr; + /// # mod libc { pub unsafe fn puts(_: *const i8) {} } + /// fn needs_cstr(_: &CStr) {} + /// + /// needs_cstr(CStr::from_bytes_with_nul(b"Hello\0").unwrap()); + /// unsafe { libc::puts("World\0".as_ptr().cast()) } + /// ``` + /// Use instead: + /// ```no_run + /// # use std::ffi::CStr; + /// # mod libc { pub unsafe fn puts(_: *const i8) {} } + /// fn needs_cstr(_: &CStr) {} + /// + /// needs_cstr(c"Hello"); + /// unsafe { libc::puts(c"World".as_ptr()) } + /// ``` + #[clippy::version = "1.76.0"] + pub MANUAL_C_STR_LITERALS, + pedantic, + r#"creating a `CStr` through functions when `c""` literals can be used"# +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4136,6 +4170,7 @@ impl_lint_pass!(Methods => [ STR_SPLIT_AT_NEWLINE, OPTION_AS_REF_CLONED, UNNECESSARY_RESULT_MAP_OR_ELSE, + MANUAL_C_STR_LITERALS, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4163,6 +4198,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { hir::ExprKind::Call(func, args) => { from_iter_instead_of_collect::check(cx, expr, args, func); unnecessary_fallible_conversions::check_function(cx, expr, func); + manual_c_str_literals::check(cx, expr, func, args, &self.msrv); }, hir::ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; @@ -4381,6 +4417,7 @@ impl Methods { } }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), + ("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("cloned", []) => { diff --git a/tests/ui/manual_c_str_literals.fixed b/tests/ui/manual_c_str_literals.fixed new file mode 100644 index 0000000000000..a24d7088c882a --- /dev/null +++ b/tests/ui/manual_c_str_literals.fixed @@ -0,0 +1,60 @@ +#![warn(clippy::manual_c_str_literals)] +#![allow(clippy::no_effect)] + +use std::ffi::CStr; + +macro_rules! cstr { + ($s:literal) => { + CStr::from_bytes_with_nul(concat!($s, "\0").as_bytes()).unwrap() + }; +} + +macro_rules! macro_returns_c_str { + () => { + CStr::from_bytes_with_nul(b"foo\0").unwrap(); + }; +} + +macro_rules! macro_returns_byte_string { + () => { + b"foo\0" + }; +} + +#[clippy::msrv = "1.76.0"] +fn pre_stabilization() { + CStr::from_bytes_with_nul(b"foo\0"); +} + +#[clippy::msrv = "1.77.0"] +fn post_stabilization() { + c"foo"; +} + +fn main() { + c"foo"; + c"foo"; + c"foo"; + c"foo\\0sdsd"; + CStr::from_bytes_with_nul(br"foo\\0sdsd\0").unwrap(); + CStr::from_bytes_with_nul(br"foo\x00").unwrap(); + CStr::from_bytes_with_nul(br##"foo#a\0"##).unwrap(); + + unsafe { c"foo" }; + unsafe { c"foo" }; + let _: *const _ = c"foo".as_ptr(); + let _: *const _ = c"foo".as_ptr(); + let _: *const _ = "foo".as_ptr(); // not a C-string + let _: *const _ = "".as_ptr(); + let _: *const _ = c"foo".as_ptr().cast::(); + let _ = "็”ต่„‘".as_ptr(); + let _ = "็”ต่„‘\\".as_ptr(); + let _ = c"็”ต่„‘\\".as_ptr(); + let _ = c"็”ต่„‘".as_ptr(); + let _ = c"็”ต่„‘".as_ptr(); + + // Macro cases, don't lint: + cstr!("foo"); + macro_returns_c_str!(); + CStr::from_bytes_with_nul(macro_returns_byte_string!()).unwrap(); +} diff --git a/tests/ui/manual_c_str_literals.rs b/tests/ui/manual_c_str_literals.rs new file mode 100644 index 0000000000000..0a007786720f7 --- /dev/null +++ b/tests/ui/manual_c_str_literals.rs @@ -0,0 +1,60 @@ +#![warn(clippy::manual_c_str_literals)] +#![allow(clippy::no_effect)] + +use std::ffi::CStr; + +macro_rules! cstr { + ($s:literal) => { + CStr::from_bytes_with_nul(concat!($s, "\0").as_bytes()).unwrap() + }; +} + +macro_rules! macro_returns_c_str { + () => { + CStr::from_bytes_with_nul(b"foo\0").unwrap(); + }; +} + +macro_rules! macro_returns_byte_string { + () => { + b"foo\0" + }; +} + +#[clippy::msrv = "1.76.0"] +fn pre_stabilization() { + CStr::from_bytes_with_nul(b"foo\0"); +} + +#[clippy::msrv = "1.77.0"] +fn post_stabilization() { + CStr::from_bytes_with_nul(b"foo\0"); +} + +fn main() { + CStr::from_bytes_with_nul(b"foo\0"); + CStr::from_bytes_with_nul(b"foo\x00"); + CStr::from_bytes_with_nul(b"foo\0").unwrap(); + CStr::from_bytes_with_nul(b"foo\\0sdsd\0").unwrap(); + CStr::from_bytes_with_nul(br"foo\\0sdsd\0").unwrap(); + CStr::from_bytes_with_nul(br"foo\x00").unwrap(); + CStr::from_bytes_with_nul(br##"foo#a\0"##).unwrap(); + + unsafe { CStr::from_ptr(b"foo\0".as_ptr().cast()) }; + unsafe { CStr::from_ptr(b"foo\0".as_ptr() as *const _) }; + let _: *const _ = b"foo\0".as_ptr(); + let _: *const _ = "foo\0".as_ptr(); + let _: *const _ = "foo".as_ptr(); // not a C-string + let _: *const _ = "".as_ptr(); + let _: *const _ = b"foo\0".as_ptr().cast::(); + let _ = "็”ต่„‘".as_ptr(); + let _ = "็”ต่„‘\\".as_ptr(); + let _ = "็”ต่„‘\\\0".as_ptr(); + let _ = "็”ต่„‘\0".as_ptr(); + let _ = "็”ต่„‘\x00".as_ptr(); + + // Macro cases, don't lint: + cstr!("foo"); + macro_returns_c_str!(); + CStr::from_bytes_with_nul(macro_returns_byte_string!()).unwrap(); +} diff --git a/tests/ui/manual_c_str_literals.stderr b/tests/ui/manual_c_str_literals.stderr new file mode 100644 index 0000000000000..8de4e16f010d6 --- /dev/null +++ b/tests/ui/manual_c_str_literals.stderr @@ -0,0 +1,83 @@ +error: calling `CStr::new` with a byte string literal + --> $DIR/manual_c_str_literals.rs:31:5 + | +LL | CStr::from_bytes_with_nul(b"foo\0"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` + | + = note: `-D clippy::manual-c-str-literals` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_c_str_literals)]` + +error: calling `CStr::new` with a byte string literal + --> $DIR/manual_c_str_literals.rs:35:5 + | +LL | CStr::from_bytes_with_nul(b"foo\0"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` + +error: calling `CStr::new` with a byte string literal + --> $DIR/manual_c_str_literals.rs:36:5 + | +LL | CStr::from_bytes_with_nul(b"foo\x00"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` + +error: calling `CStr::new` with a byte string literal + --> $DIR/manual_c_str_literals.rs:37:5 + | +LL | CStr::from_bytes_with_nul(b"foo\0").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` + +error: calling `CStr::new` with a byte string literal + --> $DIR/manual_c_str_literals.rs:38:5 + | +LL | CStr::from_bytes_with_nul(b"foo\\0sdsd\0").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo\\0sdsd"` + +error: calling `CStr::from_ptr` with a byte string literal + --> $DIR/manual_c_str_literals.rs:43:14 + | +LL | unsafe { CStr::from_ptr(b"foo\0".as_ptr().cast()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` + +error: calling `CStr::from_ptr` with a byte string literal + --> $DIR/manual_c_str_literals.rs:44:14 + | +LL | unsafe { CStr::from_ptr(b"foo\0".as_ptr() as *const _) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` + +error: manually constructing a nul-terminated string + --> $DIR/manual_c_str_literals.rs:45:23 + | +LL | let _: *const _ = b"foo\0".as_ptr(); + | ^^^^^^^^ help: use a `c""` literal: `c"foo"` + +error: manually constructing a nul-terminated string + --> $DIR/manual_c_str_literals.rs:46:23 + | +LL | let _: *const _ = "foo\0".as_ptr(); + | ^^^^^^^ help: use a `c""` literal: `c"foo"` + +error: manually constructing a nul-terminated string + --> $DIR/manual_c_str_literals.rs:49:23 + | +LL | let _: *const _ = b"foo\0".as_ptr().cast::(); + | ^^^^^^^^ help: use a `c""` literal: `c"foo"` + +error: manually constructing a nul-terminated string + --> $DIR/manual_c_str_literals.rs:52:13 + | +LL | let _ = "็”ต่„‘\\\0".as_ptr(); + | ^^^^^^^^^^ help: use a `c""` literal: `c"็”ต่„‘\\"` + +error: manually constructing a nul-terminated string + --> $DIR/manual_c_str_literals.rs:53:13 + | +LL | let _ = "็”ต่„‘\0".as_ptr(); + | ^^^^^^^^ help: use a `c""` literal: `c"็”ต่„‘"` + +error: manually constructing a nul-terminated string + --> $DIR/manual_c_str_literals.rs:54:13 + | +LL | let _ = "็”ต่„‘\x00".as_ptr(); + | ^^^^^^^^^^ help: use a `c""` literal: `c"็”ต่„‘"` + +error: aborting due to 13 previous errors + diff --git a/tests/ui/strlen_on_c_strings.fixed b/tests/ui/strlen_on_c_strings.fixed index 8304e2afd8b6b..1e7d04ffb9df9 100644 --- a/tests/ui/strlen_on_c_strings.fixed +++ b/tests/ui/strlen_on_c_strings.fixed @@ -1,5 +1,5 @@ #![warn(clippy::strlen_on_c_strings)] -#![allow(dead_code)] +#![allow(dead_code, clippy::manual_c_str_literals)] #![feature(rustc_private)] extern crate libc; diff --git a/tests/ui/strlen_on_c_strings.rs b/tests/ui/strlen_on_c_strings.rs index deba40a9ea5e6..c3ad03591d4ec 100644 --- a/tests/ui/strlen_on_c_strings.rs +++ b/tests/ui/strlen_on_c_strings.rs @@ -1,5 +1,5 @@ #![warn(clippy::strlen_on_c_strings)] -#![allow(dead_code)] +#![allow(dead_code, clippy::manual_c_str_literals)] #![feature(rustc_private)] extern crate libc; From 7895b987123258e9ab166ebd0e5baff81207ec67 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 24 Jan 2024 18:01:56 +0000 Subject: [PATCH 53/68] Add CoroutineClosure to TyKind, AggregateKind, UpvarArgs --- clippy_lints/src/dereference.rs | 1 + clippy_lints/src/utils/author.rs | 3 +++ tests/ui/author/blocks.stdout | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 8ff54dfcfa0dc..194cf69ea7ed0 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -881,6 +881,7 @@ impl TyCoercionStability { | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Closure(..) + | ty::CoroutineClosure(..) | ty::Never | ty::Tuple(_) | ty::Alias(ty::Projection, _) => Self::Deref, diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index b26ebe5cee321..288df0fd663f0 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -490,6 +490,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { format!("ClosureKind::Coroutine(CoroutineKind::Coroutine(Movability::{movability:?})") }, }, + ClosureKind::CoroutineClosure(desugaring) => format!( + "ClosureKind::CoroutineClosure(CoroutineDesugaring::{desugaring:?})" + ), }; let ret_ty = match fn_decl.output { diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index 8c4d71e68f80f..579f137f861e1 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -40,10 +40,10 @@ if let ExprKind::Block(block, None) = expr.kind { // report your lint here } -if let ExprKind::Closure { capture_clause: CaptureBy::Value { .. }, fn_decl: fn_decl, body: body_id, closure_kind: ClosureKind::Closure, .. } = expr.kind +if let ExprKind::Closure { capture_clause: CaptureBy::Value { .. }, fn_decl: fn_decl, body: body_id, closure_kind: ClosureKind::CoroutineClosure(CoroutineDesugaring::Async), .. } = expr.kind && let FnRetTy::DefaultReturn(_) = fn_decl.output && expr1 = &cx.tcx.hir().body(body_id).value - && let ExprKind::Closure { capture_clause: CaptureBy::Value { .. }, fn_decl: fn_decl1, body: body_id1, closure_kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Closure)), .. } = expr1.kind + && let ExprKind::Closure { capture_clause: CaptureBy::Ref, fn_decl: fn_decl1, body: body_id1, closure_kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Closure)), .. } = expr1.kind && let FnRetTy::DefaultReturn(_) = fn_decl1.output && expr2 = &cx.tcx.hir().body(body_id1).value && let ExprKind::Block(block, None) = expr2.kind From 36f7248da0cbc5fd9ed1903ba7393ac80c3b1cd5 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Tue, 6 Feb 2024 02:43:34 -0500 Subject: [PATCH 54/68] Fix release year in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e88f6df853a2f..9b853567219c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ document. ## Rust 1.76 -Current stable, released 2023-02-08 +Current stable, released 2024-02-08 [View all 85 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-11-02T20%3A23%3A40Z..2023-12-16T13%3A11%3A08Z+base%3Amaster) From 15ffe839bacebb2883fd77959df319eebefaf8cf Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Tue, 6 Feb 2024 20:03:49 +0100 Subject: [PATCH 55/68] add fixme --- .../rustc_trait_selection/src/solve/normalizes_to/mod.rs | 5 ++++- compiler/rustc_trait_selection/src/traits/project.rs | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 571963fa735f7..b0bdeb7d2a7f4 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -410,7 +410,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { } ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints. + // This is the "fallback impl" for type parameters, unnormalizable projections + // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. + // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't + // exist. Instead, `Pointee` should be a supertrait of `Sized`. let sized_predicate = ty::TraitRef::from_lang_item( tcx, LangItem::Sized, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 33298c4b750d9..ecaab0e008543 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2302,9 +2302,10 @@ fn confirm_builtin_candidate<'cx, 'tcx>( }; let metadata_ty = self_ty.ptr_metadata_ty_or_tail(tcx, normalize).unwrap_or_else(|tail| { if tail == self_ty { - // This is the fallback case for type parameters, unnormalizable projections - // and opaque types. - // If the `self_ty` is `Sized`, then the metadata is `()`. + // This is the "fallback impl" for type parameters, unnormalizable projections + // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. + // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't + // exist. Instead, `Pointee` should be a supertrait of `Sized`. let sized_predicate = ty::TraitRef::from_lang_item( tcx, LangItem::Sized, From c0d9776562359268ccc385b55fe55db640578fe1 Mon Sep 17 00:00:00 2001 From: joboet Date: Thu, 8 Feb 2024 12:41:46 +0100 Subject: [PATCH 56/68] std: move path into `sys` --- library/std/src/sys/mod.rs | 1 + library/std/src/sys/pal/hermit/mod.rs | 2 -- library/std/src/sys/pal/sgx/mod.rs | 1 - library/std/src/sys/pal/solid/mod.rs | 1 - library/std/src/sys/pal/teeos/mod.rs | 2 -- library/std/src/sys/pal/uefi/mod.rs | 1 - library/std/src/sys/pal/uefi/path.rs | 25 ------------------- library/std/src/sys/pal/unix/mod.rs | 1 - library/std/src/sys/pal/unsupported/mod.rs | 2 -- library/std/src/sys/pal/wasi/mod.rs | 2 -- library/std/src/sys/pal/wasm/mod.rs | 2 -- library/std/src/sys/pal/windows/fs.rs | 2 +- library/std/src/sys/pal/windows/mod.rs | 5 ++-- library/std/src/sys/pal/xous/mod.rs | 2 -- library/std/src/sys/pal/zkvm/mod.rs | 4 --- library/std/src/sys/path/mod.rs | 18 +++++++++++++ .../src/sys/{pal/sgx/path.rs => path/sgx.rs} | 0 .../sys/{pal/unix/path.rs => path/unix.rs} | 0 .../path.rs => path/unsupported_backslash.rs} | 0 .../{pal/windows/path.rs => path/windows.rs} | 4 +-- .../windows/path => path/windows}/tests.rs | 0 21 files changed, 24 insertions(+), 51 deletions(-) delete mode 100644 library/std/src/sys/pal/uefi/path.rs create mode 100644 library/std/src/sys/path/mod.rs rename library/std/src/sys/{pal/sgx/path.rs => path/sgx.rs} (100%) rename library/std/src/sys/{pal/unix/path.rs => path/unix.rs} (100%) rename library/std/src/sys/{pal/solid/path.rs => path/unsupported_backslash.rs} (100%) rename library/std/src/sys/{pal/windows/path.rs => path/windows.rs} (99%) rename library/std/src/sys/{pal/windows/path => path/windows}/tests.rs (100%) diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index e03e98b18d29f..fae21636897b8 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -7,6 +7,7 @@ mod personality; pub mod cmath; pub mod os_str; +pub mod path; // FIXME(117276): remove this, move feature implementations into individual // submodules. diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 3c83afa280be5..57cc656e266a1 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -28,8 +28,6 @@ pub mod io; pub mod memchr; pub mod net; pub mod os; -#[path = "../unix/path.rs"] -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index a769fc1ef5932..46f3e5b401da5 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -22,7 +22,6 @@ pub mod io; pub mod memchr; pub mod net; pub mod os; -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 46699e64169f5..be8e00339021f 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -29,7 +29,6 @@ pub mod fs; pub mod io; pub mod net; pub mod os; -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index 95a5b97ea4237..7953104486c56 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -25,8 +25,6 @@ pub mod net; #[path = "../unsupported/once.rs"] pub mod once; pub mod os; -#[path = "../unix/path.rs"] -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 9ee753aa1a0c1..bc0d21129e293 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -26,7 +26,6 @@ pub mod net; #[path = "../unsupported/once.rs"] pub mod once; pub mod os; -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] diff --git a/library/std/src/sys/pal/uefi/path.rs b/library/std/src/sys/pal/uefi/path.rs deleted file mode 100644 index 106682eee56b6..0000000000000 --- a/library/std/src/sys/pal/uefi/path.rs +++ /dev/null @@ -1,25 +0,0 @@ -use super::unsupported; -use crate::ffi::OsStr; -use crate::io; -use crate::path::{Path, PathBuf, Prefix}; - -pub const MAIN_SEP_STR: &str = "\\"; -pub const MAIN_SEP: char = '\\'; - -#[inline] -pub fn is_sep_byte(b: u8) -> bool { - b == b'\\' -} - -#[inline] -pub fn is_verbatim_sep(b: u8) -> bool { - b == b'\\' -} - -pub fn parse_prefix(_p: &OsStr) -> Option> { - None -} - -pub(crate) fn absolute(_path: &Path) -> io::Result { - unsupported() -} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 43cb9d89be9dd..976a437c17ff9 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -27,7 +27,6 @@ pub mod net; #[cfg(target_os = "l4re")] pub use self::l4re::net; pub mod os; -pub mod path; pub mod pipe; pub mod process; pub mod rand; diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index b56ded8579c4d..88f939cbab924 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -9,8 +9,6 @@ pub mod locks; pub mod net; pub mod once; pub mod os; -#[path = "../unix/path.rs"] -pub mod path; pub mod pipe; pub mod process; pub mod stdio; diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 4ffc8ecdd67ee..116878ee99681 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -30,8 +30,6 @@ pub mod io; pub mod net; pub mod os; -#[path = "../unix/path.rs"] -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 76306b618d82b..567555118d707 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -28,8 +28,6 @@ pub mod io; pub mod net; #[path = "../unsupported/os.rs"] pub mod os; -#[path = "../unix/path.rs"] -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index 2bdd3d96fa48c..b82a83ae7a3e8 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -16,8 +16,8 @@ use crate::sys::{c, cvt, Align8}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::thread; -use super::path::maybe_verbatim; use super::{api, to_u16s, IoResult}; +use crate::sys::path::maybe_verbatim; pub struct File { handle: Handle, diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 364521dba40ac..726a4509f280f 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -23,7 +23,6 @@ pub mod locks; pub mod memchr; pub mod net; pub mod os; -pub mod path; pub mod pipe; pub mod process; pub mod rand; @@ -210,7 +209,7 @@ pub fn to_u16s>(s: S) -> crate::io::Result> { // Once the syscall has completed (errors bail out early) the second closure is // yielded the data which has been read from the syscall. The return value // from this closure is then the return value of the function. -fn fill_utf16_buf(mut f1: F1, f2: F2) -> crate::io::Result +pub fn fill_utf16_buf(mut f1: F1, f2: F2) -> crate::io::Result where F1: FnMut(*mut u16, c::DWORD) -> c::DWORD, F2: FnOnce(&[u16]) -> T, @@ -274,7 +273,7 @@ where } } -fn os2path(s: &[u16]) -> PathBuf { +pub fn os2path(s: &[u16]) -> PathBuf { PathBuf::from(OsString::from_wide(s)) } diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index b4948d7e583b4..c9bad4ef019b5 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -12,8 +12,6 @@ pub mod io; pub mod locks; pub mod net; pub mod os; -#[path = "../unix/path.rs"] -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 7f221dc4fd98a..e859269831aa9 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -24,10 +24,6 @@ pub mod net; #[path = "../unsupported/once.rs"] pub mod once; pub mod os; -#[path = "../unix/os_str.rs"] -pub mod os_str; -#[path = "../unix/path.rs"] -pub mod path; #[path = "../unsupported/pipe.rs"] pub mod pipe; #[path = "../unsupported/process.rs"] diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs new file mode 100644 index 0000000000000..24a94ec782824 --- /dev/null +++ b/library/std/src/sys/path/mod.rs @@ -0,0 +1,18 @@ +cfg_if::cfg_if! { + if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + pub use sgx::*; + } else if #[cfg(any( + target_os = "uefi", + target_os = "solid_asp3", + ))] { + mod unsupported_backslash; + pub use unsupported_backslash::*; + } else { + mod unix; + pub use unix::*; + } +} diff --git a/library/std/src/sys/pal/sgx/path.rs b/library/std/src/sys/path/sgx.rs similarity index 100% rename from library/std/src/sys/pal/sgx/path.rs rename to library/std/src/sys/path/sgx.rs diff --git a/library/std/src/sys/pal/unix/path.rs b/library/std/src/sys/path/unix.rs similarity index 100% rename from library/std/src/sys/pal/unix/path.rs rename to library/std/src/sys/path/unix.rs diff --git a/library/std/src/sys/pal/solid/path.rs b/library/std/src/sys/path/unsupported_backslash.rs similarity index 100% rename from library/std/src/sys/pal/solid/path.rs rename to library/std/src/sys/path/unsupported_backslash.rs diff --git a/library/std/src/sys/pal/windows/path.rs b/library/std/src/sys/path/windows.rs similarity index 99% rename from library/std/src/sys/pal/windows/path.rs rename to library/std/src/sys/path/windows.rs index d9684f2175313..cebc791023115 100644 --- a/library/std/src/sys/pal/windows/path.rs +++ b/library/std/src/sys/path/windows.rs @@ -1,8 +1,8 @@ -use super::{c, fill_utf16_buf, to_u16s}; use crate::ffi::{OsStr, OsString}; use crate::io; use crate::path::{Path, PathBuf, Prefix}; use crate::ptr; +use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s}; #[cfg(test)] mod tests; @@ -339,6 +339,6 @@ pub(crate) fn absolute(path: &Path) -> io::Result { // `lpfilename` is a pointer to a null terminated string that is not // invalidated until after `GetFullPathNameW` returns successfully. |buffer, size| unsafe { c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()) }, - super::os2path, + os2path, ) } diff --git a/library/std/src/sys/pal/windows/path/tests.rs b/library/std/src/sys/path/windows/tests.rs similarity index 100% rename from library/std/src/sys/pal/windows/path/tests.rs rename to library/std/src/sys/path/windows/tests.rs From 8801144e3a4fab2c4af853f3b428b26c88fedd8b Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Thu, 8 Feb 2024 19:42:23 +0300 Subject: [PATCH 57/68] better error message on download CI LLVM failure Signed-off-by: onur-ozkan --- src/bootstrap/src/core/download.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index ec404ab8580cc..e63d60feff125 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -720,8 +720,10 @@ download-rustc = false if !tarball.exists() { let help_on_error = "ERROR: failed to download llvm from ci - HELP: old builds get deleted after a certain time - HELP: if trying to compile an old commit of rustc, disable `download-ci-llvm` in config.toml: + HELP: There could be two reasons behind this: + 1) The host triple is not supported for `download-ci-llvm`. + 2) Old builds get deleted after a certain time. + HELP: In either case, disable `download-ci-llvm` in your config.toml: [llvm] download-ci-llvm = false From 031c46dd907c320db797c468e24d05b90ce25ac3 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 8 Feb 2024 19:13:28 +0100 Subject: [PATCH 58/68] Bump nightly version -> 2024-02-08 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index d2d56e59ee3fb..fcf5c456270cd 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-01-25" +channel = "nightly-2024-02-08" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 2ca6c8419432b18474516a3b62d6d1ea46b9df78 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 8 Feb 2024 19:13:40 +0100 Subject: [PATCH 59/68] Bump Clippy version -> 0.1.78 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eda20531e40f3..321424880d1e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.77" +version = "0.1.78" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 74b8e5eaa1c41..2edc5ed592cb0 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.77" +version = "0.1.78" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 416e9a680dd73..6e6e315bb6561 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.77" +version = "0.1.78" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index b8869eedf52c2..c7454fa3328b8 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.77" +version = "0.1.78" edition = "2021" publish = false diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 5aaafb4172144..0f90cef5cdd2b 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.77" +version = "0.1.78" edition = "2021" publish = false From 92d4060176bfb4a5751e17269f993dc7268fbf80 Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Fri, 26 Jan 2024 01:37:15 +0530 Subject: [PATCH 60/68] Implement SystemTime for UEFI - Uses SystemTable->RuntimeServices->GetTime() Signed-off-by: Ayush Singh --- library/std/src/sys/pal/uefi/helpers.rs | 8 ++ library/std/src/sys/pal/uefi/mod.rs | 1 - library/std/src/sys/pal/uefi/tests.rs | 20 +++++ library/std/src/sys/pal/uefi/time.rs | 105 ++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 library/std/src/sys/pal/uefi/time.rs diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 9837cc89f2d39..ba53ed88f3765 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -146,3 +146,11 @@ pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> Option let system_handle = uefi::env::try_image_handle()?; open_protocol(system_handle, protocol_guid).ok() } + +/// Get RuntimeServices +pub(crate) fn runtime_services() -> Option> { + let system_table: NonNull = + crate::os::uefi::env::try_system_table()?.cast(); + let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services }; + NonNull::new(runtime_services) +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 9ee753aa1a0c1..687e7d99d134a 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -38,7 +38,6 @@ pub mod thread; pub mod thread_local_key; #[path = "../unsupported/thread_parking.rs"] pub mod thread_parking; -#[path = "../unsupported/time.rs"] pub mod time; mod helpers; diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 8806eda3ac0a6..5eb36da922b54 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -1,4 +1,6 @@ use super::alloc::*; +use super::time::*; +use crate::time::Duration; #[test] fn align() { @@ -19,3 +21,21 @@ fn align() { } } } + +#[test] +fn epoch() { + let t = r_efi::system::Time { + year: 1970, + month: 1, + day: 1, + hour: 0, + minute: 0, + second: 0, + nanosecond: 0, + timezone: r_efi::efi::UNSPECIFIED_TIMEZONE, + daylight: 0, + pad1: 0, + pad2: 0, + }; + assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0)); +} diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs new file mode 100644 index 0000000000000..68f428c38fbb3 --- /dev/null +++ b/library/std/src/sys/pal/uefi/time.rs @@ -0,0 +1,105 @@ +use crate::time::Duration; + +const SECS_IN_MINUTE: u64 = 60; +const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; +const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +impl Instant { + pub fn now() -> Instant { + panic!("time not implemented on this platform") + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option { + self.0.checked_sub(other.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(Instant(self.0.checked_sub(*other)?)) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + system_time_internal::now() + .unwrap_or_else(|| panic!("time not implemented on this platform")) + } + + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} + +pub(crate) mod system_time_internal { + use super::super::helpers; + use super::*; + use crate::mem::MaybeUninit; + use crate::ptr::NonNull; + use r_efi::efi::{RuntimeServices, Time}; + + pub fn now() -> Option { + let runtime_services: NonNull = helpers::runtime_services()?; + let mut t: MaybeUninit