From 998c72293f44ab0862afb943d229725764e083e9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 6 Feb 2024 09:51:39 +1100 Subject: [PATCH 01/26] Invert diagnostic lints. That is, change `diagnostic_outside_of_impl` and `untranslatable_diagnostic` from `allow` to `deny`, because more than half of the compiler has be converted to use translated diagnostics. This commit removes more `deny` attributes than it adds `allow` attributes, which proves that this change is warranted. --- clippy_config/src/lib.rs | 4 +++- clippy_lints/src/lib.rs | 7 ++++++- clippy_utils/src/lib.rs | 8 +++++++- src/driver.rs | 2 ++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs index 533e375a3104..5449feed090a 100644 --- a/clippy_config/src/lib.rs +++ b/clippy_config/src/lib.rs @@ -4,7 +4,9 @@ #![allow( clippy::must_use_candidate, clippy::missing_panics_doc, - rustc::untranslatable_diagnostic_trivial + rustc::diagnostic_outside_of_impl, + rustc::untranslatable_diagnostic, + rustc::untranslatable_diagnostic_trivial, )] extern crate rustc_ast; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index feb4d188f397..f6608b229539 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -10,7 +10,12 @@ #![feature(stmt_expr_attributes)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] -#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)] +#![allow( + clippy::missing_docs_in_private_items, + clippy::must_use_candidate, + rustc::diagnostic_outside_of_impl, + rustc::untranslatable_diagnostic, +)] #![warn(trivial_casts, trivial_numeric_casts)] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 4e499ff4cc61..36034861df5b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -8,7 +8,13 @@ #![feature(assert_matches)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] -#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] +#![allow( + clippy::missing_errors_doc, + clippy::missing_panics_doc, + clippy::must_use_candidate, + rustc::diagnostic_outside_of_impl, + rustc::untranslatable_diagnostic, +)] // warn on the same lints as `clippy_lints` #![warn(trivial_casts, trivial_numeric_casts)] // warn on lints, that are included in `rust-lang/rust`s bootstrap diff --git a/src/driver.rs b/src/driver.rs index b944a299256c..1b159f5937a2 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,3 +1,5 @@ +#![allow(rustc::diagnostic_outside_of_impl)] +#![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] #![feature(let_chains)] #![feature(lazy_cell)] From b168c20951d602872eb89f4b78aaa4cf7f6e2563 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 20 Jan 2024 15:21:27 +0300 Subject: [PATCH 02/26] hir: Make sure all `HirId`s have corresponding HIR `Node`s --- 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 41168230752a..2f9f04832a75 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -94,7 +94,7 @@ fn visit_id(&mut self, hir_id: HirId) { cx.tcx.opt_hir_node(hir_id) } else { let owner = cx.tcx.hir_owner_nodes(hir_id.owner); - owner.nodes.get(hir_id.local_id).copied().flatten().map(|p| p.node) + owner.nodes.get(hir_id.local_id).copied().map(|p| p.node) }; let Some(node) = node else { return; From b247f41596bdc5f904ff9af482bac414655ac567 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 21 Jan 2024 21:13:15 +0300 Subject: [PATCH 03/26] hir: Remove `fn opt_hir_id` and `fn opt_span` --- clippy_lints/src/absolute_paths.rs | 2 +- clippy_lints/src/casts/cast_slice_different_sizes.rs | 5 ++--- clippy_lints/src/casts/unnecessary_cast.rs | 3 +-- clippy_lints/src/derivable_impls.rs | 2 +- clippy_lints/src/empty_drop.rs | 2 +- clippy_lints/src/escape.rs | 6 +++--- clippy_lints/src/explicit_write.rs | 2 +- clippy_lints/src/index_refutable_slice.rs | 4 ++-- clippy_lints/src/len_zero.rs | 6 +++--- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/methods/filter_next.rs | 2 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 4 ++-- clippy_lints/src/min_ident_chars.rs | 2 +- clippy_lints/src/mixed_read_write_in_expression.rs | 5 +---- clippy_lints/src/non_copy_const.rs | 2 +- clippy_lints/src/same_name_method.rs | 4 ++-- clippy_lints/src/self_named_constructors.rs | 2 +- clippy_utils/src/lib.rs | 10 +++++----- 19 files changed, 31 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/absolute_paths.rs b/clippy_lints/src/absolute_paths.rs index 3822b83b4bc6..8ba661afeeb6 100644 --- a/clippy_lints/src/absolute_paths.rs +++ b/clippy_lints/src/absolute_paths.rs @@ -62,7 +62,7 @@ fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) { } = self; if !path.span.from_expansion() - && let Some(node) = cx.tcx.opt_hir_node(hir_id) + && let node = cx.tcx.hir_node(hir_id) && !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(_, _))) && let [first, rest @ ..] = path.segments // Handle `::std` diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 91bad8256ecb..0f29743856ac 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -68,9 +68,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let map = cx.tcx.hir(); - if let Some(parent_id) = map.opt_parent_id(expr.hir_id) - && let Some(parent) = cx.tcx.opt_hir_node(parent_id) - { + if let Some(parent_id) = map.opt_parent_id(expr.hir_id) { + let parent = cx.tcx.hir_node(parent_id); let expr = match parent { Node::Block(block) => { if let Some(parent_expr) = block.expr { diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index bb86b6f30759..81d0def4322d 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -144,8 +144,7 @@ pub(super) fn check<'tcx>( if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { if let Some(id) = path_to_local(cast_expr) - && let Some(span) = cx.tcx.hir().opt_span(id) - && !span.eq_ctxt(cast_expr.span) + && !cx.tcx.hir().span(id).eq_ctxt(cast_expr.span) { // Binding context is different than the identifiers context. // Weird macro wizardry could be involved here. diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 6b0423200d76..b0f46f5c646c 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -195,7 +195,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { && let Some(def_id) = trait_ref.trait_def_id() && cx.tcx.is_diagnostic_item(sym::Default, def_id) && let impl_item_hir = child.id.hir_id() - && let Some(Node::ImplItem(impl_item)) = cx.tcx.opt_hir_node(impl_item_hir) + && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir) && let ImplItemKind::Fn(_, b) = &impl_item.kind && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b) && let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind() diff --git a/clippy_lints/src/empty_drop.rs b/clippy_lints/src/empty_drop.rs index 1d2b907b9482..74db250b3ae9 100644 --- a/clippy_lints/src/empty_drop.rs +++ b/clippy_lints/src/empty_drop.rs @@ -42,7 +42,7 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { }) = item.kind && trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait() && let impl_item_hir = child.id.hir_id() - && let Some(Node::ImplItem(impl_item)) = cx.tcx.opt_hir_node(impl_item_hir) + && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir) && let ImplItemKind::Fn(_, b) = &impl_item.kind && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b) && let func_expr = peel_blocks(func_expr) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index b7776263060b..218d7c6c01ae 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -123,11 +123,11 @@ fn check_fn( // TODO: Replace with Map::is_argument(..) when it's fixed fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { - match tcx.opt_hir_node(id) { - Some(Node::Pat(Pat { + match tcx.hir_node(id) { + Node::Pat(Pat { kind: PatKind::Binding(..), .. - })) => (), + }) => (), _ => return false, } diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index e8c1e5db35e0..de048fef5f22 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -111,7 +111,7 @@ fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) // Find id of the local that expr_end_of_block resolves to && let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind && let Res::Local(expr_res) = expr_path.res - && let Some(Node::Pat(res_pat)) = cx.tcx.opt_hir_node(expr_res) + && let Node::Pat(res_pat) = cx.tcx.hir_node(expr_res) // Find id of the local we found in the block && let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 5417c13d0796..252be30c4e27 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -248,7 +248,7 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { // Checking for slice indexing && let parent_id = map.parent_id(expr.hir_id) - && let Some(hir::Node::Expr(parent_expr)) = cx.tcx.opt_hir_node(parent_id) + && let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr) && let Ok(index_value) = index_value.try_into() @@ -256,7 +256,7 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { // Make sure that this slice index is read only && let maybe_addrof_id = map.parent_id(parent_id) - && let Some(hir::Node::Expr(maybe_addrof_expr)) = cx.tcx.opt_hir_node(maybe_addrof_id) + && let hir::Node::Expr(maybe_addrof_expr) = cx.tcx.hir_node(maybe_addrof_id) && let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind { use_info.index_use.push((index_value, map.span(parent_expr.hir_id))); diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index c1ab020117ca..27d85cde5320 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -147,9 +147,9 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) && let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()) { - let (name, kind) = match cx.tcx.opt_hir_node(ty_hir_id) { - Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), - Some(Node::Item(x)) => match x.kind { + let (name, kind) = match cx.tcx.hir_node(ty_hir_id) { + Node::ForeignItem(x) => (x.ident.name, "extern type"), + Node::Item(x) => match x.kind { ItemKind::Struct(..) => (x.ident.name, "struct"), ItemKind::Enum(..) => (x.ident.name, "enum"), ItemKind::Union(..) => (x.ident.name, "union"), diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 920a887a6fd1..5f015db2b33b 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -63,7 +63,7 @@ fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: && let PatKind::Binding(bind_ann, ..) = pat.kind && !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)) && let parent_node = cx.tcx.hir().parent_id(hir_id) - && let Some(Node::Local(parent_let_expr)) = cx.tcx.opt_hir_node(parent_node) + && let Node::Local(parent_let_expr) = cx.tcx.hir_node(parent_node) && let Some(init) = parent_let_expr.init { match init.kind { diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 71a83a68db93..e1768c6d9764 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -76,7 +76,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Also ensures the const is nonzero since zero can't be a divisor && const1 == const2 && const2 == const3 && let Some(hir_id) = path_to_local(expr3) - && let Some(Node::Pat(_)) = cx.tcx.opt_hir_node(hir_id) + && let Node::Pat(_) = cx.tcx.hir_node(hir_id) { // Apply only to params or locals with annotated types match cx.tcx.hir().find_parent(hir_id) { diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 9251130a3054..7339362193e5 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>( // add note if not multi-line span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { let (applicability, pat) = if let Some(id) = path_to_local(recv) - && let Some(hir::Node::Pat(pat)) = cx.tcx.opt_hir_node(id) + && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) && let hir::PatKind::Binding(BindingAnnotation(_, Mutability::Not), _, ident, _) = pat.kind { (Applicability::Unspecified, Some((pat.span, ident))) diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 624597ffca94..ab36f854fcb1 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -135,7 +135,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) { if let Res::Local(local_id) = path.res - && let Some(Node::Pat(pat)) = self.cx.tcx.opt_hir_node(local_id) + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) && let PatKind::Binding(_, local_id, ..) = pat.kind { self.identifiers.insert(local_id); @@ -166,7 +166,7 @@ fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) { && let ExprKind::Path(ref path) = expr.kind && let QPath::Resolved(_, path) = path && let Res::Local(local_id) = path.res - && let Some(Node::Pat(pat)) = self.cx.tcx.opt_hir_node(local_id) + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) && let PatKind::Binding(_, local_id, ..) = pat.kind && self.identifiers.contains(&local_id) { diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index 2f9f04832a75..70cc43e266c6 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -91,7 +91,7 @@ fn visit_id(&mut self, hir_id: HirId) { let node = if hir_id.local_id == ItemLocalId::from_u32(0) { // In this case, we can just use `find`, `Owner`'s `node` field is private anyway so we can't // reimplement it even if we wanted to - cx.tcx.opt_hir_node(hir_id) + Some(cx.tcx.hir_node(hir_id)) } else { let owner = cx.tcx.hir_owner_nodes(hir_id.owner); owner.nodes.get(hir_id.local_id).copied().map(|p| p.node) diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 195ce17629a7..b593e48ae2e1 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -213,11 +213,8 @@ fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { if parent_id == cur_id { break; } - let Some(parent_node) = vis.cx.tcx.opt_hir_node(parent_id) else { - break; - }; - let stop_early = match parent_node { + let stop_early = match vis.cx.tcx.hir_node(parent_id) { Node::Expr(expr) => check_expr(vis, expr), Node::Stmt(stmt) => check_stmt(vis, stmt), Node::Item(_) => { diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index f8365deebd46..10ab380ba1bc 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -453,7 +453,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if parent_id == cur_expr.hir_id { break; } - if let Some(Node::Expr(parent_expr)) = cx.tcx.opt_hir_node(parent_id) { + if let Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) { match &parent_expr.kind { ExprKind::AddrOf(..) => { // `&e` => `e` must be referenced. diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 7a351dab2d45..07806b182f22 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -76,8 +76,8 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { match of_trait { Some(trait_ref) => { let mut methods_in_trait: BTreeSet = - if let Some(Node::TraitRef(TraitRef { path, .. })) = - cx.tcx.opt_hir_node(trait_ref.hir_ref_id) + if let Node::TraitRef(TraitRef { path, .. }) = + cx.tcx.hir_node(trait_ref.hir_ref_id) && let Res::Def(DefKind::Trait, did) = path.res { // FIXME: if diff --git a/clippy_lints/src/self_named_constructors.rs b/clippy_lints/src/self_named_constructors.rs index 98f3235af10a..fc5a45dd56d6 100644 --- a/clippy_lints/src/self_named_constructors.rs +++ b/clippy_lints/src/self_named_constructors.rs @@ -73,7 +73,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem< if let Some(self_def) = self_ty.ty_adt_def() && let Some(self_local_did) = self_def.did().as_local() && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did) - && let Some(Node::Item(x)) = cx.tcx.opt_hir_node(self_id) + && let Node::Item(x) = cx.tcx.hir_node(self_id) && let type_name = x.ident.name.as_str().to_lowercase() && (impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 4e499ff4cc61..3f936009e44e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -177,10 +177,10 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr /// canonical binding `HirId`. pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { let hir = cx.tcx.hir(); - if let Some(Node::Pat(pat)) = cx.tcx.opt_hir_node(hir_id) + if let Node::Pat(pat) = cx.tcx.hir_node(hir_id) && matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)) && let parent = hir.parent_id(hir_id) - && let Some(Node::Local(local)) = cx.tcx.opt_hir_node(parent) + && let Node::Local(local) = cx.tcx.hir_node(parent) { return local.init; } @@ -1327,7 +1327,7 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio let map = &cx.tcx.hir(); let enclosing_node = map .get_enclosing_scope(hir_id) - .and_then(|enclosing_id| cx.tcx.opt_hir_node(enclosing_id)); + .map(|enclosing_id| cx.tcx.hir_node(enclosing_id)); enclosing_node.and_then(|node| match node { Node::Block(block) => Some(block), Node::Item(&Item { @@ -2696,10 +2696,10 @@ pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option> { )), Self::Return(id) => { let hir_id = cx.tcx.local_def_id_to_hir_id(id.def_id); - if let Some(Node::Expr(Expr { + if let Node::Expr(Expr { kind: ExprKind::Closure(c), .. - })) = cx.tcx.opt_hir_node(hir_id) + }) = cx.tcx.hir_node(hir_id) { match c.fn_decl.output { FnRetTy::DefaultReturn(_) => None, From c8d57e8aa4435551ac394d0762515a93d77251e5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 7 Feb 2024 17:17:52 +0000 Subject: [PATCH 04/26] Use correct param env in clippy --- clippy_lints/src/default.rs | 4 ++-- clippy_lints/src/useless_conversion.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index d8a070b785d5..8789efcc9944 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -131,7 +131,7 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // only when assigning `... = Default::default()` && is_expr_default(expr, cx) && let binding_type = cx.typeck_results().node_type(binding_id) - && let Some(adt) = binding_type.ty_adt_def() + && let ty::Adt(adt, args) = *binding_type.kind() && adt.is_struct() && let variant = adt.non_enum_variant() && (adt.did().is_local() || !variant.is_field_list_non_exhaustive()) @@ -144,7 +144,7 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { .fields .iter() .all(|field| { - is_copy(cx, cx.tcx.type_of(field.did).instantiate_identity()) + is_copy(cx, cx.tcx.type_of(field.did).instantiate(cx.tcx, args)) }) && (!has_drop(cx, binding_type) || all_fields_are_copy) { diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 2e0a0f6cb3e4..f7a455977fac 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -86,7 +86,6 @@ fn into_iter_bound<'tcx>( param_index: u32, node_args: GenericArgsRef<'tcx>, ) -> Option { - let param_env = cx.tcx.param_env(fn_did); let mut into_iter_span = None; for (pred, span) in cx.tcx.explicit_predicates_of(fn_did).predicates { @@ -111,7 +110,7 @@ fn into_iter_bound<'tcx>( })); let predicate = EarlyBinder::bind(tr).instantiate(cx.tcx, args); - let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), param_env, predicate); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); if !cx .tcx .infer_ctxt() From 96ff004a683e8b33bd41e31316dfc5d7e51d14d2 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 1 Feb 2024 08:35:37 +0000 Subject: [PATCH 05/26] inline a function that is only used in clippy --- clippy_lints/src/lifetimes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ffef84d1fade..f5636945f203 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -176,7 +176,7 @@ fn check_fn_inner<'tcx>( _ => None, }); for bound in lifetimes { - if !bound.is_static() && !bound.is_elided() { + if bound.res != LifetimeName::Static && !bound.is_elided() { return; } } From 6ca819f202dc8c449d40f04edd81ea87f1e4ee6c Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Wed, 7 Feb 2024 10:26:00 -0500 Subject: [PATCH 06/26] Add a new debug_assertions instrinsic (compiler) And in clippy --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 81f4fcc2133d..8d5bcd665ad2 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -174,7 +174,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_), _) | Rvalue::ShallowInitBox(_, _) => { + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions, _) | Rvalue::ShallowInitBox(_, _) => { Ok(()) }, Rvalue::UnaryOp(_, operand) => { From f3b3d23416f1da77103bb74788f23b4f9c78ee7e Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 8 Feb 2024 20:24:42 +0100 Subject: [PATCH 07/26] Merge commit '60cb29c5e4f9772685c9873752196725c946a849' into clippyup --- .github/driver.sh | 15 + CHANGELOG.md | 66 +++- Cargo.toml | 2 +- book/src/lint_configuration.md | 33 ++ clippy_config/Cargo.toml | 2 +- clippy_config/src/conf.rs | 22 +- clippy_config/src/msrvs.rs | 13 + clippy_lints/Cargo.toml | 2 +- clippy_lints/src/booleans.rs | 1 + .../src/cargo/lint_groups_priority.rs | 168 ++++++++ clippy_lints/src/cargo/mod.rs | 43 ++- clippy_lints/src/casts/mod.rs | 30 +- clippy_lints/src/casts/ref_as_ptr.rs | 55 +++ clippy_lints/src/declared_lints.rs | 6 + clippy_lints/src/doc/mod.rs | 2 +- clippy_lints/src/eta_reduction.rs | 50 +-- clippy_lints/src/incompatible_msrv.rs | 133 +++++++ clippy_lints/src/iter_over_hash_type.rs | 2 +- clippy_lints/src/lib.rs | 21 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 6 +- clippy_lints/src/manual_retain.rs | 145 ++++--- .../src/methods/manual_c_str_literals.rs | 197 ++++++++++ clippy_lints/src/methods/map_unwrap_or.rs | 2 +- clippy_lints/src/methods/mod.rs | 65 ++++ clippy_lints/src/methods/unnecessary_fold.rs | 1 - .../methods/unnecessary_result_map_or_else.rs | 95 +++++ clippy_lints/src/no_effect.rs | 2 +- clippy_lints/src/only_used_in_recursion.rs | 5 +- clippy_lints/src/operators/mod.rs | 15 +- .../src/operators/modulo_arithmetic.rs | 27 +- clippy_lints/src/redundant_closure_call.rs | 14 +- clippy_lints/src/redundant_locals.rs | 26 ++ .../src/redundant_type_annotations.rs | 11 +- clippy_lints/src/repeat_vec_with_capacity.rs | 2 +- clippy_lints/src/returns.rs | 31 +- clippy_lints/src/to_string_trait_impl.rs | 67 ++++ clippy_lints/src/unconditional_recursion.rs | 86 +++-- clippy_lints/src/unused_io_amount.rs | 101 +++-- clippy_lints/src/utils/author.rs | 6 +- clippy_lints/src/wildcard_imports.rs | 16 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/src/lib.rs | 359 ++++++++++-------- clippy_utils/src/ty.rs | 29 -- declare_clippy_lint/Cargo.toml | 2 +- rust-toolchain | 2 +- src/driver.rs | 25 +- .../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 + .../min_rust_version/min_rust_version.fixed | 2 +- .../min_rust_version/min_rust_version.rs | 2 +- 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 | 6 + 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_whitelist/clippy.toml | 1 + .../wildcard_imports.fixed | 26 ++ .../wildcard_imports.rs | 26 ++ .../wildcard_imports.stderr | 11 + 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/eta.fixed | 71 ++++ tests/ui/eta.rs | 71 ++++ tests/ui/eta.stderr | 28 +- tests/ui/format_args.fixed | 1 + tests/ui/format_args.rs | 1 + tests/ui/format_args.stderr | 50 +-- tests/ui/ignored_unit_patterns.fixed | 7 +- tests/ui/ignored_unit_patterns.rs | 7 +- tests/ui/ignored_unit_patterns.stderr | 18 +- tests/ui/incompatible_msrv.rs | 23 ++ tests/ui/incompatible_msrv.stderr | 23 ++ 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/manual_retain.fixed | 63 +++ tests/ui/manual_retain.rs | 63 +++ tests/ui/manual_retain.stderr | 142 +++++-- tests/ui/modulo_arithmetic_integral.rs | 8 + tests/ui/needless_borrow.fixed | 3 + tests/ui/needless_borrow.rs | 3 + tests/ui/needless_borrow.stderr | 20 +- .../needless_return_with_question_mark.fixed | 50 +++ .../ui/needless_return_with_question_mark.rs | 50 +++ tests/ui/never_loop.rs | 11 +- tests/ui/nonminimal_bool.rs | 11 + tests/ui/redundant_closure_call_fixable.fixed | 9 + tests/ui/redundant_closure_call_fixable.rs | 9 + tests/ui/redundant_locals.rs | 46 +++ tests/ui/redundant_locals.stderr | 66 ++-- tests/ui/redundant_type_annotations.rs | 7 +- tests/ui/redundant_type_annotations.stderr | 4 +- tests/ui/ref_as_ptr.fixed | 110 ++++++ tests/ui/ref_as_ptr.rs | 110 ++++++ tests/ui/ref_as_ptr.stderr | 269 +++++++++++++ tests/ui/strlen_on_c_strings.fixed | 2 +- tests/ui/strlen_on_c_strings.rs | 2 +- tests/ui/to_string_trait_impl.rs | 31 ++ tests/ui/to_string_trait_impl.stderr | 16 + tests/ui/unconditional_recursion.rs | 62 +++ tests/ui/unconditional_recursion.stderr | 14 +- tests/ui/unnecessary_fold.fixed | 6 + tests/ui/unnecessary_fold.rs | 6 + tests/ui/unnecessary_fold.stderr | 41 +- tests/ui/unnecessary_operation.fixed | 7 + tests/ui/unnecessary_operation.rs | 7 + 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 ++ tests/ui/unnecessary_to_owned.fixed | 3 + tests/ui/unnecessary_to_owned.rs | 3 + tests/ui/unnecessary_to_owned.stderr | 190 ++++----- 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 +- tests/ui/unused_io_amount.rs | 43 +++ tests/ui/unused_io_amount.stderr | 8 +- tests/ui/while_let_on_iterator.fixed | 16 +- tests/ui/while_let_on_iterator.rs | 14 +- tests/ui/while_let_on_iterator.stderr | 12 +- 131 files changed, 3886 insertions(+), 621 deletions(-) mode change 100644 => 100755 .github/driver.sh create mode 100644 clippy_lints/src/cargo/lint_groups_priority.rs create mode 100644 clippy_lints/src/casts/ref_as_ptr.rs create mode 100644 clippy_lints/src/incompatible_msrv.rs create mode 100644 clippy_lints/src/methods/manual_c_str_literals.rs create mode 100644 clippy_lints/src/methods/unnecessary_result_map_or_else.rs create mode 100644 clippy_lints/src/to_string_trait_impl.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 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 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 create mode 100644 tests/ui/incompatible_msrv.rs create mode 100644 tests/ui/incompatible_msrv.stderr 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 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 create mode 100644 tests/ui/to_string_trait_impl.rs create mode 100644 tests/ui/to_string_trait_impl.stderr 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/.github/driver.sh b/.github/driver.sh old mode 100644 new mode 100755 index c05c6ecc1151..40a2aad0f537 --- 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/CHANGELOG.md b/CHANGELOG.md index 5fa45ceeb39b..9b853567219c 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 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) + +### 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) @@ -5198,6 +5252,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 @@ -5276,6 +5331,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 @@ -5284,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 @@ -5523,6 +5580,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 @@ -5622,6 +5680,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 @@ -5677,6 +5736,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 @@ -5819,4 +5879,6 @@ 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 +[`allowed-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-wildcard-imports diff --git a/Cargo.toml b/Cargo.toml index eda20531e40f..321424880d1e 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/book/src/lint_configuration.md b/book/src/lint_configuration.md index 7f7aff92bf19..f2357e2b5de9 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` @@ -828,3 +829,35 @@ 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) + + +## `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/Cargo.toml b/clippy_config/Cargo.toml index 74b8e5eaa1c4..2edc5ed592cb 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_config/src/conf.rs b/clippy_config/src/conf.rs index 4e9ddbf8259d..9741b94d5041 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -260,7 +260,7 @@ pub fn get_configuration_metadata() -> Vec { /// /// 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 = ""] @@ -567,6 +567,26 @@ pub fn get_configuration_metadata() -> Vec { /// 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), + /// Lint: 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. + (allowed_wildcard_imports: FxHashSet = FxHashSet::default()), } /// Search for the configuration file. diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs index 72d5b9aff28d..f4389db627d8 100644 --- a/clippy_config/src/msrvs.rs +++ b/clippy_config/src/msrvs.rs @@ -3,6 +3,7 @@ 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 { @@ -16,6 +17,8 @@ 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 } 1,68,0 { PATH_MAIN_SEPARATOR_STR } @@ -58,6 +61,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/Cargo.toml b/clippy_lints/Cargo.toml index 416e9a680dd7..6e6e315bb656 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_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index e11f83f22603..2d1c250ace90 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -499,6 +499,7 @@ 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 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 000000000000..a39b972b56a2 --- /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 d8107f61f371..95d5449781b4 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 @@ "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 @@ pub struct 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 @@ fn check_crate(&mut self, cx: &LateContext<'_>) { ]; 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/casts/mod.rs b/clippy_lints/src/casts/mod.rs index e05b8f66d861..14f2f4a7f59d 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -18,6 +18,7 @@ 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 @@ "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 @@ pub fn new(msrv: Msrv) -> Self { AS_PTR_CAST_MUT, CAST_NAN_TO_INT, ZERO_PTR, + REF_AS_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -771,7 +797,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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 000000000000..d600d2aec1b5 --- /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 639edd8da301..0a5baabd973e 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -71,6 +71,7 @@ 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, @@ -96,6 +97,7 @@ 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, @@ -212,6 +214,7 @@ 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, @@ -384,6 +387,7 @@ 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, @@ -452,6 +456,7 @@ 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, @@ -656,6 +661,7 @@ 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/doc/mod.rs b/clippy_lints/src/doc/mod.rs index ba452775015d..2b4ce6ddfaaa 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -226,7 +226,7 @@ /// 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/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 450cee4007c7..40be71a0e5d6 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,15 +3,14 @@ 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; @@ -21,8 +20,8 @@ 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 +135,14 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { .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 +192,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }, 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( @@ -195,11 +202,12 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { "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, ); }, @@ -301,27 +309,3 @@ fn check_ty(from_ty: Ty<'_>, to_ty: Ty<'_>) -> bool { .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_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs new file mode 100644 index 000000000000..f2f0e7d42662 --- /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/iter_over_hash_type.rs b/clippy_lints/src/iter_over_hash_type.rs index 8110c1970d9b..6c6eff9ba48b 100644 --- a/clippy_lints/src/iter_over_hash_type.rs +++ b/clippy_lints/src/iter_over_hash_type.rs @@ -34,7 +34,7 @@ /// 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/lib.rs b/clippy_lints/src/lib.rs index feb4d188f397..5636f46b22fe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -26,6 +26,7 @@ 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_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; @@ -325,6 +327,7 @@ 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; @@ -521,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, @@ -575,6 +579,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: _, @@ -872,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, + allowed_wildcard_imports.clone(), + )) + }); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(|_| Box::>::default()); @@ -968,7 +978,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)); @@ -1094,6 +1109,8 @@ 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()))); + 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/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 3c9bde86bb6f..b5e39b33c6a1 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -672,7 +672,7 @@ /// } /// } /// ``` - #[clippy::version = "1.75.0"] + #[clippy::version = "1.76.0"] pub INFINITE_LOOP, restriction, "possibly unintended infinite loop" diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 62bc663191f6..245a903f9982 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/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 1fe247dacb93..6f15fca089eb 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -11,6 +11,7 @@ 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 @@ (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 @@ /// ```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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { && 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { 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}| {})", - snippet(cx, left_expr.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, - ); + 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, "..") + ), + ); + } + // 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/clippy_lints/src/methods/manual_c_str_literals.rs b/clippy_lints/src/methods/manual_c_str_literals.rs new file mode 100644 index 000000000000..cb9fb373c10a --- /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/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 52ea584a2c8c..3226fa9cd3fa 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 03bcf108914a..e8a7a321bf4b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -51,6 +51,7 @@ 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; @@ -113,6 +114,7 @@ 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; @@ -3951,6 +3953,64 @@ "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)`" +} + +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, @@ -4109,6 +4169,8 @@ pub fn new( MANUAL_IS_VARIANT_AND, 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. @@ -4136,6 +4198,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { 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; @@ -4354,6 +4417,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } }, ("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", []) => { @@ -4592,6 +4656,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }, ("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_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index f3577ef6082b..2046692bbd0b 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, 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 000000000000..7b0cf48ac43b --- /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); + } + }, + _ => {}, + } + } +} diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 0d234f7f9b52..580160efeb70 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/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index d621051ef165..ae14016f4823 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -252,7 +252,7 @@ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) { { ( 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/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 4c09c4eea581..e002429e3a47 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -771,6 +771,7 @@ 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 @@ pub struct 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { 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 cb3916484c1d..40d4a842befb 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/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 334e6770ae40..915da18aafe2 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -2,6 +2,7 @@ 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}; @@ -13,6 +14,7 @@ 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 @@ -89,7 +91,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 @@ -110,6 +117,7 @@ fn find_innermost_closure<'tcx>( } else { ty::Asyncness::No }, + body.params, )); steps -= 1; } @@ -152,7 +160,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { // 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/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index 2c511ee0bc02..700a5dd4a851 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -4,8 +4,10 @@ use rustc_ast::Mutability; 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}; 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 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { // the local is user-controlled && !in_external_macro(cx.sess(), local.span) && !is_from_proc_macro(cx, expr) + && !is_by_value_closure_capture(cx, local.hir_id, binding_id) { span_lint_and_help( cx, @@ -82,6 +85,29 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { } } +/// 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_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) + }) +} + /// 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/clippy_lints/src/redundant_type_annotations.rs b/clippy_lints/src/redundant_type_annotations.rs index 07fcb69afbc4..c8352c052659 100644 --- a/clippy_lints/src/redundant_type_annotations.rs +++ b/clippy_lints/src/redundant_type_annotations.rs @@ -188,7 +188,6 @@ fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir:: 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 @@ fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir:: } }, 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/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs index 5a4933a3fceb..fcb79f6d694e 100644 --- a/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/clippy_lints/src/repeat_vec_with_capacity.rs @@ -42,7 +42,7 @@ /// // ^^^ 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" diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index e01750465873..2af466d3f51e 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -2,10 +2,14 @@ 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, @@ -18,6 +22,7 @@ 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 +151,14 @@ fn applicability(&self) -> Applicability { } } -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}"), } } } @@ -181,7 +186,15 @@ 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 + // 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_node(cx.tcx.hir().get_parent_item(expr.hir_id)) && let ItemKind::Fn(_, _, body) = item.kind 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 000000000000..e1cea99085fd --- /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/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index 209035804e43..224ec475c510 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/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index adc66e15ff50..6b3ea7700b73 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 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) } 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/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 288df0fd663f..29c67341a467 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -490,9 +490,9 @@ macro_rules! kind { format!("ClosureKind::Coroutine(CoroutineKind::Coroutine(Movability::{movability:?})") }, }, - ClosureKind::CoroutineClosure(desugaring) => format!( - "ClosureKind::CoroutineClosure(CoroutineDesugaring::{desugaring:?})" - ), + ClosureKind::CoroutineClosure(desugaring) => { + format!("ClosureKind::CoroutineClosure(CoroutineDesugaring::{desugaring:?})") + }, }; let ret_ty = match fn_decl.output { diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index b82bd1d7e7c8..5410e8ac1178 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 @@ pub struct WildcardImports { warn_on_all: bool, test_modules_deep: u32, + allowed_segments: FxHashSet, } impl WildcardImports { - pub fn new(warn_on_all: bool) -> Self { + pub fn new(warn_on_all: bool, allowed_wildcard_imports: FxHashSet) -> Self { Self { warn_on_all, test_modules_deep: 0, + allowed_segments: allowed_wildcard_imports, } } } @@ -190,6 +193,7 @@ fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> boo item.span.from_expansion() || is_prelude_import(segments) || (is_super_only_import(segments) && self.test_modules_deep > 0) + || is_allowed_via_config(segments, &self.allowed_segments) } } @@ -198,10 +202,18 @@ fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> boo 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 `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| allowed_segments.contains(seg.ident.as_str())) +} diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index b8869eedf52c..c7454fa3328b 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/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3f936009e44e..79bf0551506d 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)] @@ -75,6 +76,7 @@ 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 +86,7 @@ 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 +105,8 @@ 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; @@ -114,10 +117,7 @@ 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; @@ -1353,46 +1353,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(_) => (), @@ -2590,26 +2556,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; @@ -2619,18 +2589,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 } @@ -2674,6 +2645,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. @@ -2750,144 +2723,104 @@ pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option> { 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_node(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().span(parent_id).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, - }), + }) => ExprUseNode::Return(owner_id), - Node::ExprField(field) if field.span.ctxt() == ctxt => Some(ExprUseCtxt { - node: ExprUseNode::Field(field), - adjustments, - is_ty_unified, - moved_before_use, - }), - - 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, + 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::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, - }), - 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::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(_, Some(_)) | ExprKind::Break(..) => { - is_ty_unified = true; - moved_before_use = true; - None - }, - 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, } }) } @@ -3264,3 +3197,131 @@ fn visit_arm(&mut self, arm: &Arm<'tcx>) { }) } } + +/// 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/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 11bb16ff6c5b..6762c8830050 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -1000,35 +1000,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/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 5aaafb417214..0f90cef5cdd2 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 diff --git a/rust-toolchain b/rust-toolchain index d2d56e59ee3f..fcf5c456270c 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"] diff --git a/src/driver.rs b/src/driver.rs index b944a299256c..f5e52f787ab4 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -22,9 +22,11 @@ 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]); } }; 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 000000000000..103e60d84844 --- /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 000000000000..4ce41f781711 --- /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 000000000000..8b137891791f --- /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 000000000000..e9fcf803d936 --- /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 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/lint_groups_priority/pass/src/lib.rs @@ -0,0 +1 @@ + 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 6c58e07d846e..497f783087a1 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 e1dc3f4389c0..6e7874108a34 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-toml/modulo_arithmetic/clippy.toml b/tests/ui-toml/modulo_arithmetic/clippy.toml new file mode 100644 index 000000000000..de80f4ae5983 --- /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 000000000000..27d27564baf3 --- /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 000000000000..da644b05a113 --- /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 fc683e514ba4..f097d2503e16 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 @@ -14,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 @@ -80,6 +82,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 @@ -91,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 @@ -157,6 +161,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 @@ -168,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 875aaeef6c93..68815c1756ac 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 +allowed-wildcard-imports = ["utils"] diff --git a/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/tests/ui-toml/wildcard_imports/wildcard_imports.fixed index 1752f48856c2..af72d6be0e09 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 331c2c59c222..91009dd8835f 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 f11fda6a0c6e..a733d786d0e6 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/clippy.toml b/tests/ui-toml/wildcard_imports_whitelist/clippy.toml new file mode 100644 index 000000000000..6b7882e64a8b --- /dev/null +++ b/tests/ui-toml/wildcard_imports_whitelist/clippy.toml @@ -0,0 +1 @@ +allowed-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 000000000000..d539525c45ed --- /dev/null +++ b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed @@ -0,0 +1,26 @@ +#![warn(clippy::wildcard_imports)] + +mod utils { + pub fn print() {} +} + +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 new file mode 100644 index 000000000000..9169d16a08be --- /dev/null +++ b/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs @@ -0,0 +1,26 @@ +#![warn(clippy::wildcard_imports)] + +mod utils { + pub fn print() {} +} + +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 new file mode 100644 index 000000000000..12c2f9cbada7 --- /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:19: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 + diff --git a/tests/ui/borrow_as_ptr.fixed b/tests/ui/borrow_as_ptr.fixed index 6c0de96d65e3..289a5ef38b8d 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 c37c5357c82c..b5328cb22dcd 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 43a7a6bf5b57..b98618059050 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 a361a36474de..f66554de3000 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 b3fe01442b74..1fc254aafa77 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 2f258bcf966a..1ef0a948a32a 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/eta.fixed b/tests/ui/eta.fixed index 32c7499bf73f..da28ec2e653a 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> { @@ -400,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 25b7431ba8cd..f924100f8f41 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> { @@ -400,3 +417,57 @@ fn f2(f: impl Fn(T) -> T) -> T { 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 951e4ac749c2..945de466d832 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -161,10 +161,34 @@ 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` -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 diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed index ddd5976c408a..cab20b11e073 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 18e1bc1af67f..bc3645cb2c2b 100644 --- a/tests/ui/format_args.rs +++ b/tests/ui/format_args.rs @@ -14,6 +14,7 @@ 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 dcdfa668aff3..2f1714296d6c 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/ignored_unit_patterns.fixed b/tests/ui/ignored_unit_patterns.fixed index 707a0e76e4eb..118f0b488952 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 544f2b8f6929..92feb9e6c281 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 05c8f281e554..18ca7ebbcf2c 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/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs new file mode 100644 index 000000000000..a92017fb0f62 --- /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 000000000000..bd5ecd6ed2ff --- /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 + diff --git a/tests/ui/manual_c_str_literals.fixed b/tests/ui/manual_c_str_literals.fixed new file mode 100644 index 000000000000..a24d7088c882 --- /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 000000000000..0a007786720f --- /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 000000000000..8de4e16f010d --- /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/manual_retain.fixed b/tests/ui/manual_retain.fixed index 4dea3e8bfe68..5540029bf6b1 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 @@ -171,6 +195,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 +275,37 @@ 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_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]; + + // 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 d839550f33a2..cee641d9d65f 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 @@ -177,6 +201,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 +281,37 @@ 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_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]; + + // 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 0c5b1383b6ae..2c872f3b430e 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: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:177: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:178: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:200: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: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: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:201: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:202: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: aborting due to 22 previous errors +error: this expression can be written more simply using `.retain()` + --> $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: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: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: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: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: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: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:316: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 diff --git a/tests/ui/modulo_arithmetic_integral.rs b/tests/ui/modulo_arithmetic_integral.rs index 4dbed24026cf..c427b8580e17 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; } diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index ff1e2dc88756..23e8bf8a468f 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 597021539acf..27771a8f15b3 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -131,6 +131,9 @@ fn from(s: &S) -> Self { 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 44552ee6abea..a21ed8382c14 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 diff --git a/tests/ui/needless_return_with_question_mark.fixed b/tests/ui/needless_return_with_question_mark.fixed index 0147c73a94b2..9b7da8526631 100644 --- a/tests/ui/needless_return_with_question_mark.fixed +++ b/tests/ui/needless_return_with_question_mark.fixed @@ -77,3 +77,53 @@ fn issue11616() -> Result<(), ()> { }; Ok(()) } + +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(()) + } +} + +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>(()))?; + }; + 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 66e1f438f8ce..68e76d2b6402 100644 --- a/tests/ui/needless_return_with_question_mark.rs +++ b/tests/ui/needless_return_with_question_mark.rs @@ -77,3 +77,53 @@ fn issue11616() -> Result<(), ()> { }; Ok(()) } + +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(()) + } +} + +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>(()))?; + }; + 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/never_loop.rs b/tests/ui/never_loop.rs index c67a6d4494e0..92f173d9db4a 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(); diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index 4d48ef14d31c..f7c3df7066f7 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -145,3 +145,14 @@ fn not(self) -> Self::Output { // 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 + }; +} diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index f272d8359a38..ce5c7f2600be 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 f45db8c9cff5..ac09390e6eae 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -102,3 +102,12 @@ fn demo() { 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_locals.rs b/tests/ui/redundant_locals.rs index 182d067a5e9f..f6909828aa9a 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, coroutines)] extern crate proc_macros; use proc_macros::{external, with_span}; @@ -163,3 +164,48 @@ 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(); + let v5 = String::new(); + let v6 = 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); + }); + assert_static(static || { + let v5 = v5; + yield; + }); + assert_static(|| { + let v6 = v6; + yield; + }); + + 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 30ab4aa2ea91..610d587ddadc 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,41 +13,29 @@ 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 - | -LL | let x = x; - | ^^^^^^^^^^ - | -help: `x` is initially defined here - --> $DIR/redundant_locals.rs:51:9 - | -LL | let x = 1; - | ^ - error: redundant redefinition of a binding `x` --> $DIR/redundant_locals.rs:53:5 | @@ -57,7 +45,7 @@ LL | let x = x; help: `x` is initially defined here --> $DIR/redundant_locals.rs:52:9 | -LL | let x = x; +LL | let x = 1; | ^ error: redundant redefinition of a binding `x` @@ -84,86 +72,98 @@ help: `x` is initially defined here LL | let x = x; | ^ +error: redundant redefinition of a binding `x` + --> $DIR/redundant_locals.rs:56:5 + | +LL | let x = x; + | ^^^^^^^^^^ + | +help: `x` is initially defined here + --> $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); | ^ diff --git a/tests/ui/redundant_type_annotations.rs b/tests/ui/redundant_type_annotations.rs index acf53fea2bb2..dc9b073ffba8 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 d1f26f1832e3..48df465ad497 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 diff --git a/tests/ui/ref_as_ptr.fixed b/tests/ui/ref_as_ptr.fixed new file mode 100644 index 000000000000..7a946393f259 --- /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 000000000000..6f745505b469 --- /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 000000000000..371d42df528b --- /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 + diff --git a/tests/ui/strlen_on_c_strings.fixed b/tests/ui/strlen_on_c_strings.fixed index 8304e2afd8b6..1e7d04ffb9df 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 deba40a9ea5e..c3ad03591d4e 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; diff --git a/tests/ui/to_string_trait_impl.rs b/tests/ui/to_string_trait_impl.rs new file mode 100644 index 000000000000..b0731632d450 --- /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 000000000000..55fa9f12c0e6 --- /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 7b898a6e0e78..6ad3bde51cde 100644 --- a/tests/ui/unconditional_recursion.rs +++ b/tests/ui/unconditional_recursion.rs @@ -206,6 +206,7 @@ fn eq(&self, other: &Self) -> bool { 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 @@ fn to_string(&self) -> String { 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 @@ fn to_string(&self) -> String { 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 @@ -288,4 +291,63 @@ fn eq(&self, other: &Self) -> bool { } } +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 + } + } + + // 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() {} diff --git a/tests/ui/unconditional_recursion.stderr b/tests/ui/unconditional_recursion.stderr index 094b80d4586c..93a5eac91d8e 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_fold.fixed b/tests/ui/unnecessary_fold.fixed index c884d26eb617..c5bc11b55ab5 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 2e6d6ba52eb9..3a5136eeeaeb 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 f0d039638421..123d4a3be755 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 diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index 463412daec08..11761c6c90e3 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 f0d28e289029..de0081289ac3 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -110,4 +110,11 @@ macro_rules! use_expr { // 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_result_map_or_else.fixed b/tests/ui/unnecessary_result_map_or_else.fixed new file mode 100644 index 000000000000..224e0b52d75d --- /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 000000000000..4fe950a4cfa6 --- /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 000000000000..0f83be5d5566 --- /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 + diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 2dd1d746626f..7f01c981a938 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 17fad33402b5..a270ed1e1c21 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -27,6 +27,7 @@ fn as_ref(&self) -> &str { } } +#[allow(clippy::to_string_trait_impl)] impl ToString for X { fn to_string(&self) -> String { self.0.to_string() @@ -265,6 +266,7 @@ fn as_ref(&self) -> &str { } } + #[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 ad6fa422b8cd..95ff5f2ec2cf 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 - | -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 - | -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 | -LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); +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:123:44 | -LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` +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:124:30 | -LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); +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: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: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: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 f87c898f9b79..e0ba216f41bf 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 db5719e58809..70efc6ebba5d 100644 --- a/tests/ui/unnecessary_to_owned_on_split.rs +++ b/tests/ui/unnecessary_to_owned_on_split.rs @@ -8,6 +8,7 @@ fn as_ref(&self) -> &str { } } +#[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 4cfaeed3384a..9aea15b48bfb 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)` diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 9974600dad5b..7e5a10c911bf 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 4af56d264bfa..1aab56966a88 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 diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index d628d2227b71..59b5c858d046 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 525dbbaaab66..559513d56946 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -441,7 +441,19 @@ fn f3(_: fn()) {} 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 cdc83b816670..7b9a9dc049a2 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 fc8f6628ab945fc1b8586d6a83d78f247eaf512c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 9 Feb 2024 23:58:36 +0300 Subject: [PATCH 08/26] hir: Remove `hir::Map::{opt_parent_id,parent_id,get_parent,find_parent}` --- clippy_lints/src/assertions_on_constants.rs | 3 +- .../src/casts/cast_slice_different_sizes.rs | 31 ++++++++----------- clippy_lints/src/casts/unnecessary_cast.rs | 2 +- clippy_lints/src/dbg_macro.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 3 +- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/escape.rs | 6 ++-- .../src/functions/impl_trait_in_params.rs | 4 +-- clippy_lints/src/ignored_unit_patterns.rs | 4 +-- clippy_lints/src/index_refutable_slice.rs | 11 ++----- clippy_lints/src/iter_without_into_iter.rs | 2 +- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/loops/same_item_push.rs | 3 +- clippy_lints/src/manual_hash_one.rs | 6 ++-- clippy_lints/src/manual_rem_euclid.rs | 6 ++-- .../src/matches/match_single_binding.rs | 16 +++++----- clippy_lints/src/matches/redundant_guards.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- .../src/methods/readonly_write_lock.rs | 4 +-- clippy_lints/src/methods/unnecessary_fold.rs | 2 +- .../src/methods/unnecessary_literal_unwrap.rs | 2 +- .../src/mixed_read_write_in_expression.rs | 3 +- clippy_lints/src/needless_pass_by_ref_mut.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/non_copy_const.rs | 2 +- .../src/operators/modulo_arithmetic.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- .../transmutes_expressible_as_ptr_casts.rs | 2 +- clippy_lints/src/tuple_array_conversions.rs | 9 ++---- clippy_lints/src/unit_types/unit_arg.rs | 8 ++--- clippy_lints/src/unnecessary_box_returns.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 2 +- clippy_lints/src/unused_async.rs | 2 +- clippy_lints/src/unwrap.rs | 2 +- .../internal_lints/metadata_collector.rs | 6 ++-- .../internal_lints/unnecessary_def_path.rs | 3 +- clippy_lints/src/vec.rs | 4 +-- clippy_utils/src/lib.rs | 10 +++--- clippy_utils/src/ty/type_certainty/mod.rs | 2 +- 39 files changed, 78 insertions(+), 102 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 1e327f7a6dfb..9365fbfaed08 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -46,11 +46,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { return; }; if let ConstantSource::Constant = source - && let Some(node) = cx.tcx.hir().find_parent(e.hir_id) && let Node::Item(Item { kind: ItemKind::Const(..), .. - }) = node + }) = cx.tcx.parent_hir_node(e.hir_id) { return; } diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 0f29743856ac..a31943f00218 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -67,25 +67,20 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv } fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let map = cx.tcx.hir(); - if let Some(parent_id) = map.opt_parent_id(expr.hir_id) { - let parent = cx.tcx.hir_node(parent_id); - let expr = match parent { - Node::Block(block) => { - if let Some(parent_expr) = block.expr { - parent_expr - } else { - return false; - } - }, - Node::Expr(expr) => expr, - _ => return false, - }; + let parent = cx.tcx.parent_hir_node(expr.hir_id); + let expr = match parent { + Node::Block(block) => { + if let Some(parent_expr) = block.expr { + parent_expr + } else { + return false; + } + }, + Node::Expr(expr) => expr, + _ => return false, + }; - matches!(expr.kind, ExprKind::Cast(..)) - } else { - false - } + matches!(expr.kind, ExprKind::Cast(..)) } /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 81d0def4322d..b4a23d0d4db4 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -65,7 +65,7 @@ pub(super) fn check<'tcx>( && let ExprKind::Path(qpath) = inner.kind && let QPath::Resolved(None, Path { res, .. }) = qpath && let Res::Local(hir_id) = res - && let parent = cx.tcx.hir().get_parent(*hir_id) + && let parent = cx.tcx.parent_hir_node(*hir_id) && let Node::Local(local) = parent { if let Some(ty) = local.ty diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 9424a9103db8..ec66556cebff 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -63,7 +63,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { ExprKind::Block(..) => { // If the `dbg!` macro is a "free" statement and not contained within other expressions, // remove the whole statement. - if let Some(Node::Stmt(_)) = cx.tcx.hir().find_parent(expr.hir_id) + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) { (macro_call.span.to(semi_span), String::new()) diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index c4437a3c4b33..59d2df0295fb 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -128,8 +128,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { }, _, ) => { - if let Some(parent) = self.cx.tcx.hir().find_parent(expr.hir_id) - && let Some(fn_sig) = parent.fn_sig() + if let Some(fn_sig) = self.cx.tcx.parent_hir_node(expr.hir_id).fn_sig() && let FnRetTy::Return(_ty) = fn_sig.decl.output { // We cannot check the exact type since it's a `hir::Ty`` which does not implement `is_numeric` diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 194cf69ea7ed..cdbb52f497b3 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1088,7 +1088,7 @@ fn report<'tcx>( // // e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a // deref through `ManuallyDrop<_>` will not compile. - let parent_id = cx.tcx.hir().parent_id(expr.hir_id); + let parent_id = cx.tcx.parent_hir_id(expr.hir_id); if parent_id == data.first_expr.hir_id { return; } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 218d7c6c01ae..064bac2e7dc7 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -131,7 +131,7 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool { _ => return false, } - matches!(tcx.hir().find_parent(id), Some(Node::Param(_))) + matches!(tcx.parent_hir_node(id), Node::Param(_)) } impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { @@ -156,8 +156,8 @@ fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { let map = &self.cx.tcx.hir(); if is_argument(self.cx.tcx, cmt.hir_id) { // Skip closure arguments - let parent_id = map.parent_id(cmt.hir_id); - if let Some(Node::Expr(..)) = map.find_parent(parent_id) { + let parent_id = self.cx.tcx.parent_hir_id(cmt.hir_id); + if let Node::Expr(..) = self.cx.tcx.parent_hir_node(parent_id) { return; } diff --git a/clippy_lints/src/functions/impl_trait_in_params.rs b/clippy_lints/src/functions/impl_trait_in_params.rs index 8fba41c0e24d..6fb38a0d6dd8 100644 --- a/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/clippy_lints/src/functions/impl_trait_in_params.rs @@ -53,7 +53,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { if let ImplItemKind::Fn(_, body_id) = impl_item.kind - && let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id()) + && let hir::Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id()) && let hir::ItemKind::Impl(impl_) = item.kind && let hir::Impl { of_trait, .. } = *impl_ && of_trait.is_none() @@ -72,7 +72,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) { if !avoid_breaking_exported_api && let TraitItemKind::Fn(_, _) = trait_item.kind - && let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id()) + && let hir::Node::Item(item) = cx.tcx.parent_hir_node(trait_item.hir_id()) // ^^ (Will always be a trait) && !item.vis_span.is_empty() // Is public && !is_in_test_function(cx.tcx, trait_item.hir_id()) diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs index 0a2fd0c663e5..80a537b9f941 100644 --- a/clippy_lints/src/ignored_unit_patterns.rs +++ b/clippy_lints/src/ignored_unit_patterns.rs @@ -41,8 +41,8 @@ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { return; } - match cx.tcx.hir().get_parent(pat.hir_id) { - Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => { + match cx.tcx.parent_hir_node(pat.hir_id) { + Node::Param(param) if matches!(cx.tcx.parent_hir_node(param.hir_id), Node::Item(_)) => { // Ignore function parameters return; }, diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 252be30c4e27..51b4f26b6d13 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -242,12 +242,8 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { } = *self; if let Some(use_info) = slice_lint_info.get_mut(&local_id) - // Check if this is even a local we're interested in - - && let map = cx.tcx.hir() - // Checking for slice indexing - && let parent_id = map.parent_id(expr.hir_id) + && let parent_id = cx.tcx.parent_hir_id(expr.hir_id) && let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr) @@ -255,11 +251,10 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { && index_value < max_suggested_slice // Make sure that this slice index is read only - && let maybe_addrof_id = map.parent_id(parent_id) - && let hir::Node::Expr(maybe_addrof_expr) = cx.tcx.hir_node(maybe_addrof_id) + && let hir::Node::Expr(maybe_addrof_expr) = cx.tcx.parent_hir_node(parent_id) && let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind { - use_info.index_use.push((index_value, map.span(parent_expr.hir_id))); + use_info.index_use.push((index_value, cx.tcx.hir().span(parent_expr.hir_id))); return; } diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs index 82a37bb4f278..b5821d909f84 100644 --- a/clippy_lints/src/iter_without_into_iter.rs +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -269,7 +269,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<' // } let span_behind_impl = cx .tcx - .def_span(cx.tcx.hir().parent_id(item.hir_id()).owner.def_id) + .def_span(cx.tcx.parent_hir_id(item.hir_id()).owner.def_id) .shrink_to_lo(); let sugg = format!( diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index f5636945f203..2b73663d229e 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -285,7 +285,7 @@ fn elision_suggestions( .iter() .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) .map(|usage| { - match cx.tcx.hir().get_parent(usage.hir_id) { + match cx.tcx.parent_hir_node(usage.hir_id) { Node::Ty(Ty { kind: TyKind::Ref(..), .. }) => { diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 5f015db2b33b..0f35514b8ad6 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -62,8 +62,7 @@ fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: if let Node::Pat(pat) = node && let PatKind::Binding(bind_ann, ..) = pat.kind && !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)) - && let parent_node = cx.tcx.hir().parent_id(hir_id) - && let Node::Local(parent_let_expr) = cx.tcx.hir_node(parent_node) + && let Node::Local(parent_let_expr) = cx.tcx.parent_hir_node(hir_id) && let Some(init) = parent_let_expr.init { match init.kind { diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index 73687fbbe54e..5cbab0ec977c 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -68,8 +68,8 @@ fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind && seg.ident.name == sym!(build_hasher) - && let Node::Stmt(local_stmt) = cx.tcx.hir().get_parent(local.hir_id) - && let Node::Block(block) = cx.tcx.hir().get_parent(local_stmt.hir_id) + && let Node::Stmt(local_stmt) = cx.tcx.parent_hir_node(local.hir_id) + && let Node::Block(block) = cx.tcx.parent_hir_node(local_stmt.hir_id) && let mut stmts = block.stmts.iter() .skip_while(|stmt| stmt.hir_id != local_stmt.hir_id) @@ -91,7 +91,7 @@ fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { // `hasher.finish()`, may be anywhere in a statement or the trailing expr of the block && let Some(path_expr) = local_used_once(cx, (maybe_finish_stmt, block.expr), hasher) - && let Node::Expr(finish_expr) = cx.tcx.hir().get_parent(path_expr.hir_id) + && let Node::Expr(finish_expr) = cx.tcx.parent_hir_node(path_expr.hir_id) && !finish_expr.span.from_expansion() && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind && seg.ident.name == sym!(finish) diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index e1768c6d9764..0bde62bd5549 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -79,9 +79,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { && let Node::Pat(_) = cx.tcx.hir_node(hir_id) { // Apply only to params or locals with annotated types - match cx.tcx.hir().find_parent(hir_id) { - Some(Node::Param(..)) => (), - Some(Node::Local(local)) => { + match cx.tcx.parent_hir_node(hir_id) { + Node::Param(..) => (), + Node::Local(local) => { let Some(ty) = local.ty else { return }; if matches!(ty.kind, TyKind::Infer) { return; diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 89da7a55cbd5..61977045fd46 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -36,7 +36,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e .to_string(); // Do we need to add ';' to suggestion ? - if let Node::Stmt(stmt) = cx.tcx.hir().get_parent(expr.hir_id) + if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) && let StmtKind::Expr(_) = stmt.kind && match match_body.kind { // We don't need to add a ; to blocks, unless that block is from a macro expansion @@ -146,18 +146,16 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e /// Returns true if the `ex` match expression is in a local (`let`) or assign expression fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { - let map = &cx.tcx.hir(); - - if let Some(Node::Expr(parent_arm_expr)) = map.find_parent(ex.hir_id) { - return match map.find_parent(parent_arm_expr.hir_id) { - Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local { + if let Node::Expr(parent_arm_expr) = cx.tcx.parent_hir_node(ex.hir_id) { + return match cx.tcx.parent_hir_node(parent_arm_expr.hir_id) { + Node::Local(parent_let_expr) => Some(AssignmentExpr::Local { span: parent_let_expr.span, pat_span: parent_let_expr.pat.span(), }), - Some(Node::Expr(Expr { + Node::Expr(Expr { kind: ExprKind::Assign(parent_assign_expr, match_expr, _), .. - })) => Some(AssignmentExpr::Assign { + }) => Some(AssignmentExpr::Assign { span: parent_assign_expr.span, match_span: match_expr.span, }), @@ -191,7 +189,7 @@ fn sugg_with_curlies<'a>( // If the parent is already an arm, and the body is another match statement, // we need curly braces around suggestion - if let Node::Arm(arm) = &cx.tcx.hir().get_parent(match_expr.hir_id) { + if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) { if let ExprKind::Match(..) = arm.body.kind { cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the match diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs index dfaaeb14ca3c..a1b82679f2e2 100644 --- a/clippy_lints/src/matches/redundant_guards.rs +++ b/clippy_lints/src/matches/redundant_guards.rs @@ -199,7 +199,7 @@ fn get_pat_binding<'tcx>( return span.map(|span| PatBindingInfo { span, byref_ident, - is_field: matches!(cx.tcx.hir().get_parent(local), Node::PatField(_)), + is_field: matches!(cx.tcx.parent_hir_node(local), Node::PatField(_)), }); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e8a7a321bf4b..1452547807ba 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4458,7 +4458,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { _ => {}, }, ("drain", ..) => { - if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.hir().get_parent(expr.hir_id) + if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.parent_hir_node(expr.hir_id) && matches!(kind, StmtKind::Semi(_)) && args.len() <= 1 { diff --git a/clippy_lints/src/methods/readonly_write_lock.rs b/clippy_lints/src/methods/readonly_write_lock.rs index 1184dd4525a7..6c6846c4b476 100644 --- a/clippy_lints/src/methods/readonly_write_lock.rs +++ b/clippy_lints/src/methods/readonly_write_lock.rs @@ -21,9 +21,9 @@ fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver: &Expr<'_>) { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::RwLock) - && let Node::Expr(unwrap_call_expr) = cx.tcx.hir().get_parent(expr.hir_id) + && let Node::Expr(unwrap_call_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_unwrap_call(cx, unwrap_call_expr) - && let parent = cx.tcx.hir().get_parent(unwrap_call_expr.hir_id) + && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) && let Node::Local(local) = parent && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id) && let Some((local, _)) = mir diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 2046692bbd0b..988f3e86fcf0 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -16,7 +16,7 @@ /// Changing `fold` to `sum` needs it sometimes when the return type can't be /// inferred. This checks for some common cases where it can be safely omitted fn needs_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - let parent = cx.tcx.hir().get_parent(expr.hir_id); + let parent = cx.tcx.parent_hir_node(expr.hir_id); // some common cases where turbofish isn't needed: // - assigned to a local variable with a type annotation diff --git a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index a1125d70db39..1b2bfbf4090e 100644 --- a/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -76,7 +76,7 @@ pub(super) fn check( (expr.span.with_lo(call_args[0].span.hi()), String::new()), ]; // try to also remove the unsafe block if present - if let hir::Node::Block(block) = cx.tcx.hir().get_parent(expr.hir_id) + if let hir::Node::Block(block) = cx.tcx.parent_hir_node(expr.hir_id) && let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules { suggs.extend([ diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index b593e48ae2e1..a1f7dc7b38c4 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -206,10 +206,9 @@ fn visit_block(&mut self, _: &'tcx Block<'_>) { /// /// When such a read is found, the lint is triggered. fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { - let map = &vis.cx.tcx.hir(); let mut cur_id = vis.write_expr.hir_id; loop { - let parent_id = map.parent_id(cur_id); + let parent_id = vis.cx.tcx.parent_hir_id(cur_id); if parent_id == cur_id { break; } diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index d2eef6ae4338..149d440ecac4 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -161,7 +161,7 @@ fn check_fn( }; // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 2c5c3dcaa752..384a402ce5b0 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -100,7 +100,7 @@ fn check_fn( } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 10ab380ba1bc..ea73d9afa2ea 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -449,7 +449,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let mut dereferenced_expr = expr; let mut needs_check_adjustment = true; loop { - let parent_id = cx.tcx.hir().parent_id(cur_expr.hir_id); + let parent_id = cx.tcx.parent_hir_id(cur_expr.hir_id); if parent_id == cur_expr.hir_id { break; } diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs index 40d4a842befb..2a933a11e12c 100644 --- a/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/clippy_lints/src/operators/modulo_arithmetic.rs @@ -34,7 +34,7 @@ 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 { + let Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id) else { return false; }; let ExprKind::Binary(op, lhs, rhs) = parent_expr.kind else { diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 57d37067e8f9..ec03ab0e41ab 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -301,7 +301,7 @@ fn check_fn( } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 513a913f56ad..bbecc39a8130 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -41,7 +41,7 @@ pub(super) fn check<'tcx>( _ => return false, }; - if let Node::Expr(parent) = cx.tcx.hir().get_parent(e.hir_id) + if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id) && parent.precedence().order() > ExprPrecedence::Cast.order() { sugg = format!("({sugg})"); diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index e1cd82e18d56..c11504cd2d4f 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -153,13 +153,10 @@ fn all_bindings_are_for_conv<'tcx>( let Some(locals) = locals.iter().map(|e| path_to_local(e)).collect::>>() else { return false; }; - let Some(local_parents) = locals + let local_parents = locals .iter() - .map(|&l| cx.tcx.hir().find_parent(l)) - .collect::>>() - else { - return false; - }; + .map(|l| cx.tcx.parent_hir_node(*l)) + .collect::>(); local_parents .iter() diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index 44cff78a7936..eba7fa7b993c 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -19,9 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if is_questionmark_desugar_marked_call(expr) { return; } - let map = &cx.tcx.hir(); - let opt_parent_node = map.find_parent(expr.hir_id); - if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node + if let hir::Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_questionmark_desugar_marked_call(parent_expr) { return; @@ -183,8 +181,8 @@ fn fmt_stmts_and_call( let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); // expr is not in a block statement or result expression position, wrap in a block - let parent_node = cx.tcx.hir().find_parent(call_expr.hir_id); - if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + let parent_node = cx.tcx.parent_hir_node(call_expr.hir_id); + if !matches!(parent_node, Node::Block(_)) && !matches!(parent_node, Node::Stmt(_)) { let block_indent = call_expr_indent + 4; stmts_and_call_snippet = reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned(); diff --git a/clippy_lints/src/unnecessary_box_returns.rs b/clippy_lints/src/unnecessary_box_returns.rs index f5af540fa148..c332cf076ae7 100644 --- a/clippy_lints/src/unnecessary_box_returns.rs +++ b/clippy_lints/src/unnecessary_box_returns.rs @@ -116,7 +116,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) { // Ignore implementations of traits, because the lint should be on the // trait, not on the implementation of it. - let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { + let Node::Item(parent) = cx.tcx.parent_hir_node(item.hir_id()) else { return; }; let ItemKind::Impl(parent) = parent.kind else { return }; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 446160f8e0fd..9c8b0ae17276 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -92,7 +92,7 @@ fn check_fn( // Abort if the method is implementing a trait or of it a trait method. let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 1d42375ba8e5..738fba54fa83 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -156,7 +156,7 @@ fn is_node_func_call(node: Node<'_>, expected_receiver: Span) -> bool { && let Some(local_def_id) = def_id.as_local() && cx.tcx.def_kind(def_id) == DefKind::Fn && cx.tcx.asyncness(def_id).is_async() - && !is_node_func_call(cx.tcx.hir().get_parent(hir_id), path.span) + && !is_node_func_call(cx.tcx.parent_hir_node(hir_id), path.span) { self.async_fns_as_value.insert(local_def_id); } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index ae2ac38cffe1..f2eb774b5cbf 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -208,7 +208,7 @@ struct MutationVisitor<'tcx> { /// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent /// expression: that will be where the actual method call is. fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { - if let Node::Expr(mutating_expr) = tcx.hir().get_parent(expr_id) + if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id) && let ExprKind::MethodCall(path, ..) = mutating_expr.kind { path.ident.name.as_str() == "as_mut" diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index fae1b90ace21..349e0e3e077a 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1007,9 +1007,9 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) - fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { let map = cx.tcx.hir(); - match map.find_parent(hir_id) { - Some(hir::Node::Local(local)) => Some(local), - Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id), + match cx.tcx.parent_hir_node(hir_id) { + hir::Node::Local(local) => Some(local), + hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id), _ => None, } } diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 6e449dc98063..38c832931fc6 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -217,8 +217,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { - let parent_id = cx.tcx.hir().parent_id(hir_id); - if let Node::Local(Local { init: Some(init), .. }) = cx.tcx.hir_node(parent_id) { + if let Node::Local(Local { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { path_to_matched_type(cx, init) } else { None diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 2c33c93412a3..b3489142558e 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -74,7 +74,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) { // search for `let foo = vec![_]` expressions where all uses of `foo` // adjust to slices or call a method that exist on slices (e.g. len) - if let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id) + if let Node::Local(local) = cx.tcx.parent_hir_node(expr.hir_id) // for now ignore locals with type annotations. // this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..]; && local.ty.is_none() @@ -103,7 +103,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } // if the local pattern has a specified type, do not lint. else if let Some(_) = higher::VecArgs::hir(cx, expr) - && let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id) + && let Node::Local(local) = cx.tcx.parent_hir_node(expr.hir_id) && local.ty.is_some() { let span = expr.span.ctxt().outer_expn_data().call_site; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6a33e11be465..67a18caca0e2 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -182,11 +182,9 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr /// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the /// canonical binding `HirId`. pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { - let hir = cx.tcx.hir(); if let Node::Pat(pat) = cx.tcx.hir_node(hir_id) && matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)) - && let parent = hir.parent_id(hir_id) - && let Node::Local(local) = cx.tcx.hir_node(parent) + && let Node::Local(local) = cx.tcx.parent_hir_node(hir_id) { return local.init; } @@ -333,7 +331,7 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) /// Checks if the `def_id` belongs to a function that is part of a trait impl. pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { if let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(def_id) - && let Node::Item(item) = cx.tcx.hir().get_parent(hir_id) + && let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) && let ItemKind::Impl(imp) = item.kind { imp.of_trait.is_some() @@ -1311,7 +1309,7 @@ pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool { /// Gets the parent node, if any. pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { - tcx.hir().find_parent(id) + Some(tcx.parent_hir_node(id)) } /// Gets the parent expression, if any –- this is useful to constrain a lint. @@ -2227,7 +2225,7 @@ pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { /// } /// ``` pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { - if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { + if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) { matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) } else { false diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index adca2ca1c3ef..7913926928f2 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -237,7 +237,7 @@ fn path_segment_certainty( }, // `get_parent` because `hir_id` refers to a `Pat`, and we're interested in the node containing the `Pat`. - Res::Local(hir_id) => match cx.tcx.hir().get_parent(hir_id) { + Res::Local(hir_id) => match cx.tcx.parent_hir_node(hir_id) { // An argument's type is always certain. Node::Param(..) => Certainty::Certain(None), // A local's type is certain if its type annotation is certain or it has an initializer whose From 4fbd5cc31fad26f5cb439ece09e634310c6868ae Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 11 Feb 2024 21:36:12 +0000 Subject: [PATCH 09/26] is_closure_like --- clippy_lints/src/redundant_locals.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/redundant_locals.rs b/clippy_lints/src/redundant_locals.rs index 700a5dd4a851..6528a7b369f7 100644 --- a/clippy_lints/src/redundant_locals.rs +++ b/clippy_lints/src/redundant_locals.rs @@ -101,7 +101,7 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { 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.is_closure_like(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) From 05101ffe5b20b404daebc34634566b948aa5d6b2 Mon Sep 17 00:00:00 2001 From: Frank King Date: Thu, 4 Jan 2024 21:53:06 +0800 Subject: [PATCH 10/26] Lower anonymous structs or unions to HIR --- clippy_lints/src/dereference.rs | 1 + clippy_utils/src/hir_utils.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index cdbb52f497b3..0ddfeaa0ae05 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -831,6 +831,7 @@ fn for_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Self { | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::InferDelegation(..) + | TyKind::AnonAdt(..) | TyKind::Err(_) => Self::Reborrow, }; } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 4fa93ad23c36..d50332e82da5 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -515,6 +515,7 @@ pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), (&TyKind::Infer, &TyKind::Infer) => true, + (TyKind::AnonAdt(l_item_id), TyKind::AnonAdt(r_item_id)) => l_item_id == r_item_id, _ => false, } } @@ -1108,7 +1109,7 @@ pub fn hash_tykind(&mut self, ty: &TyKind<'_>) { TyKind::Typeof(anon_const) => { self.hash_body(anon_const.body); }, - TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) => {}, + TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) | TyKind::AnonAdt(_) => {}, } } From 6fda300087f664f23bc19364dfaeea061ecd5a63 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 12 Feb 2024 15:26:59 +1100 Subject: [PATCH 11/26] Tweak delayed bug mentions. Now that we have both `delayed_bug` and `span_delayed_bug`, it makes sense to use the generic term "delayed bug" more. --- tests/integration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration.rs b/tests/integration.rs index 267f095f9c20..7f4500826ff6 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -77,7 +77,7 @@ fn integration_test() { // the repo basically just contains a span_delayed_bug that forces rustc/clippy to panic: /* #![feature(rustc_attrs)] - #[rustc_error(span_delayed_bug_from_inside_query)] + #[rustc_error(delayed_bug_from_inside_query)] fn main() {} */ From d55fdb25ce982f13496df04bd64c960687b43337 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 30 Jan 2024 14:20:22 +0000 Subject: [PATCH 12/26] Make `is_intrinsic` query return the intrinsic name --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 8d5bcd665ad2..47195fcc17b4 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -335,7 +335,7 @@ fn check_terminator<'tcx>( // within const fns. `transmute` is allowed in all other const contexts. // This won't really scale to more intrinsics or functions. Let's allow const // transmutes in const fn before we add more hacks to this. - if tcx.is_intrinsic(fn_def_id) && tcx.item_name(fn_def_id) == sym::transmute { + if matches!(tcx.intrinsic(fn_def_id), Some(sym::transmute)) { return Err(( span, "can only call `transmute` from const items, not `const fn`".into(), From f2064f71653c280b02c7e08b308acac42f4d0d6c Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 17 Dec 2023 17:28:24 +0100 Subject: [PATCH 13/26] Avoid UB in clippy transmute_ptr_to_ptr UI test --- tests/ui/transmute_ptr_to_ptr.fixed | 6 +++--- tests/ui/transmute_ptr_to_ptr.rs | 6 +++--- tests/ui/transmute_ptr_to_ptr.stderr | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/ui/transmute_ptr_to_ptr.fixed b/tests/ui/transmute_ptr_to_ptr.fixed index 4e145693c553..696def08f142 100644 --- a/tests/ui/transmute_ptr_to_ptr.fixed +++ b/tests/ui/transmute_ptr_to_ptr.fixed @@ -35,7 +35,7 @@ fn transmute_ptr_to_ptr() { // ref-ref transmutes; bad let _: &f32 = &*(&1u32 as *const u32 as *const f32); //~^ ERROR: transmute from a reference to a reference - let _: &f64 = &*(&1f32 as *const f32 as *const f64); + let _: &f32 = &*(&1f64 as *const f64 as *const f32); //~^ ERROR: transmute from a reference to a reference //:^ this test is here because both f32 and f64 are the same TypeVariant, but they are not // the same type @@ -43,8 +43,8 @@ fn transmute_ptr_to_ptr() { //~^ ERROR: transmute from a reference to a reference let _: &GenericParam = &*(&GenericParam { t: 1u32 } as *const GenericParam as *const GenericParam); //~^ ERROR: transmute from a reference to a reference - let u8_ref: &u8 = &0u8; - let u64_ref: &u64 = unsafe { &*(u8_ref as *const u8 as *const u64) }; + let u64_ref: &u64 = &0u64; + let u8_ref: &u8 = unsafe { &*(u64_ref as *const u64 as *const u8) }; //~^ ERROR: transmute from a reference to a reference } diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 086aadc36474..0700d8c19576 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -35,7 +35,7 @@ fn transmute_ptr_to_ptr() { // ref-ref transmutes; bad let _: &f32 = std::mem::transmute(&1u32); //~^ ERROR: transmute from a reference to a reference - let _: &f64 = std::mem::transmute(&1f32); + let _: &f32 = std::mem::transmute(&1f64); //~^ ERROR: transmute from a reference to a reference //:^ this test is here because both f32 and f64 are the same TypeVariant, but they are not // the same type @@ -43,8 +43,8 @@ fn transmute_ptr_to_ptr() { //~^ ERROR: transmute from a reference to a reference let _: &GenericParam = std::mem::transmute(&GenericParam { t: 1u32 }); //~^ ERROR: transmute from a reference to a reference - let u8_ref: &u8 = &0u8; - let u64_ref: &u64 = unsafe { std::mem::transmute(u8_ref) }; + let u64_ref: &u64 = &0u64; + let u8_ref: &u8 = unsafe { std::mem::transmute(u64_ref) }; //~^ ERROR: transmute from a reference to a reference } diff --git a/tests/ui/transmute_ptr_to_ptr.stderr b/tests/ui/transmute_ptr_to_ptr.stderr index 9f8599921ec7..6e3af1f73371 100644 --- a/tests/ui/transmute_ptr_to_ptr.stderr +++ b/tests/ui/transmute_ptr_to_ptr.stderr @@ -22,8 +22,8 @@ LL | let _: &f32 = std::mem::transmute(&1u32); error: transmute from a reference to a reference --> $DIR/transmute_ptr_to_ptr.rs:38:23 | -LL | let _: &f64 = std::mem::transmute(&1f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)` +LL | let _: &f32 = std::mem::transmute(&1f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f64 as *const f64 as *const f32)` error: transmute from a reference to a reference --> $DIR/transmute_ptr_to_ptr.rs:42:27 @@ -38,10 +38,10 @@ LL | let _: &GenericParam = std::mem::transmute(&GenericParam { t: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam as *const GenericParam)` error: transmute from a reference to a reference - --> $DIR/transmute_ptr_to_ptr.rs:47:38 + --> $DIR/transmute_ptr_to_ptr.rs:47:36 | -LL | let u64_ref: &u64 = unsafe { std::mem::transmute(u8_ref) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(u8_ref as *const u8 as *const u64)` +LL | let u8_ref: &u8 = unsafe { std::mem::transmute(u64_ref) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)` error: aborting due to 7 previous errors From b3b9f31a869326bcd623c84fd230777f397847f3 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sun, 28 Jan 2024 20:53:28 +0000 Subject: [PATCH 14/26] Bump `indexmap` `swap` has been deprecated in favour of `swap_remove` - the behaviour is the same though. --- clippy_lints/src/copies.rs | 3 ++- clippy_lints/src/escape.rs | 6 ++++-- clippy_lints/src/index_refutable_slice.rs | 6 ++++-- clippy_lints/src/matches/match_same_arms.rs | 3 ++- clippy_lints/src/needless_pass_by_ref_mut.rs | 3 ++- clippy_lints/src/no_effect.rs | 6 ++++-- clippy_lints/src/only_used_in_recursion.rs | 3 ++- 7 files changed, 20 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index bd07c19a2d81..247048bbc49d 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -511,7 +511,8 @@ fn scan_block_for_eq<'tcx>( for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { if let StmtKind::Local(l) = stmt.kind { l.pat.each_binding_or_first(&mut |_, id, _, _| { - eq.locals.remove(&id); + // FIXME(rust/#120456) - is `swap_remove` correct? + eq.locals.swap_remove(&id); }); } } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 064bac2e7dc7..8857cb8e3827 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -138,7 +138,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { if cmt.place.projections.is_empty() { if let PlaceBase::Local(lid) = cmt.place.base { - self.set.remove(&lid); + // FIXME(rust/#120456) - is `swap_remove` correct? + self.set.swap_remove(&lid); } } } @@ -146,7 +147,8 @@ fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { if cmt.place.projections.is_empty() { if let PlaceBase::Local(lid) = cmt.place.base { - self.set.remove(&lid); + // FIXME(rust/#120456) - is `swap_remove` correct? + self.set.swap_remove(&lid); } } } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 51b4f26b6d13..41e9d5b1c2e9 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -106,7 +106,8 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap) { } // The slice was used for something other than indexing - self.slice_lint_info.remove(&local_id); + // FIXME(rust/#120456) - is `swap_remove` correct? + self.slice_lint_info.swap_remove(&local_id); } intravisit::walk_expr(self, expr); } diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index d645e6c6c05c..6595658b7692 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -415,6 +415,7 @@ fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool { /// Returns true if all the bindings in the `Pat` are in `ids` and vice versa fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool { let mut result = true; - pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id)); + // FIXME(rust/#120456) - is `swap_remove` correct? + pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.swap_remove(&id)); result && ids.is_empty() } diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 149d440ecac4..710ecd745f8d 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -382,7 +382,8 @@ fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, id: HirId) { self.add_mutably_used_var(*vid); } self.prev_bind = None; - self.prev_move_to_closure.remove(vid); + // FIXME(rust/#120456) - is `swap_remove` correct? + self.prev_move_to_closure.swap_remove(vid); } } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 580160efeb70..6a5555ca3836 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -98,7 +98,8 @@ fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx> 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) { + // FIXME(rust/#120456) - is `swap_remove` correct? + if let Some(span) = self.underscore_bindings.swap_remove(&hir_id) { span_lint_hir( cx, NO_EFFECT_UNDERSCORE_BINDING, @@ -112,7 +113,8 @@ fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block 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); + // FIXME(rust/#120456) - is `swap_remove` correct? + self.underscore_bindings.swap_remove(&def_id); } } } diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index ae14016f4823..a568392ecc45 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -153,7 +153,8 @@ fn remove_by_id(&mut self, id: HirId) { param.uses = Vec::new(); let key = (param.fn_id, param.idx); self.by_fn.remove(&key); - self.by_id.remove(&id); + // FIXME(rust/#120456) - is `swap_remove` correct? + self.by_id.swap_remove(&id); } } From ad7914f2e1122e12b82993c180043fe0d734c62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 14 Feb 2024 19:18:28 +0000 Subject: [PATCH 15/26] Fix msg for verbose suggestions with confusable capitalization When encountering a verbose/multipart suggestion that has changes that are only caused by different capitalization of ASCII letters that have little differenciation, expand the message to highlight that fact (like we already do for inline suggestions). The logic to do this was already present, but implemented incorrectly. --- tests/ui/match_str_case_mismatch.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/match_str_case_mismatch.stderr b/tests/ui/match_str_case_mismatch.stderr index f799a4698b94..b178fb7512cd 100644 --- a/tests/ui/match_str_case_mismatch.stderr +++ b/tests/ui/match_str_case_mismatch.stderr @@ -17,7 +17,7 @@ error: this `match` arm has a differing case than its expression LL | "~!@#$%^&*()-_=+Foo" => {}, | ^^^^^^^^^^^^^^^^^^^^ | -help: consider changing the case of this arm to respect `to_ascii_lowercase` +help: consider changing the case of this arm to respect `to_ascii_lowercase` (notice the capitalization difference) | LL | "~!@#$%^&*()-_=+foo" => {}, | ~~~~~~~~~~~~~~~~~~~~ From d2aa1beed775e09a7f79f3a7761feece92572bf1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 14 Feb 2024 14:50:49 +1100 Subject: [PATCH 16/26] Add an `ErrorGuaranteed` to `ast::TyKind::Err`. This makes it more like `hir::TyKind::Err`, and avoids a `span_delayed_bug` call in `LoweringContext::lower_ty_direct`. It also requires adding `ast::TyKind::Dummy`, now that `ast::TyKind::Err` can't be used for that purpose in the absence of an error emission. There are a couple of cases that aren't as neat as I would have liked, marked with `FIXME` comments. --- clippy_utils/src/ast_utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index adc35bd82ae3..0467a8a65709 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -690,7 +690,7 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool { match (&l.kind, &r.kind) { (Paren(l), _) => eq_ty(l, r), (_, Paren(r)) => eq_ty(l, r), - (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err, Err) | (CVarArgs, CVarArgs) => true, + (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err(_), Err(_)) | (CVarArgs, CVarArgs) => true, (Slice(l), Slice(r)) => eq_ty(l, r), (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value), (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty), From 33603a6d80f090ad6ade21c7e9a369aba47b9f46 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 14 Feb 2024 20:12:05 +1100 Subject: [PATCH 17/26] Add `ErrorGuaranteed` to `ast::LitKind::Err`, `token::LitKind::Err`. This mostly works well, and eliminates a couple of delayed bugs. One annoying thing is that we should really also add an `ErrorGuaranteed` to `proc_macro::bridge::LitKind::Err`. But that's difficult because `proc_macro` doesn't have access to `ErrorGuaranteed`, so we have to fake it. --- clippy_lints/src/matches/match_same_arms.rs | 3 ++- clippy_lints/src/redundant_type_annotations.rs | 2 +- clippy_lints/src/utils/author.rs | 2 +- clippy_utils/src/consts.rs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 6595658b7692..b8f7d9668201 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -295,7 +295,8 @@ fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> LitKind::Char(val) => Self::LitInt(val.into()), LitKind::Int(val, _) => Self::LitInt(val.get()), LitKind::Bool(val) => Self::LitBool(val), - LitKind::Float(..) | LitKind::Err => Self::Wild, + LitKind::Float(..) => Self::Wild, + LitKind::Err(guar) => Self::Err(guar), }, _ => Self::Wild, }, diff --git a/clippy_lints/src/redundant_type_annotations.rs b/clippy_lints/src/redundant_type_annotations.rs index c8352c052659..079e6500e3cf 100644 --- a/clippy_lints/src/redundant_type_annotations.rs +++ b/clippy_lints/src/redundant_type_annotations.rs @@ -200,7 +200,7 @@ fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir:: span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation"); } }, - LitKind::Err => (), + 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 diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 29c67341a467..a0a6382046d0 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -279,7 +279,7 @@ macro_rules! kind { match lit.value.node { LitKind::Bool(val) => kind!("Bool({val:?})"), LitKind::Char(c) => kind!("Char({c:?})"), - LitKind::Err => kind!("Err"), + LitKind::Err(_) => kind!("Err"), LitKind::Byte(b) => kind!("Byte({b})"), LitKind::Int(i, suffix) => { let int_ty = match suffix { diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 61b38391d9e0..79c691992a85 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -286,7 +286,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan _ => bug!(), }, LitKind::Bool(b) => Constant::Bool(b), - LitKind::Err => Constant::Err, + LitKind::Err(_) => Constant::Err, } } From 1ac4d934a8fb0541fb2ae089e7c8d068fe622f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 17 Feb 2024 12:46:18 +0100 Subject: [PATCH 18/26] remove a couple of redundant clones --- clippy_utils/src/diagnostics.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 56978eb2ee80..5199959c0f2b 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -84,9 +84,9 @@ pub fn span_lint_and_help( cx.span_lint(lint, span, msg.to_string(), |diag| { let help = help.to_string(); if let Some(help_span) = help_span { - diag.span_help(help_span, help.to_string()); + diag.span_help(help_span, help); } else { - diag.help(help.to_string()); + diag.help(help); } docs_link(diag, lint); }); From d88ad9e6b7b07d177c28bf7c65c3bf44759338b4 Mon Sep 17 00:00:00 2001 From: surechen Date: Fri, 10 Nov 2023 10:11:24 +0800 Subject: [PATCH 19/26] By tracking import use types to check whether it is scope uses or the other situations like module-relative uses, we can do more accurate redundant import checking. fixes #117448 For example unnecessary imports in std::prelude that can be eliminated: ```rust use std::option::Option::Some;//~ WARNING the item `Some` is imported redundantly use std::option::Option::None; //~ WARNING the item `None` is imported redundantly ``` --- src/driver.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index bdf1cf0a2240..85867d1511d9 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -28,7 +28,6 @@ use std::ops::Deref; use std::path::Path; use std::process::exit; -use std::string::ToString; use anstream::println; From a16dbd7339b63ed3b6660117dfa51696712cd1b5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 Feb 2024 10:13:24 +1100 Subject: [PATCH 20/26] Prefer `DiagnosticBuilder` over `Diagnostic` in diagnostic modifiers. There are lots of functions that modify a diagnostic. This can be via a `&mut Diagnostic` or a `&mut DiagnosticBuilder`, because the latter type wraps the former and impls `DerefMut`. This commit converts all the `&mut Diagnostic` occurrences to `&mut DiagnosticBuilder`. This is a step towards greatly simplifying `Diagnostic`. Some of the relevant function are made generic, because they deal with both errors and warnings. No function bodies are changed, because all the modifier methods are available on both `Diagnostic` and `DiagnosticBuilder`. --- clippy_lints/src/casts/cast_possible_truncation.rs | 4 ++-- clippy_lints/src/functions/result.rs | 4 ++-- clippy_lints/src/if_let_mutex.rs | 4 ++-- clippy_lints/src/implicit_hasher.rs | 4 ++-- clippy_lints/src/manual_clamp.rs | 4 ++-- .../src/matches/significant_drop_in_scrutinee.rs | 4 ++-- .../src/methods/suspicious_command_arg_space.rs | 4 ++-- clippy_lints/src/missing_asserts_for_indexing.rs | 4 ++-- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- clippy_utils/src/diagnostics.rs | 12 ++++++------ 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index f99a51e2b88c..1543ae803996 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; -use rustc_errors::{Applicability, Diagnostic, SuggestionStyle}; +use rustc_errors::{Applicability, DiagnosticBuilder, SuggestionStyle}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -177,7 +177,7 @@ fn offer_suggestion( expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to_span: Span, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_, ()>, ) { let cast_to_snip = snippet(cx, cast_to_span, ".."); let suggestion = if cast_to_snip == "_" { diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index f1200c2edc13..9505741e68ff 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -1,4 +1,4 @@ -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -135,7 +135,7 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty RESULT_LARGE_ERR, hir_ty_span, "the `Err`-variant returned from this function is very large", - |diag: &mut Diagnostic| { + |diag: &mut DiagnosticBuilder<'_, ()>| { diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); }, diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 5e354209cbfc..61a322ea8812 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{higher, SpanlessEq}; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_hir::intravisit::{self as visit, Visitor}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -59,7 +59,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { arm_visit.visit_expr(if_else); if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) { - let diag = |diag: &mut Diagnostic| { + let diag = |diag: &mut DiagnosticBuilder<'_, ()>| { diag.span_label( op_mutex.span, "this Mutex will remain locked for the entire `if let`-block...", diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 87f6f5e7959e..746de50c0fa7 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; @@ -65,7 +65,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn suggestion( cx: &LateContext<'_>, - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_, ()>, generics_span: Span, generics_suggestion_span: Span, target: &ImplicitHasherType<'_>, diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 0da309f9531e..12bb80dfde2c 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -9,7 +9,7 @@ peel_blocks_with_stmt, MaybePath, }; use itertools::Itertools; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::def::Res; use rustc_hir::{Arm, BinOpKind, Block, Expr, ExprKind, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -163,7 +163,7 @@ fn emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'t }; let suggestion = format!("{assignment}{input}.clamp({min}, {max}){semicolon}"); let msg = "clamp-like pattern without using clamp function"; - let lint_builder = |d: &mut Diagnostic| { + let lint_builder = |d: &mut DiagnosticBuilder<'_, ()>| { d.span_suggestion(*span, "replace with clamp", suggestion, Applicability::MaybeIncorrect); if *is_float { d.note("clamp will panic if max < min, min.is_nan(), or max.is_nan()") diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index ee0fdb35313d..a7e42fd24059 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{indent_of, snippet}; use clippy_utils::{get_attr, is_lint_allowed}; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LintContext}; @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( } } -fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { +fn set_diagnostic<'tcx>(diag: &mut DiagnosticBuilder<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { if found.lint_suggestion == LintSuggestion::MoveAndClone { // If our suggestion is to move and clone, then we want to leave it to the user to // decide how to address this lint, since it may be that cloning is inappropriate. diff --git a/clippy_lints/src/methods/suspicious_command_arg_space.rs b/clippy_lints/src/methods/suspicious_command_arg_space.rs index b2c5987e43d2..617d6d998fcc 100644 --- a/clippy_lints/src/methods/suspicious_command_arg_space.rs +++ b/clippy_lints/src/methods/suspicious_command_arg_space.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_lint::LateContext; use rustc_span::{sym, Span}; use {rustc_ast as ast, rustc_hir as hir}; @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg SUSPICIOUS_COMMAND_ARG_SPACE, arg.span, "single argument that looks like it should be multiple arguments", - |diag: &mut Diagnostic| { + |diag: &mut DiagnosticBuilder<'_, ()>| { diag.multipart_suggestion_verbose( "consider splitting the argument", vec![(span, "args".to_string()), (arg.span, format!("[{arg1:?}, {arg2:?}]"))], diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index 0e4d39c99902..ab25dde7efee 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -9,7 +9,7 @@ use rustc_ast::{LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -67,7 +67,7 @@ fn report_lint(cx: &LateContext<'_>, full_span: Span, msg: &str, indexes: &[Span], f: F) where - F: FnOnce(&mut Diagnostic), + F: FnOnce(&mut DiagnosticBuilder<'_, ()>), { span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, full_span, msg, |diag| { f(diag); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 384a402ce5b0..6252f91b25f8 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -6,7 +6,7 @@ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; use rustc_ast::ast::Attribute; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind, @@ -196,7 +196,7 @@ fn check_fn( && !moved_vars.contains(&canonical_id) { // Dereference suggestion - let sugg = |diag: &mut Diagnostic| { + let sugg = |diag: &mut DiagnosticBuilder<'_, ()>| { if let ty::Adt(def, ..) = ty.kind() { if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { if type_allowed_to_implement_copy( diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 5199959c0f2b..db94b60dc951 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -8,13 +8,13 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{Applicability, Diagnostic, MultiSpan}; +use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan}; use rustc_hir::HirId; use rustc_lint::{LateContext, Lint, LintContext}; use rustc_span::Span; use std::env; -fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { +fn docs_link(diag: &mut DiagnosticBuilder<'_, ()>, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { diag.help(format!( @@ -143,7 +143,7 @@ pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: &str where C: LintContext, S: Into, - F: FnOnce(&mut Diagnostic), + F: FnOnce(&mut DiagnosticBuilder<'_, ()>), { #[expect(clippy::disallowed_methods)] cx.span_lint(lint, sp, msg.to_string(), |diag| { @@ -165,7 +165,7 @@ pub fn span_lint_hir_and_then( hir_id: HirId, sp: impl Into, msg: &str, - f: impl FnOnce(&mut Diagnostic), + f: impl FnOnce(&mut DiagnosticBuilder<'_, ()>), ) { #[expect(clippy::disallowed_methods)] cx.tcx.node_span_lint(lint, hir_id, sp, msg.to_string(), |diag| { @@ -214,7 +214,7 @@ pub fn span_lint_and_sugg( /// appear once per /// replacement. In human-readable format though, it only appears once before /// the whole suggestion. -pub fn multispan_sugg(diag: &mut Diagnostic, help_msg: &str, sugg: I) +pub fn multispan_sugg(diag: &mut DiagnosticBuilder<'_, ()>, help_msg: &str, sugg: I) where I: IntoIterator, { @@ -227,7 +227,7 @@ pub fn multispan_sugg(diag: &mut Diagnostic, help_msg: &str, sugg: I) /// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141). /// Suggestions with multiple spans will be silently ignored. pub fn multispan_sugg_with_applicability( - diag: &mut Diagnostic, + diag: &mut DiagnosticBuilder<'_, ()>, help_msg: &str, applicability: Applicability, sugg: I, From 16d5a2be3d938cc3a8263a88fe2229758115dbb2 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 19 Feb 2024 17:39:25 -0300 Subject: [PATCH 21/26] Remove suspicious auto trait lint --- tests/ui/non_send_fields_in_send_ty.rs | 1 - tests/ui/non_send_fields_in_send_ty.stderr | 52 +++++++++++----------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/tests/ui/non_send_fields_in_send_ty.rs b/tests/ui/non_send_fields_in_send_ty.rs index c6855a096968..046ea70b08f1 100644 --- a/tests/ui/non_send_fields_in_send_ty.rs +++ b/tests/ui/non_send_fields_in_send_ty.rs @@ -1,5 +1,4 @@ #![warn(clippy::non_send_fields_in_send_ty)] -#![allow(suspicious_auto_trait_impls)] #![feature(extern_types)] use std::cell::UnsafeCell; diff --git a/tests/ui/non_send_fields_in_send_ty.stderr b/tests/ui/non_send_fields_in_send_ty.stderr index 1ea76196af93..99fd4ea60b60 100644 --- a/tests/ui/non_send_fields_in_send_ty.stderr +++ b/tests/ui/non_send_fields_in_send_ty.stderr @@ -1,11 +1,11 @@ error: some fields in `RingBuffer` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:17:1 + --> $DIR/non_send_fields_in_send_ty.rs:16:1 | LL | unsafe impl Send for RingBuffer {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `data` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:12:5 + --> $DIR/non_send_fields_in_send_ty.rs:11:5 | LL | data: Vec>, | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -14,155 +14,155 @@ LL | data: Vec>, = help: to override `-D warnings` add `#[allow(clippy::non_send_fields_in_send_ty)]` error: some fields in `MvccRwLock` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:26:1 + --> $DIR/non_send_fields_in_send_ty.rs:25:1 | LL | unsafe impl Send for MvccRwLock {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `lock` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:23:5 + --> $DIR/non_send_fields_in_send_ty.rs:22:5 | LL | lock: Mutex>, | ^^^^^^^^^^^^^^^^^^^ = help: add bounds on type parameter `T` that satisfy `Mutex>: Send` error: some fields in `ArcGuard` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:35:1 + --> $DIR/non_send_fields_in_send_ty.rs:34:1 | LL | unsafe impl Send for ArcGuard {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `head` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:32:5 + --> $DIR/non_send_fields_in_send_ty.rs:31:5 | LL | head: Arc, | ^^^^^^^^^^^^^ = help: add bounds on type parameter `RC` that satisfy `Arc: Send` error: some fields in `DeviceHandle` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:52:1 + --> $DIR/non_send_fields_in_send_ty.rs:51:1 | LL | unsafe impl Send for DeviceHandle {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `context` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:48:5 + --> $DIR/non_send_fields_in_send_ty.rs:47:5 | LL | context: T, | ^^^^^^^^^^ = help: add `T: Send` bound in `Send` impl error: some fields in `NoGeneric` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:60:1 + --> $DIR/non_send_fields_in_send_ty.rs:59:1 | LL | unsafe impl Send for NoGeneric {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `rc_is_not_send` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:57:5 + --> $DIR/non_send_fields_in_send_ty.rs:56:5 | LL | rc_is_not_send: Rc, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use a thread-safe type that implements `Send` error: some fields in `MultiField` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:69:1 + --> $DIR/non_send_fields_in_send_ty.rs:68:1 | LL | unsafe impl Send for MultiField {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `field1` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:64:5 + --> $DIR/non_send_fields_in_send_ty.rs:63:5 | LL | field1: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl note: it is not safe to send field `field2` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:65:5 + --> $DIR/non_send_fields_in_send_ty.rs:64:5 | LL | field2: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl note: it is not safe to send field `field3` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:66:5 + --> $DIR/non_send_fields_in_send_ty.rs:65:5 | LL | field3: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl error: some fields in `MyOption` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:77:1 + --> $DIR/non_send_fields_in_send_ty.rs:76:1 | LL | unsafe impl Send for MyOption {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `0` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:73:12 + --> $DIR/non_send_fields_in_send_ty.rs:72:12 | LL | MySome(T), | ^ = help: add `T: Send` bound in `Send` impl error: some fields in `MultiParam` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:90:1 + --> $DIR/non_send_fields_in_send_ty.rs:89:1 | LL | unsafe impl Send for MultiParam {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `vec` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:87:5 + --> $DIR/non_send_fields_in_send_ty.rs:86:5 | LL | vec: Vec<(A, B)>, | ^^^^^^^^^^^^^^^^ = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send` error: some fields in `HeuristicTest` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:109:1 + --> $DIR/non_send_fields_in_send_ty.rs:108:1 | LL | unsafe impl Send for HeuristicTest {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `field4` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:104:5 + --> $DIR/non_send_fields_in_send_ty.rs:103:5 | LL | field4: (*const NonSend, Rc), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use a thread-safe type that implements `Send` error: some fields in `AttrTest3` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:129:1 + --> $DIR/non_send_fields_in_send_ty.rs:128:1 | LL | unsafe impl Send for AttrTest3 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `0` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:124:11 + --> $DIR/non_send_fields_in_send_ty.rs:123:11 | LL | Enum2(T), | ^ = help: add `T: Send` bound in `Send` impl error: some fields in `Complex` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:138:1 + --> $DIR/non_send_fields_in_send_ty.rs:137:1 | LL | unsafe impl

Send for Complex {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `field1` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:134:5 + --> $DIR/non_send_fields_in_send_ty.rs:133:5 | LL | field1: A, | ^^^^^^^^^ = help: add `P: Send` bound in `Send` impl error: some fields in `Complex>` are not safe to be sent to another thread - --> $DIR/non_send_fields_in_send_ty.rs:142:1 + --> $DIR/non_send_fields_in_send_ty.rs:141:1 | LL | unsafe impl Send for Complex> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: it is not safe to send field `field2` to another thread - --> $DIR/non_send_fields_in_send_ty.rs:135:5 + --> $DIR/non_send_fields_in_send_ty.rs:134:5 | LL | field2: B, | ^^^^^^^^^ From d136b05c1be89db5bce650b6ef5e3ae3f521bf86 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 14 Feb 2024 12:28:07 +0000 Subject: [PATCH 22/26] Always evaluate free constants and statics, even if previous errors occurred --- tests/ui-toml/suppress_lint_in_const/test.rs | 5 -- .../suppress_lint_in_const/test.stderr | 33 +++---------- tests/ui/indexing_slicing_index.rs | 2 - tests/ui/indexing_slicing_index.stderr | 47 +++++++------------ 4 files changed, 23 insertions(+), 64 deletions(-) diff --git a/tests/ui-toml/suppress_lint_in_const/test.rs b/tests/ui-toml/suppress_lint_in_const/test.rs index 3edb3a10b769..4ae75544c60c 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.rs +++ b/tests/ui-toml/suppress_lint_in_const/test.rs @@ -13,8 +13,6 @@ const ARR: [i32; 2] = [1, 2]; const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. -const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. -//~^ ERROR: failed const fn idx() -> usize { 1 @@ -35,9 +33,6 @@ fn main() { x[const { idx() }]; // Ok, should not produce stderr. x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. const { &ARR[idx()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. - const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. - // - //~^^ ERROR: failed let y = &x; y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr index 84e7eff45573..d5ce891b6805 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.stderr +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -1,17 +1,5 @@ -error[E0080]: evaluation of `main::{constant#3}` failed - --> $DIR/test.rs:38:14 - | -LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. - | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 - -note: erroneous constant encountered - --> $DIR/test.rs:38:5 - | -LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. - | ^^^^^^^^^^^^^^^^^^^^^^ - error: indexing may panic - --> $DIR/test.rs:29:5 + --> $DIR/test.rs:27:5 | LL | x[index]; | ^^^^^^^^ @@ -21,7 +9,7 @@ LL | x[index]; = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` error: indexing may panic - --> $DIR/test.rs:47:5 + --> $DIR/test.rs:42:5 | LL | v[0]; | ^^^^ @@ -29,7 +17,7 @@ LL | v[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/test.rs:48:5 + --> $DIR/test.rs:43:5 | LL | v[10]; | ^^^^^ @@ -37,7 +25,7 @@ LL | v[10]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/test.rs:49:5 + --> $DIR/test.rs:44:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -45,7 +33,7 @@ LL | v[1 << 3]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/test.rs:55:5 + --> $DIR/test.rs:50:5 | LL | v[N]; | ^^^^ @@ -53,19 +41,12 @@ LL | v[N]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/test.rs:56:5 + --> $DIR/test.rs:51:5 | LL | v[M]; | ^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead -error[E0080]: evaluation of constant value failed - --> $DIR/test.rs:16:24 - | -LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. - | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 +error: aborting due to 6 previous errors -error: aborting due to 8 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 1ac0bb11014a..2ababad7fc75 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -13,8 +13,6 @@ const ARR: [i32; 2] = [1, 2]; const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. //~^ ERROR: indexing may panic -const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. -//~^ ERROR: indexing may panic const fn idx() -> usize { 1 diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 6d64fa1e6cf8..2996e31a1aa2 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -9,29 +9,20 @@ LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-re = note: `-D clippy::indexing-slicing` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::indexing_slicing)]` -error: indexing may panic - --> $DIR/indexing_slicing_index.rs:16:24 - | -LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. - | ^^^^^^^^^^^ - | - = help: consider using `.get(n)` or `.get_mut(n)` instead - = note: the suggestion might not be applicable in constant blocks - error[E0080]: evaluation of `main::{constant#3}` failed - --> $DIR/indexing_slicing_index.rs:48:14 + --> $DIR/indexing_slicing_index.rs:46:14 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 note: erroneous constant encountered - --> $DIR/indexing_slicing_index.rs:48:5 + --> $DIR/indexing_slicing_index.rs:46:5 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^^^^^^^^^^^^ error: indexing may panic - --> $DIR/indexing_slicing_index.rs:29:5 + --> $DIR/indexing_slicing_index.rs:27:5 | LL | x[index]; | ^^^^^^^^ @@ -39,7 +30,7 @@ LL | x[index]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> $DIR/indexing_slicing_index.rs:32:5 + --> $DIR/indexing_slicing_index.rs:30:5 | LL | x[4]; | ^^^^ @@ -48,13 +39,13 @@ LL | x[4]; = help: to override `-D warnings` add `#[allow(clippy::out_of_bounds_indexing)]` error: index is out of bounds - --> $DIR/indexing_slicing_index.rs:34:5 + --> $DIR/indexing_slicing_index.rs:32:5 | LL | x[1 << 3]; | ^^^^^^^^^ error: indexing may panic - --> $DIR/indexing_slicing_index.rs:45:14 + --> $DIR/indexing_slicing_index.rs:43:14 | LL | const { &ARR[idx()] }; | ^^^^^^^^^^ @@ -63,7 +54,7 @@ LL | const { &ARR[idx()] }; = note: the suggestion might not be applicable in constant blocks error: indexing may panic - --> $DIR/indexing_slicing_index.rs:48:14 + --> $DIR/indexing_slicing_index.rs:46:14 | LL | const { &ARR[idx4()] }; | ^^^^^^^^^^^ @@ -72,13 +63,13 @@ LL | const { &ARR[idx4()] }; = note: the suggestion might not be applicable in constant blocks error: index is out of bounds - --> $DIR/indexing_slicing_index.rs:55:5 + --> $DIR/indexing_slicing_index.rs:53:5 | LL | y[4]; | ^^^^ error: indexing may panic - --> $DIR/indexing_slicing_index.rs:58:5 + --> $DIR/indexing_slicing_index.rs:56:5 | LL | v[0]; | ^^^^ @@ -86,7 +77,7 @@ LL | v[0]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:60:5 + --> $DIR/indexing_slicing_index.rs:58:5 | LL | v[10]; | ^^^^^ @@ -94,7 +85,7 @@ LL | v[10]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:62:5 + --> $DIR/indexing_slicing_index.rs:60:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -102,13 +93,13 @@ LL | v[1 << 3]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> $DIR/indexing_slicing_index.rs:70:5 + --> $DIR/indexing_slicing_index.rs:68:5 | LL | x[N]; | ^^^^ error: indexing may panic - --> $DIR/indexing_slicing_index.rs:73:5 + --> $DIR/indexing_slicing_index.rs:71:5 | LL | v[N]; | ^^^^ @@ -116,7 +107,7 @@ LL | v[N]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic - --> $DIR/indexing_slicing_index.rs:75:5 + --> $DIR/indexing_slicing_index.rs:73:5 | LL | v[M]; | ^^^^ @@ -124,17 +115,11 @@ LL | v[M]; = help: consider using `.get(n)` or `.get_mut(n)` instead error: index is out of bounds - --> $DIR/indexing_slicing_index.rs:79:13 + --> $DIR/indexing_slicing_index.rs:77:13 | LL | let _ = x[4]; | ^^^^ -error[E0080]: evaluation of constant value failed - --> $DIR/indexing_slicing_index.rs:16:24 - | -LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. - | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 - -error: aborting due to 17 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0080`. From 86cb711b9647827783c4c0dd699d5ae835b2dc40 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 6 Feb 2024 16:44:30 +1100 Subject: [PATCH 23/26] Reduce capabilities of `Diagnostic`. Currently many diagnostic modifier methods are available on both `Diagnostic` and `DiagnosticBuilder`. This commit removes most of them from `Diagnostic`. To minimize the diff size, it keeps them within `diagnostic.rs` but changes the surrounding `impl Diagnostic` block to `impl DiagnosticBuilder`. (I intend to move things around later, to give a more sensible code layout.) `Diagnostic` keeps a few methods that it still needs, like `sub`, `arg`, and `replace_args`. The `forward!` macro, which defined two additional methods per call (e.g. `note` and `with_note`), is replaced by the `with_fn!` macro, which defines one additional method per call (e.g. `with_note`). It's now also only used when necessary -- not all modifier methods currently need a `with_*` form. (New ones can be easily added as necessary.) All this also requires changing `trait AddToDiagnostic` so its methods take `DiagnosticBuilder` instead of `Diagnostic`, which leads to many mechanical changes. `SubdiagnosticMessageOp` gains a type parameter `G`. There are three subdiagnostics -- `DelayedAtWithoutNewline`, `DelayedAtWithNewline`, and `InvalidFlushedDelayedDiagnosticLevel` -- that are created within the diagnostics machinery and appended to external diagnostics. These are handled at the `Diagnostic` level, which means it's now hard to construct them via `derive(Diagnostic)`, so instead we construct them by hand. This has no effect on what they look like when printed. There are lots of new `allow` markers for `untranslatable_diagnostics` and `diagnostics_outside_of_impl`. This is because `#[rustc_lint_diagnostics]` annotations were present on the `Diagnostic` modifier methods, but missing from the `DiagnosticBuilder` modifier methods. They're now present. --- clippy_utils/src/sugg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index c86362c427ce..b355e66b7b12 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -683,7 +683,7 @@ fn indentation(cx: &T, span: Span) -> Option { }) } -/// Convenience extension trait for `Diagnostic`. +/// Convenience extension trait for `DiagnosticBuilder`. pub trait DiagnosticExt { /// Suggests to add an attribute to an item. /// @@ -731,7 +731,7 @@ fn suggest_item_with_attr( fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability); } -impl DiagnosticExt for rustc_errors::Diagnostic { +impl DiagnosticExt for rustc_errors::DiagnosticBuilder<'_, ()> { fn suggest_item_with_attr( &mut self, cx: &T, From 6591dc683e733c793e156b255d068c1576be2c0f Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 22 Feb 2024 15:59:39 +0100 Subject: [PATCH 24/26] Bump nightly version -> 2024-02-22 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index fcf5c456270c..fb038b617dfe 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-02-08" +channel = "nightly-2024-02-22" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From cc6dcaae570a3f1deeb92c73df4eb674ba3848cb Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 22 Feb 2024 16:07:28 +0100 Subject: [PATCH 25/26] Use Level::from_symbol in unnecessary_clippy_cfg --- clippy_lints/src/attrs.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index f90a7dbbfe51..340111edcf14 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1000,9 +1000,7 @@ fn check_clippy_cfg_attr( ) { if cfg_attr.has_name(sym::clippy) && let Some(ident) = behind_cfg_attr.ident() - // FIXME: replace with `from_symbol` once https://github.com/rust-lang/rust/pull/121230 - // is merged. - && Level::from_str(ident.name.as_str()).is_some() + && Level::from_symbol(ident.name, Some(attr.id)).is_some() && let Some(items) = behind_cfg_attr.meta_item_list() { let nb_items = items.len(); From 8a58b7613ddeb397cd295962aa26ec2093ad44df Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 22 Feb 2024 16:27:24 +0100 Subject: [PATCH 26/26] Update i686 asm test stderr --- tests/ui/asm_syntax_x86.i686.stderr | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/ui/asm_syntax_x86.i686.stderr b/tests/ui/asm_syntax_x86.i686.stderr index 3caca42b4f67..aaf742731a0c 100644 --- a/tests/ui/asm_syntax_x86.i686.stderr +++ b/tests/ui/asm_syntax_x86.i686.stderr @@ -31,7 +31,6 @@ LL | global_asm!(""); | ^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax - = note: this error originates in the macro `global_asm` (in Nightly builds, run with -Z macro-backtrace for more info) error: Intel x86 assembly syntax used --> tests/ui/asm_syntax_x86.rs:22:5 @@ -40,7 +39,6 @@ LL | global_asm!("", options()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use AT&T x86 assembly syntax - = note: this error originates in the macro `global_asm` (in Nightly builds, run with -Z macro-backtrace for more info) error: AT&T x86 assembly syntax used --> tests/ui/asm_syntax_x86.rs:35:9 @@ -67,7 +65,6 @@ LL | global_asm!("", options(att_syntax)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use Intel x86 assembly syntax - = note: this error originates in the macro `global_asm` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 8 previous errors