Skip to content

Commit

Permalink
Auto merge of rust-lang#12681 - y21:issue12677, r=Jarcho
Browse files Browse the repository at this point in the history
Let `qualify_min_const_fn` deal with drop terminators

Fixes rust-lang#12677

The `method_accepts_droppable` check that was there seemed overly conservative.
> Returns true if any of the method parameters is a type that implements `Drop`.
> The method can't be made const then, because `drop` can't be const-evaluated.

Accepting parameters that implement `Drop` should still be fine as long as the parameter isn't actually dropped, as is the case in the linked issue where the droppable is moved into the return place. This more accurate analysis ("is there a `drop` terminator") is already done by `qualify_min_const_fn` [here](https://github.com/rust-lang/rust-clippy/blob/f5e250180c342bb52808c9a934c962a8fe40afc7/clippy_utils/src/qualify_min_const_fn.rs#L298), so I don't think this additional check is really necessary?

Fixing the other, second case in the linked issue was only slightly more involved, since `Vec::new()` is a function call that has the ability to panic, so there must be a `drop()` terminator for cleanup, however we should be able to freely ignore that. [Const checking ignores cleanup blocks](https://github.com/rust-lang/rust/blob/master/compiler/rustc_const_eval/src/transform/check_consts/check.rs#L382-L388), so we should, too?

r? `@Jarcho`

----

changelog: [`missing_const_for_fn`]: continue linting on fns with parameters implementing `Drop` if they're not actually dropped
  • Loading branch information
bors committed Jun 11, 2024
2 parents 740b72e + c3d3a3f commit 38d12a9
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 23 deletions.
21 changes: 2 additions & 19 deletions clippy_lints/src/missing_const_for_fn.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::ty::has_drop;
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_hir::def_id::CRATE_DEF_ID;
Expand Down Expand Up @@ -121,10 +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)
|| method_accepts_droppable(cx, def_id)
{
if trait_ref_of_method(cx, def_id).is_some() || already_const(sig.header) {
return;
}
},
Expand All @@ -151,26 +147,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {

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

if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) {
if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
cx.tcx.dcx().span_err(span, err);
}
} else {
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`");
}
}
extract_msrv_attr!(LateContext);
}

/// Returns true if any of the method parameters is a type that implements `Drop`. The method
/// can't be made const then, because `drop` can't be const-evaluated.
fn method_accepts_droppable(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();

// If any of the params are droppable, return true
sig.inputs().iter().any(|&ty| has_drop(cx, ty))
}

// We don't have to lint on something that's already `const`
#[must_use]
fn already_const(header: hir::FnHeader) -> bool {
Expand Down
10 changes: 7 additions & 3 deletions clippy_utils/src/qualify_min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,13 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
)?;

for bb in &*body.basic_blocks {
check_terminator(tcx, body, bb.terminator(), msrv)?;
for stmt in &bb.statements {
check_statement(tcx, body, def_id, stmt, msrv)?;
// Cleanup blocks are ignored entirely by const eval, so we can too:
// https://github.com/rust-lang/rust/blob/1dea922ea6e74f99a0e97de5cdb8174e4dea0444/compiler/rustc_const_eval/src/transform/check_consts/check.rs#L382
if !bb.is_cleanup {
check_terminator(tcx, body, bb.terminator(), msrv)?;
for stmt in &bb.statements {
check_statement(tcx, body, def_id, stmt, msrv)?;
}
}
}
Ok(())
Expand Down
30 changes: 30 additions & 0 deletions tests/ui/missing_const_for_fn/could_be_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,33 @@ mod msrv {
let _ = unsafe { bar.val };
}
}

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

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

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

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

impl Other {
pub fn new(text: String) -> Self {
let vec = Vec::new();
Self { text, vec }
}
}
}
27 changes: 26 additions & 1 deletion tests/ui/missing_const_for_fn/could_be_const.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,30 @@ LL | | let _ = unsafe { bar.val };
LL | | }
| |_____^

error: aborting due to 14 previous errors
error: this could be a `const fn`
--> tests/ui/missing_const_for_fn/could_be_const.rs:152:9
|
LL | / pub fn new(strings: Vec<String>) -> Self {
LL | | Self { strings }
LL | | }
| |_________^

error: this could be a `const fn`
--> tests/ui/missing_const_for_fn/could_be_const.rs:157:9
|
LL | / pub fn empty() -> Self {
LL | | Self { strings: Vec::new() }
LL | | }
| |_________^

error: this could be a `const fn`
--> tests/ui/missing_const_for_fn/could_be_const.rs:168:9
|
LL | / pub fn new(text: String) -> Self {
LL | | let vec = Vec::new();
LL | | Self { text, vec }
LL | | }
| |_________^

error: aborting due to 17 previous errors

0 comments on commit 38d12a9

Please sign in to comment.