mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 15:14:29 +00:00
Auto merge of #12681 - y21:issue12677, r=Jarcho
Let `qualify_min_const_fn` deal with drop terminators
Fixes #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](f5e250180c/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
This commit is contained in:
commit
38d12a9bc0
4 changed files with 65 additions and 23 deletions
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue