about summary refs log tree commit diff
diff options
context:
space:
mode:
authoryanglsh <yanglsh@shanghaitech.edu.cn>2025-02-25 22:47:42 +0800
committeryanglsh <yanglsh@shanghaitech.edu.cn>2025-03-06 14:25:06 +0800
commita028651184f66700f3c48755f4d8ff52666df1c6 (patch)
tree8313a179f0c34b3c4aa03ccefbdb43c9931fd2d2
parent81643e297cf44ce3c7648b8443fc4d6592fa81eb (diff)
fix: `missing_const_for_fn` FP on unstable const traits
-rw-r--r--clippy_utils/src/qualify_min_const_fn.rs40
-rw-r--r--tests/ui/missing_const_for_fn/cant_be_const.rs88
2 files changed, 112 insertions, 16 deletions
diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index 8e6f4d4a317..5d0401010db 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -395,24 +395,32 @@ fn check_terminator<'tcx>(
 
 fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool {
     cx.tcx.is_const_fn(def_id)
-        && cx.tcx.lookup_const_stability(def_id).is_none_or(|const_stab| {
-            if let rustc_attr_parsing::StabilityLevel::Stable { since, .. } = const_stab.level {
-                // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
-                // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`.
-                // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
+        && cx
+            .tcx
+            .lookup_const_stability(def_id)
+            .or_else(|| {
+                cx.tcx
+                    .trait_of_item(def_id)
+                    .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id))
+            })
+            .is_none_or(|const_stab| {
+                if let rustc_attr_parsing::StabilityLevel::Stable { since, .. } = const_stab.level {
+                    // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
+                    // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`.
+                    // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
 
-                let const_stab_rust_version = match since {
-                    StableSince::Version(version) => version,
-                    StableSince::Current => RustcVersion::CURRENT,
-                    StableSince::Err => return false,
-                };
+                    let const_stab_rust_version = match since {
+                        StableSince::Version(version) => version,
+                        StableSince::Current => RustcVersion::CURRENT,
+                        StableSince::Err => return false,
+                    };
 
-                msrv.meets(cx, const_stab_rust_version)
-            } else {
-                // Unstable const fn, check if the feature is enabled.
-                cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none()
-            }
-        })
+                    msrv.meets(cx, const_stab_rust_version)
+                } else {
+                    // Unstable const fn, check if the feature is enabled.
+                    cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none()
+                }
+            })
 }
 
 fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool {
diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs
index aef5eb5b890..d8e1c468db0 100644
--- a/tests/ui/missing_const_for_fn/cant_be_const.rs
+++ b/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -217,3 +217,91 @@ mod with_ty_alias {
 fn mut_add(x: &mut i32) {
     *x += 1;
 }
+
+#[clippy::msrv = "1.87"]
+mod issue14020 {
+    use std::ops::Add;
+
+    fn f<T: Add>(a: T, b: T) -> <T as Add>::Output {
+        a + b
+    }
+}
+
+#[clippy::msrv = "1.87"]
+mod issue14290 {
+    use std::ops::{Deref, DerefMut};
+
+    struct Wrapper<T> {
+        t: T,
+    }
+
+    impl<T> Deref for Wrapper<T> {
+        type Target = T;
+
+        fn deref(&self) -> &Self::Target {
+            &self.t
+        }
+    }
+    impl<T> DerefMut for Wrapper<T> {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.t
+        }
+    }
+
+    struct Example(bool);
+
+    fn do_something(mut a: Wrapper<Example>) {
+        a.0 = !a.0;
+    }
+
+    pub struct Stream(Vec<u8>);
+
+    impl Stream {
+        pub fn bytes(&self) -> &[u8] {
+            &self.0
+        }
+    }
+}
+
+#[clippy::msrv = "1.87"]
+mod issue14091 {
+    use std::mem::ManuallyDrop;
+
+    struct BucketSlotGuard<'a> {
+        id: u32,
+        free_list: &'a mut Vec<u32>,
+    }
+
+    impl BucketSlotGuard<'_> {
+        fn into_inner(self) -> u32 {
+            let this = ManuallyDrop::new(self);
+            this.id
+        }
+    }
+
+    use std::ops::{Deref, DerefMut};
+
+    struct Wrap<T>(T);
+
+    impl<T> Deref for Wrap<T> {
+        type Target = T;
+        fn deref(&self) -> &T {
+            &self.0
+        }
+    }
+
+    impl<T> DerefMut for Wrap<T> {
+        fn deref_mut(&mut self) -> &mut T {
+            &mut self.0
+        }
+    }
+
+    fn smart_two_field(v: &mut Wrap<(i32, i32)>) {
+        let _a = &mut v.0;
+        let _b = &mut v.1;
+    }
+
+    fn smart_destructure(v: &mut Wrap<(i32, i32)>) {
+        let (ref mut _head, ref mut _tail) = **v;
+    }
+}