From 8f2d47ea72fdc754697b381441fb49953bf37b58 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 19 Aug 2023 18:06:46 +0000 Subject: [PATCH] Check that the suggested method exists in unwrap_or_default --- clippy_lints/src/methods/or_fun_call.rs | 19 ++++++++++++++-- tests/ui/unwrap_or_else_default.fixed | 30 +++++++++++++++++++++++++ tests/ui/unwrap_or_else_default.rs | 30 +++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 8b2f57160af4..942f3bd79a61 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -65,11 +65,26 @@ pub(super) fn check<'tcx>( }; let sugg = match (name, call_expr.is_some()) { - ("unwrap_or", true) | ("unwrap_or_else", false) => "unwrap_or_default", - ("or_insert", true) | ("or_insert_with", false) => "or_default", + ("unwrap_or", true) | ("unwrap_or_else", false) => sym!(unwrap_or_default), + ("or_insert", true) | ("or_insert_with", false) => sym!(or_default), _ => return false, }; + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); + let has_suggested_method = receiver_ty.ty_adt_def().is_some_and(|adt_def| { + cx.tcx + .inherent_impls(adt_def.did()) + .iter() + .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) + .any(|assoc| { + assoc.fn_has_self_parameter + && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 + }) + }); + if !has_suggested_method { + return false; + } + // needs to target Default::default in particular or be *::new and have a Default impl // available if (is_new(fun) && output_type_implements_default(fun)) diff --git a/tests/ui/unwrap_or_else_default.fixed b/tests/ui/unwrap_or_else_default.fixed index 73d999079586..8d5d34175c52 100644 --- a/tests/ui/unwrap_or_else_default.fixed +++ b/tests/ui/unwrap_or_else_default.fixed @@ -130,4 +130,34 @@ fn method_call_with_deref() { let _ = inner_map.entry(0).or_default(); } +fn missing_suggested_method() { + #[derive(Copy, Clone)] + struct S(T); + + impl S { + fn or_insert_with(&mut self, default: impl FnOnce() -> T) -> &mut T { + &mut self.0 + } + + fn or_insert(&mut self, default: T) -> &mut T { + &mut self.0 + } + + fn unwrap_or_else(self, default: impl FnOnce() -> T) -> T { + self.0 + } + + fn unwrap_or(self, default: T) -> T { + self.0 + } + } + + // Don't lint when or_default/unwrap_or_default do not exist on the type + let mut s = S(1); + s.or_insert_with(Default::default); + s.or_insert(Default::default()); + s.unwrap_or_else(Default::default); + s.unwrap_or(Default::default()); +} + fn main() {} diff --git a/tests/ui/unwrap_or_else_default.rs b/tests/ui/unwrap_or_else_default.rs index afacedf17c63..adbcb4b44659 100644 --- a/tests/ui/unwrap_or_else_default.rs +++ b/tests/ui/unwrap_or_else_default.rs @@ -130,4 +130,34 @@ fn method_call_with_deref() { let _ = inner_map.entry(0).or_insert_with(Default::default); } +fn missing_suggested_method() { + #[derive(Copy, Clone)] + struct S(T); + + impl S { + fn or_insert_with(&mut self, default: impl FnOnce() -> T) -> &mut T { + &mut self.0 + } + + fn or_insert(&mut self, default: T) -> &mut T { + &mut self.0 + } + + fn unwrap_or_else(self, default: impl FnOnce() -> T) -> T { + self.0 + } + + fn unwrap_or(self, default: T) -> T { + self.0 + } + } + + // Don't lint when or_default/unwrap_or_default do not exist on the type + let mut s = S(1); + s.or_insert_with(Default::default); + s.or_insert(Default::default()); + s.unwrap_or_else(Default::default); + s.unwrap_or(Default::default()); +} + fn main() {}