Skip to content

Commit 26a1989

Browse files
committed
Fix a FP in missing_const_for_fn
where a function that calls a standard library function whose constness is unstable is considered as being able to be a const function
1 parent e9728b8 commit 26a1989

File tree

7 files changed

+92
-14
lines changed

7 files changed

+92
-14
lines changed

clippy_lints/src/missing_const_for_fn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
138138

139139
let mir = cx.tcx.optimized_mir(def_id);
140140

141-
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) {
141+
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
142142
if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
143143
cx.tcx.sess.span_err(span, &err);
144144
}

clippy_utils/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
1010
extern crate rustc_ast;
1111
extern crate rustc_ast_pretty;
12+
extern crate rustc_attr;
1213
extern crate rustc_data_structures;
1314
extern crate rustc_errors;
1415
extern crate rustc_hir;

clippy_utils/src/qualify_min_const_fn.rs

+36-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// This code used to be a part of `rustc` but moved to Clippy as a result of
2+
// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
3+
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
4+
// differ from the time of `rustc` even if the name stays the same.
5+
16
use rustc_hir as hir;
27
use rustc_hir::def_id::DefId;
38
use rustc_middle::mir::{
@@ -6,14 +11,15 @@ use rustc_middle::mir::{
611
};
712
use rustc_middle::ty::subst::GenericArgKind;
813
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
14+
use rustc_semver::RustcVersion;
915
use rustc_span::symbol::sym;
1016
use rustc_span::Span;
1117
use rustc_target::spec::abi::Abi::RustIntrinsic;
1218
use std::borrow::Cow;
1319

1420
type McfResult = Result<(), (Span, Cow<'static, str>)>;
1521

16-
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
22+
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
1723
let def_id = body.source.def_id();
1824
let mut current = def_id;
1925
loop {
@@ -70,7 +76,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
7076
)?;
7177

7278
for bb in body.basic_blocks() {
73-
check_terminator(tcx, body, bb.terminator())?;
79+
check_terminator(tcx, body, bb.terminator(), msrv)?;
7480
for stmt in &bb.statements {
7581
check_statement(tcx, body, def_id, stmt)?;
7682
}
@@ -268,7 +274,12 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t
268274
Ok(())
269275
}
270276

271-
fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
277+
fn check_terminator(
278+
tcx: TyCtxt<'tcx>,
279+
body: &'a Body<'tcx>,
280+
terminator: &Terminator<'tcx>,
281+
msrv: Option<&RustcVersion>,
282+
) -> McfResult {
272283
let span = terminator.source_info.span;
273284
match &terminator.kind {
274285
TerminatorKind::FalseEdge { .. }
@@ -305,7 +316,7 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
305316
} => {
306317
let fn_ty = func.ty(body, tcx);
307318
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
308-
if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
319+
if !is_const_fn(tcx, fn_def_id, msrv) {
309320
return Err((
310321
span,
311322
format!(
@@ -350,3 +361,24 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
350361
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
351362
}
352363
}
364+
365+
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
366+
rustc_mir::const_eval::is_const_fn(tcx, def_id)
367+
&& if let Some(const_stab) = tcx.lookup_const_stability(def_id) {
368+
if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
369+
// Checking MSRV is manually necessary because `rustc` has no such concept. This entire
370+
// function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
371+
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
372+
crate::meets_msrv(
373+
msrv,
374+
&RustcVersion::parse(&since.as_str())
375+
.expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
376+
)
377+
} else {
378+
// `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions.
379+
unreachable!();
380+
}
381+
} else {
382+
true
383+
}
384+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// This file provides a const function that is unstably const forever.
2+
3+
#![feature(staged_api)]
4+
#![stable(feature = "1", since = "1.0.0")]
5+
6+
#[stable(feature = "1", since = "1.0.0")]
7+
#[rustc_const_unstable(feature = "foo", issue = "none")]
8+
pub const fn unstably_const_fn() {}

tests/ui/missing_const_for_fn/cant_be_const.rs

+19
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22
//! compilation error.
33
//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
44
5+
// aux-build:helper.rs
6+
57
#![warn(clippy::missing_const_for_fn)]
68
#![allow(incomplete_features)]
79
#![feature(start, const_generics)]
10+
#![feature(custom_inner_attributes)]
11+
12+
extern crate helper;
813

914
struct Game;
1015

@@ -101,3 +106,17 @@ fn const_generic_return<T, const N: usize>(t: &[T]) -> &[T; N] {
101106

102107
unsafe { &*p }
103108
}
109+
110+
// Do not lint this because it calls a function whose constness is unstable.
111+
fn unstably_const_fn() {
112+
helper::unstably_const_fn()
113+
}
114+
115+
mod const_fn_stabilized_after_msrv {
116+
#![clippy::msrv = "1.46.0"]
117+
118+
// Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0.
119+
fn const_fn_stabilized_after_msrv(byte: u8) {
120+
byte.is_ascii_digit();
121+
}
122+
}

tests/ui/missing_const_for_fn/could_be_const.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![warn(clippy::missing_const_for_fn)]
22
#![allow(incomplete_features, clippy::let_and_return)]
33
#![feature(const_generics)]
4+
#![feature(custom_inner_attributes)]
45

56
use std::mem::transmute;
67

@@ -70,5 +71,14 @@ mod with_drop {
7071
}
7172
}
7273

74+
mod const_fn_stabilized_before_msrv {
75+
#![clippy::msrv = "1.47.0"]
76+
77+
// This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
78+
fn const_fn_stabilized_before_msrv(byte: u8) {
79+
byte.is_ascii_digit();
80+
}
81+
}
82+
7383
// Should not be const
7484
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: this could be a `const fn`
2-
--> $DIR/could_be_const.rs:13:5
2+
--> $DIR/could_be_const.rs:14:5
33
|
44
LL | / pub fn new() -> Self {
55
LL | | Self { guess: 42 }
@@ -9,23 +9,23 @@ LL | | }
99
= note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
1010

1111
error: this could be a `const fn`
12-
--> $DIR/could_be_const.rs:17:5
12+
--> $DIR/could_be_const.rs:18:5
1313
|
1414
LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
1515
LL | | b
1616
LL | | }
1717
| |_____^
1818

1919
error: this could be a `const fn`
20-
--> $DIR/could_be_const.rs:23:1
20+
--> $DIR/could_be_const.rs:24:1
2121
|
2222
LL | / fn one() -> i32 {
2323
LL | | 1
2424
LL | | }
2525
| |_^
2626

2727
error: this could be a `const fn`
28-
--> $DIR/could_be_const.rs:28:1
28+
--> $DIR/could_be_const.rs:29:1
2929
|
3030
LL | / fn two() -> i32 {
3131
LL | | let abc = 2;
@@ -34,36 +34,44 @@ LL | | }
3434
| |_^
3535

3636
error: this could be a `const fn`
37-
--> $DIR/could_be_const.rs:34:1
37+
--> $DIR/could_be_const.rs:35:1
3838
|
3939
LL | / fn string() -> String {
4040
LL | | String::new()
4141
LL | | }
4242
| |_^
4343

4444
error: this could be a `const fn`
45-
--> $DIR/could_be_const.rs:39:1
45+
--> $DIR/could_be_const.rs:40:1
4646
|
4747
LL | / unsafe fn four() -> i32 {
4848
LL | | 4
4949
LL | | }
5050
| |_^
5151

5252
error: this could be a `const fn`
53-
--> $DIR/could_be_const.rs:44:1
53+
--> $DIR/could_be_const.rs:45:1
5454
|
5555
LL | / fn generic<T>(t: T) -> T {
5656
LL | | t
5757
LL | | }
5858
| |_^
5959

6060
error: this could be a `const fn`
61-
--> $DIR/could_be_const.rs:67:9
61+
--> $DIR/could_be_const.rs:68:9
6262
|
6363
LL | / pub fn b(self, a: &A) -> B {
6464
LL | | B
6565
LL | | }
6666
| |_________^
6767

68-
error: aborting due to 8 previous errors
68+
error: this could be a `const fn`
69+
--> $DIR/could_be_const.rs:78:5
70+
|
71+
LL | / fn const_fn_stabilized_before_msrv(byte: u8) {
72+
LL | | byte.is_ascii_digit();
73+
LL | | }
74+
| |_____^
75+
76+
error: aborting due to 9 previous errors
6977

0 commit comments

Comments
 (0)