mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
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
This commit is contained in:
parent
e9728b80ce
commit
26a1989041
@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
|||||||
|
|
||||||
let mir = cx.tcx.optimized_mir(def_id);
|
let mir = cx.tcx.optimized_mir(def_id);
|
||||||
|
|
||||||
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) {
|
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
|
||||||
if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
|
if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
|
||||||
cx.tcx.sess.span_err(span, &err);
|
cx.tcx.sess.span_err(span, &err);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
|
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
|
||||||
extern crate rustc_ast;
|
extern crate rustc_ast;
|
||||||
extern crate rustc_ast_pretty;
|
extern crate rustc_ast_pretty;
|
||||||
|
extern crate rustc_attr;
|
||||||
extern crate rustc_data_structures;
|
extern crate rustc_data_structures;
|
||||||
extern crate rustc_errors;
|
extern crate rustc_errors;
|
||||||
extern crate rustc_hir;
|
extern crate rustc_hir;
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
// This code used to be a part of `rustc` but moved to Clippy as a result of
|
||||||
|
// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
|
||||||
|
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
|
||||||
|
// differ from the time of `rustc` even if the name stays the same.
|
||||||
|
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
@ -6,6 +11,7 @@ use rustc_middle::mir::{
|
|||||||
};
|
};
|
||||||
use rustc_middle::ty::subst::GenericArgKind;
|
use rustc_middle::ty::subst::GenericArgKind;
|
||||||
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
|
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
|
||||||
|
use rustc_semver::RustcVersion;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_target::spec::abi::Abi::RustIntrinsic;
|
use rustc_target::spec::abi::Abi::RustIntrinsic;
|
||||||
@ -13,7 +19,7 @@ use std::borrow::Cow;
|
|||||||
|
|
||||||
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
||||||
|
|
||||||
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
|
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
|
||||||
let def_id = body.source.def_id();
|
let def_id = body.source.def_id();
|
||||||
let mut current = def_id;
|
let mut current = def_id;
|
||||||
loop {
|
loop {
|
||||||
@ -70,7 +76,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
for bb in body.basic_blocks() {
|
for bb in body.basic_blocks() {
|
||||||
check_terminator(tcx, body, bb.terminator())?;
|
check_terminator(tcx, body, bb.terminator(), msrv)?;
|
||||||
for stmt in &bb.statements {
|
for stmt in &bb.statements {
|
||||||
check_statement(tcx, body, def_id, stmt)?;
|
check_statement(tcx, body, def_id, stmt)?;
|
||||||
}
|
}
|
||||||
@ -268,7 +274,12 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
|
fn check_terminator(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
body: &'a Body<'tcx>,
|
||||||
|
terminator: &Terminator<'tcx>,
|
||||||
|
msrv: Option<&RustcVersion>,
|
||||||
|
) -> McfResult {
|
||||||
let span = terminator.source_info.span;
|
let span = terminator.source_info.span;
|
||||||
match &terminator.kind {
|
match &terminator.kind {
|
||||||
TerminatorKind::FalseEdge { .. }
|
TerminatorKind::FalseEdge { .. }
|
||||||
@ -305,7 +316,7 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
|
|||||||
} => {
|
} => {
|
||||||
let fn_ty = func.ty(body, tcx);
|
let fn_ty = func.ty(body, tcx);
|
||||||
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
|
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
|
||||||
if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
|
if !is_const_fn(tcx, fn_def_id, msrv) {
|
||||||
return Err((
|
return Err((
|
||||||
span,
|
span,
|
||||||
format!(
|
format!(
|
||||||
@ -350,3 +361,24 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
|
|||||||
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
|
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
|
||||||
|
rustc_mir::const_eval::is_const_fn(tcx, def_id)
|
||||||
|
&& if let Some(const_stab) = tcx.lookup_const_stability(def_id) {
|
||||||
|
if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
|
||||||
|
// Checking MSRV is manually necessary because `rustc` has no such concept. This entire
|
||||||
|
// function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
|
||||||
|
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
|
||||||
|
crate::meets_msrv(
|
||||||
|
msrv,
|
||||||
|
&RustcVersion::parse(&since.as_str())
|
||||||
|
.expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions.
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
8
tests/ui/missing_const_for_fn/auxiliary/helper.rs
Normal file
8
tests/ui/missing_const_for_fn/auxiliary/helper.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// This file provides a const function that is unstably const forever.
|
||||||
|
|
||||||
|
#![feature(staged_api)]
|
||||||
|
#![stable(feature = "1", since = "1.0.0")]
|
||||||
|
|
||||||
|
#[stable(feature = "1", since = "1.0.0")]
|
||||||
|
#[rustc_const_unstable(feature = "foo", issue = "none")]
|
||||||
|
pub const fn unstably_const_fn() {}
|
@ -2,9 +2,14 @@
|
|||||||
//! compilation error.
|
//! compilation error.
|
||||||
//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
|
//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
|
||||||
|
|
||||||
|
// aux-build:helper.rs
|
||||||
|
|
||||||
#![warn(clippy::missing_const_for_fn)]
|
#![warn(clippy::missing_const_for_fn)]
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(start, const_generics)]
|
#![feature(start, const_generics)]
|
||||||
|
#![feature(custom_inner_attributes)]
|
||||||
|
|
||||||
|
extern crate helper;
|
||||||
|
|
||||||
struct Game;
|
struct Game;
|
||||||
|
|
||||||
@ -101,3 +106,17 @@ fn const_generic_return<T, const N: usize>(t: &[T]) -> &[T; N] {
|
|||||||
|
|
||||||
unsafe { &*p }
|
unsafe { &*p }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not lint this because it calls a function whose constness is unstable.
|
||||||
|
fn unstably_const_fn() {
|
||||||
|
helper::unstably_const_fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
mod const_fn_stabilized_after_msrv {
|
||||||
|
#![clippy::msrv = "1.46.0"]
|
||||||
|
|
||||||
|
// Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0.
|
||||||
|
fn const_fn_stabilized_after_msrv(byte: u8) {
|
||||||
|
byte.is_ascii_digit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![warn(clippy::missing_const_for_fn)]
|
#![warn(clippy::missing_const_for_fn)]
|
||||||
#![allow(incomplete_features, clippy::let_and_return)]
|
#![allow(incomplete_features, clippy::let_and_return)]
|
||||||
#![feature(const_generics)]
|
#![feature(const_generics)]
|
||||||
|
#![feature(custom_inner_attributes)]
|
||||||
|
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
|
|
||||||
@ -70,5 +71,14 @@ mod with_drop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod const_fn_stabilized_before_msrv {
|
||||||
|
#![clippy::msrv = "1.47.0"]
|
||||||
|
|
||||||
|
// This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
|
||||||
|
fn const_fn_stabilized_before_msrv(byte: u8) {
|
||||||
|
byte.is_ascii_digit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Should not be const
|
// Should not be const
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error: this could be a `const fn`
|
error: this could be a `const fn`
|
||||||
--> $DIR/could_be_const.rs:13:5
|
--> $DIR/could_be_const.rs:14:5
|
||||||
|
|
|
|
||||||
LL | / pub fn new() -> Self {
|
LL | / pub fn new() -> Self {
|
||||||
LL | | Self { guess: 42 }
|
LL | | Self { guess: 42 }
|
||||||
@ -9,7 +9,7 @@ LL | | }
|
|||||||
= note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
|
= note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
|
||||||
|
|
||||||
error: this could be a `const fn`
|
error: this could be a `const fn`
|
||||||
--> $DIR/could_be_const.rs:17:5
|
--> $DIR/could_be_const.rs:18:5
|
||||||
|
|
|
|
||||||
LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
|
LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
|
||||||
LL | | b
|
LL | | b
|
||||||
@ -17,7 +17,7 @@ LL | | }
|
|||||||
| |_____^
|
| |_____^
|
||||||
|
|
||||||
error: this could be a `const fn`
|
error: this could be a `const fn`
|
||||||
--> $DIR/could_be_const.rs:23:1
|
--> $DIR/could_be_const.rs:24:1
|
||||||
|
|
|
|
||||||
LL | / fn one() -> i32 {
|
LL | / fn one() -> i32 {
|
||||||
LL | | 1
|
LL | | 1
|
||||||
@ -25,7 +25,7 @@ LL | | }
|
|||||||
| |_^
|
| |_^
|
||||||
|
|
||||||
error: this could be a `const fn`
|
error: this could be a `const fn`
|
||||||
--> $DIR/could_be_const.rs:28:1
|
--> $DIR/could_be_const.rs:29:1
|
||||||
|
|
|
|
||||||
LL | / fn two() -> i32 {
|
LL | / fn two() -> i32 {
|
||||||
LL | | let abc = 2;
|
LL | | let abc = 2;
|
||||||
@ -34,7 +34,7 @@ LL | | }
|
|||||||
| |_^
|
| |_^
|
||||||
|
|
||||||
error: this could be a `const fn`
|
error: this could be a `const fn`
|
||||||
--> $DIR/could_be_const.rs:34:1
|
--> $DIR/could_be_const.rs:35:1
|
||||||
|
|
|
|
||||||
LL | / fn string() -> String {
|
LL | / fn string() -> String {
|
||||||
LL | | String::new()
|
LL | | String::new()
|
||||||
@ -42,7 +42,7 @@ LL | | }
|
|||||||
| |_^
|
| |_^
|
||||||
|
|
||||||
error: this could be a `const fn`
|
error: this could be a `const fn`
|
||||||
--> $DIR/could_be_const.rs:39:1
|
--> $DIR/could_be_const.rs:40:1
|
||||||
|
|
|
|
||||||
LL | / unsafe fn four() -> i32 {
|
LL | / unsafe fn four() -> i32 {
|
||||||
LL | | 4
|
LL | | 4
|
||||||
@ -50,7 +50,7 @@ LL | | }
|
|||||||
| |_^
|
| |_^
|
||||||
|
|
||||||
error: this could be a `const fn`
|
error: this could be a `const fn`
|
||||||
--> $DIR/could_be_const.rs:44:1
|
--> $DIR/could_be_const.rs:45:1
|
||||||
|
|
|
|
||||||
LL | / fn generic<T>(t: T) -> T {
|
LL | / fn generic<T>(t: T) -> T {
|
||||||
LL | | t
|
LL | | t
|
||||||
@ -58,12 +58,20 @@ LL | | }
|
|||||||
| |_^
|
| |_^
|
||||||
|
|
||||||
error: this could be a `const fn`
|
error: this could be a `const fn`
|
||||||
--> $DIR/could_be_const.rs:67:9
|
--> $DIR/could_be_const.rs:68:9
|
||||||
|
|
|
|
||||||
LL | / pub fn b(self, a: &A) -> B {
|
LL | / pub fn b(self, a: &A) -> B {
|
||||||
LL | | B
|
LL | | B
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_________^
|
| |_________^
|
||||||
|
|
||||||
error: aborting due to 8 previous errors
|
error: this could be a `const fn`
|
||||||
|
--> $DIR/could_be_const.rs:78:5
|
||||||
|
|
|
||||||
|
LL | / fn const_fn_stabilized_before_msrv(byte: u8) {
|
||||||
|
LL | | byte.is_ascii_digit();
|
||||||
|
LL | | }
|
||||||
|
| |_____^
|
||||||
|
|
||||||
|
error: aborting due to 9 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user