diff --git a/README.md b/README.md
index 20a5e997e629..21915e5c6fe0 100644
--- a/README.md
+++ b/README.md
@@ -237,37 +237,21 @@ define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
 ### Specifying the minimum supported Rust version
 
 Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
-specifying the minimum supported Rust version (MSRV) in the Clippy configuration file.
-
-```toml
-msrv = "1.30.0"
-```
-
-Alternatively, the [`rust-version` field](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field)
-in the `Cargo.toml` can be used.
+specifying the minimum supported Rust version (MSRV) in the [`rust-version` field](https://doc.rust-lang.org/cargo/reference/rust-version.html)
+of `Cargo.toml`.
 
 ```toml
 # Cargo.toml
 rust-version = "1.30"
 ```
 
-The MSRV can also be specified as an attribute, like below.
-
-```rust,ignore
-#![feature(custom_inner_attributes)]
-#![clippy::msrv = "1.30.0"]
-
-fn main() {
-  ...
-}
-```
-
-You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
-is equivalent to `msrv = 1.30.0`.
+Alternatively the [`msrv` field](https://doc.rust-lang.org/clippy/lint_configuration.html#msrv) can be specified in the
+Clippy configuration file.
 
-Note: `custom_inner_attributes` is an unstable feature, so it has to be enabled explicitly.
+Clippy will automatically adjust the MSRV for sections of code that uses `#[cfg(version)]`, alternatively the
+`#[clippy::msrv]` attribute can be used to specifiy the MSRV without any other effect.
 
-Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
+For more information see [Specifying the minimum supported Rust version](https://doc.rust-lang.org/clippy/configuration.html#specifying-the-minimum-supported-rust-version).
 
 ## Contributing
 
diff --git a/book/src/configuration.md b/book/src/configuration.md
index b13054431898..d47e9f282484 100644
--- a/book/src/configuration.md
+++ b/book/src/configuration.md
@@ -98,28 +98,45 @@ For more details and options, refer to the Cargo documentation.
 
 ### Specifying the minimum supported Rust version
 
-Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the
-minimum supported Rust version (MSRV) in the Clippy configuration file.
+Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
+specifying the minimum supported Rust version (MSRV) in the [`rust-version` field](https://doc.rust-lang.org/cargo/reference/rust-version.html)
+of `Cargo.toml`.
 
 ```toml
-msrv = "1.30.0"
+# Cargo.toml
+rust-version = "1.30"
 ```
 
-The MSRV can also be specified as an attribute, like below.
+Alternatively the [`msrv` field](https://doc.rust-lang.org/clippy/lint_configuration.html#msrv) can be specified in the
+Clippy configuration file.
 
-```rust,ignore
-#![feature(custom_inner_attributes)]
-#![clippy::msrv = "1.30.0"]
+```toml
+# clippy.toml
+msrv = "1.30"
+```
+
+Clippy will automatically adjust the MSRV for sections of code that use `#[cfg(version)]`:
 
-fn main() {
-    ...
+```rust
+#[cfg(version("1.90"))]
+fn f() {
+    // The MSRV here is set to 1.90
 }
 ```
 
-You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
-is equivalent to `msrv = 1.30.0`.
+> **Note:** `cfg(version)` is not yet [available on stable](https://github.com/rust-lang/rust/pull/141766)
+
+The `#[clippy::msrv]` can also be used to set the MSRV for a section of code with no other effect:
+
+```rust
+#[clippy::msrv = "1.30"]
+fn f() {
+    // The MSRV here is set to 1.30
+}
+```
 
-Note: `custom_inner_attributes` is an unstable feature, so it has to be enabled explicitly.
+If both `#[cfg(version)]` and `#[clippy::msrv]` attributes are applied to the same node then `#[clippy::msrv]` takes
+precedence.
 
 Lints that recognize this configuration option can be
 found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index a5e66ad463bb..64fb9656ae24 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -1,6 +1,6 @@
 use crate::sym;
-use rustc_ast::Attribute;
 use rustc_ast::attr::AttributeExt;
+use rustc_ast::{Attribute, LitKind, MetaItem, MetaItemInner};
 use rustc_attr_data_structures::RustcVersion;
 use rustc_attr_parsing::parse_version;
 use rustc_lint::LateContext;
@@ -186,27 +186,67 @@ impl MsrvStack {
 }
 
 fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt]) -> Option<RustcVersion> {
-    let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym::msrv]));
-
-    if let Some(msrv_attr) = msrv_attrs.next() {
-        if let Some(duplicate) = msrv_attrs.next_back() {
-            sess.dcx()
-                .struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times")
-                .with_span_note(msrv_attr.span(), "first definition found here")
-                .emit();
-        }
-
-        if let Some(msrv) = msrv_attr.value_str() {
-            if let Some(version) = parse_version(msrv) {
-                return Some(version);
+    let mut first_clippy_attr = None;
+    let mut clippy_msrv = None;
+    let mut cfg_version = None;
+    for attr in attrs {
+        if attr.path_matches(&[sym::clippy, sym::msrv]) {
+            match first_clippy_attr {
+                None => first_clippy_attr = Some(attr),
+                Some(first) => {
+                    sess.dcx()
+                        .struct_span_err(attr.span(), "`clippy::msrv` is defined multiple times")
+                        .with_span_note(first.span(), "first definition found here")
+                        .emit();
+                },
             }
 
-            sess.dcx()
-                .span_err(msrv_attr.span(), format!("`{msrv}` is not a valid Rust version"));
-        } else {
-            sess.dcx().span_err(msrv_attr.span(), "bad clippy attribute");
+            if let Some(msrv) = attr.value_str() {
+                if let Some(version) = parse_version(msrv) {
+                    clippy_msrv = Some(version);
+                } else {
+                    sess.dcx()
+                        .span_err(attr.span(), format!("`{msrv}` is not a valid Rust version"));
+                }
+            } else {
+                sess.dcx().span_err(attr.span(), "bad clippy attribute");
+            }
+        } else if matches!(attr.name(), Some(sym::cfg | sym::cfg_trace)) // cfg in early passes, cfg_trace in late
+            && let Some(list) = attr.meta_item_list()
+            && let [MetaItemInner::MetaItem(meta_item)] = list.as_slice()
+        {
+            parse_cfg_version(&mut cfg_version, meta_item, false);
         }
     }
 
-    None
+    clippy_msrv.or(cfg_version)
+}
+
+fn parse_cfg_version(current: &mut Option<RustcVersion>, meta_item: &MetaItem, mut negated: bool) {
+    let Some(name) = meta_item.name() else { return };
+    match name {
+        sym::version => {
+            if !negated
+                && let Some([MetaItemInner::Lit(lit)]) = meta_item.meta_item_list()
+                && let LitKind::Str(s, _) = lit.kind
+                && let Some(version) = parse_version(s)
+            {
+                match current {
+                    Some(current) => *current = version.min(*current),
+                    None => *current = Some(version),
+                }
+            }
+        },
+        sym::any | sym::all | sym::not => {
+            if name == sym::not {
+                negated = !negated;
+            }
+            for inner in meta_item.meta_item_list().into_iter().flatten() {
+                if let Some(inner_meta_item) = inner.meta_item() {
+                    parse_cfg_version(current, inner_meta_item, negated);
+                }
+            }
+        },
+        _ => {},
+    }
 }
diff --git a/tests/ui/cfg_version_msrv.rs b/tests/ui/cfg_version_msrv.rs
new file mode 100644
index 000000000000..a9afca8cf190
--- /dev/null
+++ b/tests/ui/cfg_version_msrv.rs
@@ -0,0 +1,40 @@
+#![feature(cfg_version)]
+
+fn f(i: i32) {
+    #[cfg(version("1.50"))]
+    let _ = i.isqrt();
+    //~^ ERROR: is `1.50.0`
+
+    // When `any/all` are used pick the smallest version seen
+    #[cfg(any(version("1.49"), version("1.50")))]
+    let _ = i.isqrt();
+    //~^ ERROR: is `1.49.0`
+    #[cfg(all(version("1.60"), version("1.59")))]
+    let _ = i.isqrt();
+    //~^ ERROR: is `1.59.0`
+
+    // Ignore negated version requirements
+    #[cfg(not(version("1.50")))]
+    let _ = i.isqrt();
+    #[cfg(not(not(version("1.50"))))]
+    let _ = i.isqrt();
+    //~^ ERROR: is `1.50.0`
+    #[cfg(not(all(version("1.40"), not(version("1.50")))))]
+    let _ = i.isqrt();
+    //~^ ERROR: is `1.50.0`
+}
+
+/// If both are specified on the same node then `clippy::msrv` takes precedence
+#[clippy::msrv = "1.50"]
+#[cfg(version("1.40"))]
+fn both_attributes_cfg_lower(i: i32) {
+    let _ = i.isqrt();
+    //~^ ERROR: is `1.50.0`
+}
+
+#[clippy::msrv = "1.40"]
+#[cfg(version("1.50"))]
+fn both_attributes_cfg_higher(i: i32) {
+    let _ = i.isqrt();
+    //~^ ERROR: is `1.40.0`
+}
diff --git a/tests/ui/cfg_version_msrv.stderr b/tests/ui/cfg_version_msrv.stderr
new file mode 100644
index 000000000000..5610f7523799
--- /dev/null
+++ b/tests/ui/cfg_version_msrv.stderr
@@ -0,0 +1,47 @@
+error: current MSRV (Minimum Supported Rust Version) is `1.50.0` but this item is stable since `1.84.0`
+  --> tests/ui/cfg_version_msrv.rs:5:15
+   |
+LL |     let _ = i.isqrt();
+   |               ^^^^^^^
+   |
+   = note: `-D clippy::incompatible-msrv` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::incompatible_msrv)]`
+
+error: current MSRV (Minimum Supported Rust Version) is `1.49.0` but this item is stable since `1.84.0`
+  --> tests/ui/cfg_version_msrv.rs:10:15
+   |
+LL |     let _ = i.isqrt();
+   |               ^^^^^^^
+
+error: current MSRV (Minimum Supported Rust Version) is `1.59.0` but this item is stable since `1.84.0`
+  --> tests/ui/cfg_version_msrv.rs:13:15
+   |
+LL |     let _ = i.isqrt();
+   |               ^^^^^^^
+
+error: current MSRV (Minimum Supported Rust Version) is `1.50.0` but this item is stable since `1.84.0`
+  --> tests/ui/cfg_version_msrv.rs:20:15
+   |
+LL |     let _ = i.isqrt();
+   |               ^^^^^^^
+
+error: current MSRV (Minimum Supported Rust Version) is `1.50.0` but this item is stable since `1.84.0`
+  --> tests/ui/cfg_version_msrv.rs:23:15
+   |
+LL |     let _ = i.isqrt();
+   |               ^^^^^^^
+
+error: current MSRV (Minimum Supported Rust Version) is `1.50.0` but this item is stable since `1.84.0`
+  --> tests/ui/cfg_version_msrv.rs:31:15
+   |
+LL |     let _ = i.isqrt();
+   |               ^^^^^^^
+
+error: current MSRV (Minimum Supported Rust Version) is `1.40.0` but this item is stable since `1.84.0`
+  --> tests/ui/cfg_version_msrv.rs:38:15
+   |
+LL |     let _ = i.isqrt();
+   |               ^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs
index c8409d78ed77..1d232adb6216 100644
--- a/tests/ui/min_rust_version_invalid_attr.rs
+++ b/tests/ui/min_rust_version_invalid_attr.rs
@@ -13,6 +13,8 @@ fn outer_attr() {}
 mod multiple {
     #![clippy::msrv = "1.40"]
     #![clippy::msrv = "=1.35.0"]
+    //~^ ERROR: `clippy::msrv` is defined multiple times
+    //~| ERROR: `=1.35.0` is not a valid Rust version
     #![clippy::msrv = "1.10.1"]
     //~^ ERROR: `clippy::msrv` is defined multiple times
 
diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr
index dbc276ed89df..dc05cc634f83 100644
--- a/tests/ui/min_rust_version_invalid_attr.stderr
+++ b/tests/ui/min_rust_version_invalid_attr.stderr
@@ -11,7 +11,25 @@ LL | #[clippy::msrv = "invalid.version"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `clippy::msrv` is defined multiple times
-  --> tests/ui/min_rust_version_invalid_attr.rs:16:5
+  --> tests/ui/min_rust_version_invalid_attr.rs:15:5
+   |
+LL |     #![clippy::msrv = "=1.35.0"]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: first definition found here
+  --> tests/ui/min_rust_version_invalid_attr.rs:14:5
+   |
+LL |     #![clippy::msrv = "1.40"]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `=1.35.0` is not a valid Rust version
+  --> tests/ui/min_rust_version_invalid_attr.rs:15:5
+   |
+LL |     #![clippy::msrv = "=1.35.0"]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `clippy::msrv` is defined multiple times
+  --> tests/ui/min_rust_version_invalid_attr.rs:18:5
    |
 LL |     #![clippy::msrv = "1.10.1"]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -23,16 +41,16 @@ LL |     #![clippy::msrv = "1.40"]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `clippy::msrv` is defined multiple times
-  --> tests/ui/min_rust_version_invalid_attr.rs:21:9
+  --> tests/ui/min_rust_version_invalid_attr.rs:23:9
    |
 LL |         #![clippy::msrv = "1.0.0"]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: first definition found here
-  --> tests/ui/min_rust_version_invalid_attr.rs:20:9
+  --> tests/ui/min_rust_version_invalid_attr.rs:22:9
    |
 LL |         #![clippy::msrv = "1.0"]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 6 previous errors