diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index addb1e7d7553..116d63c3bb15 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_in_test; use clippy_utils::msrvs::Msrv; -use rustc_attr_data_structures::{RustcVersion, Stability, StableSince}; +use clippy_utils::{is_in_const_context, is_in_test}; +use rustc_attr_data_structures::{RustcVersion, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::DefKind; use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; @@ -80,7 +81,7 @@ enum Availability { pub struct IncompatibleMsrv { msrv: Msrv, - availability_cache: FxHashMap, + availability_cache: FxHashMap<(DefId, bool), Availability>, check_in_tests: bool, } @@ -96,18 +97,32 @@ impl IncompatibleMsrv { } /// Returns the availability of `def_id`, whether it is enabled through a feature or - /// available since a given version (the default being Rust 1.0.0). - fn get_def_id_availability(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> Availability { - if let Some(availability) = self.availability_cache.get(&def_id) { + /// available since a given version (the default being Rust 1.0.0). `needs_const` requires + /// the `const`-stability to be looked up instead of the stability in non-`const` contexts. + fn get_def_id_availability(&mut self, tcx: TyCtxt<'_>, def_id: DefId, needs_const: bool) -> Availability { + if let Some(availability) = self.availability_cache.get(&(def_id, needs_const)) { return *availability; } - let stability = tcx.lookup_stability(def_id); - let version = if stability.is_some_and(|stability| tcx.features().enabled(stability.feature)) { + let (feature, stability_level) = if needs_const { + tcx.lookup_const_stability(def_id) + .map(|stability| (stability.feature, stability.level)) + .unzip() + } else { + tcx.lookup_stability(def_id) + .map(|stability| (stability.feature, stability.level)) + .unzip() + }; + let version = if feature.is_some_and(|feature| tcx.features().enabled(feature)) { Availability::FeatureEnabled - } else if let Some(StableSince::Version(version)) = stability.as_ref().and_then(Stability::stable_since) { + } else if let Some(StableSince::Version(version)) = + stability_level.as_ref().and_then(StabilityLevel::stable_since) + { Availability::Since(version) + } else if needs_const { + // Fallback to regular stability + self.get_def_id_availability(tcx, def_id, false) } else if let Some(parent_def_id) = tcx.opt_parent(def_id) { - self.get_def_id_availability(tcx, parent_def_id) + self.get_def_id_availability(tcx, parent_def_id, needs_const) } else { Availability::Since(RustcVersion { major: 1, @@ -115,10 +130,11 @@ impl IncompatibleMsrv { patch: 0, }) }; - self.availability_cache.insert(def_id, version); + self.availability_cache.insert((def_id, needs_const), version); version } + /// Emit lint if `def_id`, associated with `node` and `span`, is below the current MSRV. fn emit_lint_if_under_msrv(&mut self, cx: &LateContext<'_>, def_id: DefId, node: HirId, span: Span) { if def_id.is_local() { // We don't check local items since their MSRV is supposed to always be valid. @@ -144,9 +160,13 @@ impl IncompatibleMsrv { return; } + let needs_const = cx.enclosing_body.is_some() + && is_in_const_context(cx) + && matches!(cx.tcx.def_kind(def_id), DefKind::AssocFn | DefKind::Fn); + if (self.check_in_tests || !is_in_test(cx.tcx, node)) && let Some(current) = self.msrv.current(cx) - && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id) + && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id, needs_const) && version > current { span_lint_and_then( @@ -154,7 +174,8 @@ impl IncompatibleMsrv { INCOMPATIBLE_MSRV, span, format!( - "current MSRV (Minimum Supported Rust Version) is `{current}` but this item is stable since `{version}`" + "current MSRV (Minimum Supported Rust Version) is `{current}` but this item is stable{} since `{version}`", + if needs_const { " in a `const` context" } else { "" }, ), |diag| { if is_under_cfg_attribute(cx, node) { @@ -168,7 +189,6 @@ impl IncompatibleMsrv { impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - // TODO: check for const stability when in const context match expr.kind { ExprKind::MethodCall(_, _, _, span) => { if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs index 1f9069c7c1cd..f7f21e1850d0 100644 --- a/tests/ui/incompatible_msrv.rs +++ b/tests/ui/incompatible_msrv.rs @@ -4,6 +4,7 @@ #![feature(strict_provenance)] // For use in test #![clippy::msrv = "1.3.0"] +use std::cell::Cell; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::future::Future; @@ -128,4 +129,43 @@ fn non_fn_items() { //~^ incompatible_msrv } +#[clippy::msrv = "1.87.0"] +fn msrv_non_ok_in_const() { + { + let c = Cell::new(42); + _ = c.get(); + } + const { + let c = Cell::new(42); + _ = c.get(); + //~^ incompatible_msrv + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_ok_in_const() { + { + let c = Cell::new(42); + _ = c.get(); + } + const { + let c = Cell::new(42); + _ = c.get(); + } +} + +#[clippy::msrv = "1.86.0"] +fn enum_variant_not_ok() { + let _ = std::io::ErrorKind::InvalidFilename; + //~^ incompatible_msrv + let _ = const { std::io::ErrorKind::InvalidFilename }; + //~^ incompatible_msrv +} + +#[clippy::msrv = "1.87.0"] +fn enum_variant_ok() { + let _ = std::io::ErrorKind::InvalidFilename; + let _ = const { std::io::ErrorKind::InvalidFilename }; +} + fn main() {} diff --git a/tests/ui/incompatible_msrv.stderr b/tests/ui/incompatible_msrv.stderr index fff1b956561c..e42360d296f5 100644 --- a/tests/ui/incompatible_msrv.stderr +++ b/tests/ui/incompatible_msrv.stderr @@ -1,5 +1,5 @@ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.10.0` - --> tests/ui/incompatible_msrv.rs:15:39 + --> tests/ui/incompatible_msrv.rs:16:39 | LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); | ^^^^^ @@ -8,37 +8,37 @@ LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); = 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` - --> tests/ui/incompatible_msrv.rs:21:11 + --> tests/ui/incompatible_msrv.rs:22: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` - --> tests/ui/incompatible_msrv.rs:25:5 + --> tests/ui/incompatible_msrv.rs:26:5 | LL | sleep(Duration::new(1, 0)); | ^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.2.0` but this item is stable since `1.3.0` - --> tests/ui/incompatible_msrv.rs:30:33 + --> tests/ui/incompatible_msrv.rs:31:33 | LL | static NO_BODY_BAD_MSRV: Option = None; | ^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.2.0` but this item is stable since `1.3.0` - --> tests/ui/incompatible_msrv.rs:37:19 + --> tests/ui/incompatible_msrv.rs:38:19 | LL | let _: Option = None; | ^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.43.0` - --> tests/ui/incompatible_msrv.rs:61:17 + --> tests/ui/incompatible_msrv.rs:62:17 | LL | let _ = core::iter::once_with(|| 0); | ^^^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.43.0` - --> tests/ui/incompatible_msrv.rs:68:21 + --> tests/ui/incompatible_msrv.rs:69:21 | LL | let _ = core::iter::once_with(|| $msg); | ^^^^^^^^^^^^^^^^^^^^^ @@ -49,25 +49,25 @@ LL | my_panic!("foo"); = note: this error originates in the macro `my_panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.43.0` - --> tests/ui/incompatible_msrv.rs:75:13 + --> tests/ui/incompatible_msrv.rs:76:13 | LL | assert!(core::iter::once_with(|| 0).next().is_some()); | ^^^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.80.0` but this item is stable since `1.82.0` - --> tests/ui/incompatible_msrv.rs:88:13 + --> tests/ui/incompatible_msrv.rs:89:13 | LL | let _ = std::iter::repeat_n((), 5); | ^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.82.0` - --> tests/ui/incompatible_msrv.rs:99:13 + --> tests/ui/incompatible_msrv.rs:100:13 | LL | let _ = std::iter::repeat_n((), 5); | ^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.82.0` - --> tests/ui/incompatible_msrv.rs:104:17 + --> tests/ui/incompatible_msrv.rs:105:17 | LL | let _ = std::iter::repeat_n((), 5); | ^^^^^^^^^^^^^^^^^^^ @@ -75,22 +75,40 @@ LL | let _ = std::iter::repeat_n((), 5); = note: you may want to conditionally increase the MSRV considered by Clippy using the `clippy::msrv` attribute error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.82.0` - --> tests/ui/incompatible_msrv.rs:109:17 + --> tests/ui/incompatible_msrv.rs:110:17 | LL | let _ = std::iter::repeat_n((), 5); | ^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.78.0` but this item is stable since `1.84.0` - --> tests/ui/incompatible_msrv.rs:122:7 + --> tests/ui/incompatible_msrv.rs:123:7 | LL | r.isqrt() | ^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.85.0` - --> tests/ui/incompatible_msrv.rs:127:13 + --> tests/ui/incompatible_msrv.rs:128:13 | LL | let _ = std::io::ErrorKind::CrossesDevices; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: current MSRV (Minimum Supported Rust Version) is `1.87.0` but this item is stable in a `const` context since `1.88.0` + --> tests/ui/incompatible_msrv.rs:140:15 + | +LL | _ = c.get(); + | ^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.86.0` but this item is stable since `1.87.0` + --> tests/ui/incompatible_msrv.rs:159:13 + | +LL | let _ = std::io::ErrorKind::InvalidFilename; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.86.0` but this item is stable since `1.87.0` + --> tests/ui/incompatible_msrv.rs:161:21 + | +LL | let _ = const { std::io::ErrorKind::InvalidFilename }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors