about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorSamuel Tardieu <sam@rfc1149.net>2025-03-18 19:21:30 +0100
committerSamuel Tardieu <sam@rfc1149.net>2025-05-21 17:17:30 +0200
commit205e313ae4904468d8d2ee6f2cb785bb91271512 (patch)
tree5b9c0939ab721c476f2b567f1164b6614f48bdab /clippy_lints/src
parentf2d49731575198637ee3bb2d83c7060c3581a512 (diff)
Do not warn about feature-enabled items
If an item has been enabled through a feature, it will not be linted
even though the MSRV is not compatible. This use case may happen when
stable compilers are allowed to enable unstable features, e.g. in
the Rust for Linux toolchain.
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/incompatible_msrv.rs45
1 files changed, 24 insertions, 21 deletions
diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs
index 9d98777c632..1fd8ecd179a 100644
--- a/clippy_lints/src/incompatible_msrv.rs
+++ b/clippy_lints/src/incompatible_msrv.rs
@@ -2,7 +2,7 @@ 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, StabilityLevel, StableSince};
+use rustc_attr_data_structures::{RustcVersion, Stability, StableSince};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{Expr, ExprKind, HirId, QPath};
 use rustc_lint::{LateContext, LateLintPass};
@@ -72,9 +72,15 @@ declare_clippy_lint! {
     "ensures that all items used in the crate are available for the current MSRV"
 }
 
+#[derive(Clone, Copy)]
+enum Availability {
+    FeatureEnabled,
+    Since(RustcVersion),
+}
+
 pub struct IncompatibleMsrv {
     msrv: Msrv,
-    is_above_msrv: FxHashMap<DefId, RustcVersion>,
+    availability_cache: FxHashMap<DefId, Availability>,
     check_in_tests: bool,
 }
 
@@ -84,35 +90,32 @@ impl IncompatibleMsrv {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
             msrv: conf.msrv,
-            is_above_msrv: FxHashMap::default(),
+            availability_cache: FxHashMap::default(),
             check_in_tests: conf.check_incompatible_msrv_in_tests,
         }
     }
 
-    fn get_def_id_version(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> RustcVersion {
-        if let Some(version) = self.is_above_msrv.get(&def_id) {
-            return *version;
+    /// 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) {
+            return *availability;
         }
-        let version = if let Some(version) = tcx
-            .lookup_stability(def_id)
-            .and_then(|stability| match stability.level {
-                StabilityLevel::Stable {
-                    since: StableSince::Version(version),
-                    ..
-                } => Some(version),
-                _ => None,
-            }) {
-            version
+        let stability = tcx.lookup_stability(def_id);
+        let version = if stability.is_some_and(|stability| tcx.features().enabled(stability.feature)) {
+            Availability::FeatureEnabled
+        } else if let Some(StableSince::Version(version)) = stability.as_ref().and_then(Stability::stable_since) {
+            Availability::Since(version)
         } else if let Some(parent_def_id) = tcx.opt_parent(def_id) {
-            self.get_def_id_version(tcx, parent_def_id)
+            self.get_def_id_availability(tcx, parent_def_id)
         } else {
-            RustcVersion {
+            Availability::Since(RustcVersion {
                 major: 1,
                 minor: 0,
                 patch: 0,
-            }
+            })
         };
-        self.is_above_msrv.insert(def_id, version);
+        self.availability_cache.insert(def_id, version);
         version
     }
 
@@ -143,7 +146,7 @@ impl IncompatibleMsrv {
 
         if (self.check_in_tests || !is_in_test(cx.tcx, node))
             && let Some(current) = self.msrv.current(cx)
-            && let version = self.get_def_id_version(cx.tcx, def_id)
+            && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id)
             && version > current
         {
             span_lint_and_then(