about summary refs log tree commit diff
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
parentf2d49731575198637ee3bb2d83c7060c3581a512 (diff)
downloadrust-205e313ae4904468d8d2ee6f2cb785bb91271512.tar.gz
rust-205e313ae4904468d8d2ee6f2cb785bb91271512.zip
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.
-rw-r--r--clippy_lints/src/incompatible_msrv.rs45
-rw-r--r--tests/ui/incompatible_msrv.rs14
-rw-r--r--tests/ui/incompatible_msrv.stderr28
3 files changed, 55 insertions, 32 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(
diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs
index a6595dcb81e..c68c9c02dc4 100644
--- a/tests/ui/incompatible_msrv.rs
+++ b/tests/ui/incompatible_msrv.rs
@@ -1,5 +1,7 @@
 #![warn(clippy::incompatible_msrv)]
 #![feature(custom_inner_attributes)]
+#![allow(stable_features)]
+#![feature(strict_provenance)] // For use in test
 #![clippy::msrv = "1.3.0"]
 
 use std::collections::HashMap;
@@ -91,4 +93,16 @@ fn local_msrv_change_suggestion() {
     }
 }
 
+#[clippy::msrv = "1.78.0"]
+fn feature_enable_14425(ptr: *const u8) -> usize {
+    // Do not warn, because it is enabled through a feature even though
+    // it is stabilized only since Rust 1.84.0.
+    let r = ptr.addr();
+
+    // Warn about this which has been introduced in the same Rust version
+    // but is not allowed through a feature.
+    r.isqrt()
+    //~^ incompatible_msrv
+}
+
 fn main() {}
diff --git a/tests/ui/incompatible_msrv.stderr b/tests/ui/incompatible_msrv.stderr
index 18548c3ec18..b4477a02ac8 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:13:39
+  --> tests/ui/incompatible_msrv.rs:15:39
    |
 LL |     assert_eq!(map.entry("poneyland").key(), &"poneyland");
    |                                       ^^^^^
@@ -8,25 +8,25 @@ 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:19:11
+  --> tests/ui/incompatible_msrv.rs:21: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:23:5
+  --> tests/ui/incompatible_msrv.rs:25:5
    |
 LL |     sleep(Duration::new(1, 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:47:17
+  --> tests/ui/incompatible_msrv.rs:49: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:54:21
+  --> tests/ui/incompatible_msrv.rs:56:21
    |
 LL |             let _ = core::iter::once_with(|| $msg);
    |                     ^^^^^^^^^^^^^^^^^^^^^
@@ -37,25 +37,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:61:13
+  --> tests/ui/incompatible_msrv.rs:63: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:74:13
+  --> tests/ui/incompatible_msrv.rs:76: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:79:13
+  --> tests/ui/incompatible_msrv.rs:81: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:84:17
+  --> tests/ui/incompatible_msrv.rs:86:17
    |
 LL |         let _ = std::iter::repeat_n((), 5);
    |                 ^^^^^^^^^^^^^^^^^^^
@@ -63,10 +63,16 @@ 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:89:17
+  --> tests/ui/incompatible_msrv.rs:91:17
    |
 LL |         let _ = std::iter::repeat_n((), 5);
    |                 ^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 10 previous errors
+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:104:7
+   |
+LL |     r.isqrt()
+   |       ^^^^^^^
+
+error: aborting due to 11 previous errors