Skip to content

fix: missing_const_for_fn FP on unstable const traits #14294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions clippy_utils/src/qualify_min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
88 changes: 88 additions & 0 deletions tests/ui/missing_const_for_fn/cant_be_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}