Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[missing_const_for_fn]: add machine-applicable suggestion #12930

Merged
merged 1 commit into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions clippy_lints/src/missing_const_for_fn.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
use rustc_hir as hir;
use rustc_errors::Applicability;
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind};
use rustc_hir::{self as hir, Body, Constness, FnDecl, GenericParamKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::impl_lint_pass;
Expand Down Expand Up @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
}
},
FnKind::Method(_, sig, ..) => {
if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) {
if already_const(sig.header) || trait_ref_of_method(cx, def_id).is_some() {
return;
}
},
Expand All @@ -147,10 +147,22 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {

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

if let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
if let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv)
&& let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) =
cx.tcx.hir_node_by_def_id(def_id)
{
let suggestion = if vis_span.is_empty() { "const " } else { " const" };
span_lint_and_then(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`", |diag| {
diag.span_suggestion_verbose(
vis_span.shrink_to_hi(),
"make the function `const`",
suggestion,
Applicability::MachineApplicable,
);
});
}
}

extract_msrv_attr!(LateContext);
}

Expand Down
173 changes: 173 additions & 0 deletions tests/ui/missing_const_for_fn/could_be_const.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#![warn(clippy::missing_const_for_fn)]
#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)]
#![feature(const_mut_refs)]
#![feature(const_trait_impl)]

use std::mem::transmute;

struct Game {
guess: i32,
}

impl Game {
// Could be const
pub const fn new() -> Self {
//~^ ERROR: this could be a `const fn`
//~| NOTE: `-D clippy::missing-const-for-fn` implied by `-D warnings`
Self { guess: 42 }
}

const fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
//~^ ERROR: this could be a `const fn`
b
}
}

// Could be const
const fn one() -> i32 {
//~^ ERROR: this could be a `const fn`
1
}

// Could also be const
const fn two() -> i32 {
//~^ ERROR: this could be a `const fn`
let abc = 2;
abc
}

// Could be const (since Rust 1.39)
const fn string() -> String {
//~^ ERROR: this could be a `const fn`
String::new()
}

// Could be const
const unsafe fn four() -> i32 {
//~^ ERROR: this could be a `const fn`
4
}

// Could also be const
const fn generic<T>(t: T) -> T {
//~^ ERROR: this could be a `const fn`
t
}

fn sub(x: u32) -> usize {
unsafe { transmute(&x) }
}

const fn generic_arr<T: Copy>(t: [T; 1]) -> T {
//~^ ERROR: this could be a `const fn`
t[0]
}

mod with_drop {
pub struct A;
pub struct B;
impl Drop for A {
fn drop(&mut self) {}
}

impl B {
// This can be const, because `a` is passed by reference
pub const fn b(self, a: &A) -> B {
//~^ ERROR: this could be a `const fn`
B
}
}
}

#[clippy::msrv = "1.47.0"]
mod const_fn_stabilized_before_msrv {
// This could be const because `u8::is_ascii_digit` is a stable const function in 1.47.
const fn const_fn_stabilized_before_msrv(byte: u8) {
//~^ ERROR: this could be a `const fn`
byte.is_ascii_digit();
}
}

#[clippy::msrv = "1.45"]
fn msrv_1_45() -> i32 {
45
}

#[clippy::msrv = "1.46"]
const fn msrv_1_46() -> i32 {
//~^ ERROR: this could be a `const fn`
46
}

// Should not be const
fn main() {}

struct D;

impl const Drop for D {
fn drop(&mut self) {
todo!();
}
}

// Lint this, since it can be dropped in const contexts
// FIXME(effects)
fn d(this: D) {}

mod msrv {
struct Foo(*const u8, &'static u8);

impl Foo {
#[clippy::msrv = "1.58"]
const fn deref_ptr_can_be_const(self) -> usize {
//~^ ERROR: this could be a `const fn`
unsafe { *self.0 as usize }
}

const fn deref_copied_val(self) -> usize {
//~^ ERROR: this could be a `const fn`
*self.1 as usize
}
}

union Bar {
val: u8,
}

#[clippy::msrv = "1.56"]
const fn union_access_can_be_const() {
//~^ ERROR: this could be a `const fn`
let bar = Bar { val: 1 };
let _ = unsafe { bar.val };
}
}

mod issue12677 {
pub struct Wrapper {
pub strings: Vec<String>,
}

impl Wrapper {
#[must_use]
pub const fn new(strings: Vec<String>) -> Self {
Self { strings }
}

#[must_use]
pub const fn empty() -> Self {
Self { strings: Vec::new() }
}
}

pub struct Other {
pub text: String,
pub vec: Vec<String>,
}

impl Other {
pub const fn new(text: String) -> Self {
let vec = Vec::new();
Self { text, vec }
}
}
}
Loading