Skip to content

Commit 41ca6fb

Browse files
committed
incompatible_msrv: Don't check the const version for functions referenced by name, but not called.
1 parent 3c93ba0 commit 41ca6fb

File tree

3 files changed

+73
-121
lines changed

3 files changed

+73
-121
lines changed

clippy_lints/src/incompatible_msrv.rs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::msrvs::Msrv;
44
use clippy_utils::{is_in_const_context, is_in_test};
55
use rustc_data_structures::fx::FxHashMap;
6-
use rustc_hir::def::DefKind;
76
use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, QPath, RustcVersion, StabilityLevel, StableSince};
87
use rustc_lint::{LateContext, LateLintPass};
9-
use rustc_middle::ty::TyCtxt;
8+
use rustc_middle::ty::{self, TyCtxt};
109
use rustc_session::impl_lint_pass;
1110
use rustc_span::def_id::{CrateNum, DefId};
1211
use rustc_span::{ExpnKind, Span, sym};
@@ -83,6 +82,10 @@ pub struct IncompatibleMsrv {
8382
availability_cache: FxHashMap<(DefId, bool), Availability>,
8483
check_in_tests: bool,
8584
core_crate: Option<CrateNum>,
85+
86+
// The most recently called path. Used to skip checking the path after it's
87+
// been checked when visiting the call expression.
88+
called_path: Option<HirId>,
8689
}
8790

8891
impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]);
@@ -98,6 +101,7 @@ impl IncompatibleMsrv {
98101
.iter()
99102
.find(|krate| tcx.crate_name(**krate) == sym::core)
100103
.copied(),
104+
called_path: None,
101105
}
102106
}
103107

@@ -140,7 +144,14 @@ impl IncompatibleMsrv {
140144
}
141145

142146
/// Emit lint if `def_id`, associated with `node` and `span`, is below the current MSRV.
143-
fn emit_lint_if_under_msrv(&mut self, cx: &LateContext<'_>, def_id: DefId, node: HirId, span: Span) {
147+
fn emit_lint_if_under_msrv(
148+
&mut self,
149+
cx: &LateContext<'_>,
150+
needs_const: bool,
151+
def_id: DefId,
152+
node: HirId,
153+
span: Span,
154+
) {
144155
if def_id.is_local() {
145156
// We don't check local items since their MSRV is supposed to always be valid.
146157
return;
@@ -158,10 +169,6 @@ impl IncompatibleMsrv {
158169
return;
159170
}
160171

161-
let needs_const = cx.enclosing_body.is_some()
162-
&& is_in_const_context(cx)
163-
&& matches!(cx.tcx.def_kind(def_id), DefKind::AssocFn | DefKind::Fn);
164-
165172
if (self.check_in_tests || !is_in_test(cx.tcx, node))
166173
&& let Some(current) = self.msrv.current(cx)
167174
&& let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id, needs_const)
@@ -190,16 +197,41 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
190197
match expr.kind {
191198
ExprKind::MethodCall(_, _, _, span) => {
192199
if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
193-
self.emit_lint_if_under_msrv(cx, method_did, expr.hir_id, span);
200+
self.emit_lint_if_under_msrv(
201+
cx,
202+
cx.enclosing_body.is_some() && is_in_const_context(cx),
203+
method_did,
204+
expr.hir_id,
205+
span,
206+
);
194207
}
195208
},
209+
ExprKind::Call(callee, _)
210+
if let ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) = callee.kind =>
211+
{
212+
self.called_path = Some(callee.hir_id);
213+
let needs_const = cx.enclosing_body.is_some() && is_in_const_context(cx);
214+
let def_id = if let Some(def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id() {
215+
def_id
216+
} else if needs_const && let ty::FnDef(def_id, _) = *cx.typeck_results().expr_ty(callee).kind() {
217+
// Edge case where a function is first assigned then called.
218+
// We previously would have warned for the non-const MSRV, when
219+
// checking the path, but now that it's called the const MSRV
220+
// must also be met.
221+
def_id
222+
} else {
223+
return;
224+
};
225+
self.emit_lint_if_under_msrv(cx, needs_const, def_id, expr.hir_id, callee.span);
226+
},
196227
// Desugaring into function calls by the compiler will use `QPath::LangItem` variants. Those should
197228
// not be linted as they will not be generated in older compilers if the function is not available,
198229
// and the compiler is allowed to call unstable functions.
199-
ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..))) => {
200-
if let Some(path_def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id() {
201-
self.emit_lint_if_under_msrv(cx, path_def_id, expr.hir_id, expr.span);
202-
}
230+
ExprKind::Path(qpath @ (QPath::Resolved(..) | QPath::TypeRelative(..)))
231+
if let Some(path_def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id()
232+
&& self.called_path != Some(expr.hir_id) =>
233+
{
234+
self.emit_lint_if_under_msrv(cx, false, path_def_id, expr.hir_id, expr.span);
203235
},
204236
_ => {},
205237
}
@@ -211,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
211243
// `CStr` and `CString` have been moved around but have been available since Rust 1.0.0
212244
&& !matches!(cx.tcx.get_diagnostic_name(ty_def_id), Some(sym::cstr_type | sym::cstring_type))
213245
{
214-
self.emit_lint_if_under_msrv(cx, ty_def_id, hir_ty.hir_id, hir_ty.span);
246+
self.emit_lint_if_under_msrv(cx, false, ty_def_id, hir_ty.hir_id, hir_ty.span);
215247
}
216248
}
217249
}

tests/ui/incompatible_msrv.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,14 @@ fn enum_variant_ok() {
168168
let _ = const { std::io::ErrorKind::InvalidFilename };
169169
}
170170

171+
#[clippy::msrv = "1.38.0"]
172+
const fn uncalled_len() {
173+
let _ = Vec::len;
174+
let x = str::len;
175+
let _ = x("");
176+
//~^ incompatible_msrv
177+
let _ = "".len();
178+
//~^ incompatible_msrv
179+
}
180+
171181
fn main() {}

tests/ui/incompatible_msrv.stderr

Lines changed: 18 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,24 @@
1-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.10.0`
2-
--> tests/ui/incompatible_msrv.rs:16:39
1+
error[E0283]: type annotations needed
2+
--> tests/ui/incompatible_msrv.rs:173:13
33
|
4-
LL | assert_eq!(map.entry("poneyland").key(), &"poneyland");
5-
| ^^^^^
4+
LL | let _ = Vec::len;
5+
| ^^^^^^^^ cannot infer type of the type parameter `A` declared on the struct `Vec`
66
|
7-
= note: `-D clippy::incompatible-msrv` implied by `-D warnings`
8-
= help: to override `-D warnings` add `#[allow(clippy::incompatible_msrv)]`
9-
10-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.12.0`
11-
--> tests/ui/incompatible_msrv.rs:22:11
12-
|
13-
LL | v.into_key();
14-
| ^^^^^^^^^^
15-
16-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.4.0`
17-
--> tests/ui/incompatible_msrv.rs:26:5
18-
|
19-
LL | sleep(Duration::new(1, 0));
20-
| ^^^^^
21-
22-
error: current MSRV (Minimum Supported Rust Version) is `1.2.0` but this item is stable since `1.3.0`
23-
--> tests/ui/incompatible_msrv.rs:31:33
24-
|
25-
LL | static NO_BODY_BAD_MSRV: Option<Duration> = None;
26-
| ^^^^^^^^
27-
28-
error: current MSRV (Minimum Supported Rust Version) is `1.2.0` but this item is stable since `1.3.0`
29-
--> tests/ui/incompatible_msrv.rs:38:19
30-
|
31-
LL | let _: Option<Duration> = None;
32-
| ^^^^^^^^
33-
34-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.43.0`
35-
--> tests/ui/incompatible_msrv.rs:62:17
36-
|
37-
LL | let _ = core::iter::once_with(|| 0);
38-
| ^^^^^^^^^^^^^^^^^^^^^
39-
40-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.43.0`
41-
--> tests/ui/incompatible_msrv.rs:69:21
42-
|
43-
LL | let _ = core::iter::once_with(|| $msg);
44-
| ^^^^^^^^^^^^^^^^^^^^^
45-
...
46-
LL | my_panic!("foo");
47-
| ---------------- in this macro invocation
48-
|
49-
= note: this error originates in the macro `my_panic` (in Nightly builds, run with -Z macro-backtrace for more info)
50-
51-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.43.0`
52-
--> tests/ui/incompatible_msrv.rs:76:13
53-
|
54-
LL | assert!(core::iter::once_with(|| 0).next().is_some());
55-
| ^^^^^^^^^^^^^^^^^^^^^
56-
57-
error: current MSRV (Minimum Supported Rust Version) is `1.80.0` but this item is stable since `1.82.0`
58-
--> tests/ui/incompatible_msrv.rs:89:13
7+
= note: cannot satisfy `_: std::alloc::Allocator`
8+
= help: the following types implement trait `std::alloc::Allocator`:
9+
&A
10+
std::alloc::Global
11+
std::alloc::System
12+
note: required by a bound in `std::vec::Vec`
13+
--> F:/.rustup/toolchains/nightly-2025-09-18-x86_64-pc-windows-msvc/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:434:78
5914
|
60-
LL | let _ = std::iter::repeat_n((), 5);
61-
| ^^^^^^^^^^^^^^^^^^^
62-
63-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.82.0`
64-
--> tests/ui/incompatible_msrv.rs:100:13
65-
|
66-
LL | let _ = std::iter::repeat_n((), 5);
67-
| ^^^^^^^^^^^^^^^^^^^
68-
69-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.82.0`
70-
--> tests/ui/incompatible_msrv.rs:105:17
71-
|
72-
LL | let _ = std::iter::repeat_n((), 5);
73-
| ^^^^^^^^^^^^^^^^^^^
74-
|
75-
= note: you may want to conditionally increase the MSRV considered by Clippy using the `clippy::msrv` attribute
76-
77-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.82.0`
78-
--> tests/ui/incompatible_msrv.rs:110:17
79-
|
80-
LL | let _ = std::iter::repeat_n((), 5);
81-
| ^^^^^^^^^^^^^^^^^^^
82-
83-
error: current MSRV (Minimum Supported Rust Version) is `1.78.0` but this item is stable since `1.84.0`
84-
--> tests/ui/incompatible_msrv.rs:123:7
85-
|
86-
LL | r.isqrt()
87-
| ^^^^^^^
88-
89-
error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.85.0`
90-
--> tests/ui/incompatible_msrv.rs:128:13
91-
|
92-
LL | let _ = std::io::ErrorKind::CrossesDevices;
93-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
94-
95-
error: current MSRV (Minimum Supported Rust Version) is `1.87.0` but this item is stable in a `const` context since `1.88.0`
96-
--> tests/ui/incompatible_msrv.rs:140:15
97-
|
98-
LL | _ = c.get();
99-
| ^^^^^
100-
101-
error: current MSRV (Minimum Supported Rust Version) is `1.86.0` but this item is stable since `1.87.0`
102-
--> tests/ui/incompatible_msrv.rs:159:13
103-
|
104-
LL | let _ = std::io::ErrorKind::InvalidFilename;
105-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
106-
107-
error: current MSRV (Minimum Supported Rust Version) is `1.86.0` but this item is stable since `1.87.0`
108-
--> tests/ui/incompatible_msrv.rs:161:21
15+
LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
16+
| ^^^^^^^^^ required by this bound in `Vec`
17+
help: consider specifying the generic arguments
10918
|
110-
LL | let _ = const { std::io::ErrorKind::InvalidFilename };
111-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
LL | let _ = Vec::<T, A>::len;
20+
| ++++++++
11221

113-
error: aborting due to 17 previous errors
22+
error: aborting due to 1 previous error
11423

24+
For more information about this error, try `rustc --explain E0283`.

0 commit comments

Comments
 (0)