about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/methods/or_fun_call.rs19
-rw-r--r--tests/ui/unwrap_or_else_default.fixed30
-rw-r--r--tests/ui/unwrap_or_else_default.rs30
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 8b2f57160af..942f3bd79a6 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 73d99907958..8d5d34175c5 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>(T);
+
+    impl<T> S<T> {
+        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 afacedf17c6..adbcb4b4465 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>(T);
+
+    impl<T> S<T> {
+        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() {}