Auto merge of #4450 - phansch:fix_const_fn_fp, r=flip1995

Fix missing_const_for_fn false positive

We don't want to lint if the type of the method implements drop.
(constant functions cannot evaluate destructors)

changelog: Fix `missing_const_for_fn` false positive

Fixes #4449
This commit is contained in:
bors 2019-08-29 17:24:21 +00:00
commit 28a8a6a483
4 changed files with 63 additions and 4 deletions

View File

@ -1,10 +1,11 @@
use crate::utils::{is_entrypoint_fn, span_lint, trait_ref_of_method};
use crate::utils::{has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
use rustc::hir;
use rustc::hir::intravisit::FnKind;
use rustc::hir::{Body, Constness, FnDecl, HirId};
use rustc::hir::{Body, Constness, FnDecl, HirId, HirVec};
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn;
use rustc_typeck::hir_ty_to_ty;
use syntax_pos::Span;
declare_clippy_lint! {
@ -94,7 +95,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn {
}
},
FnKind::Method(_, sig, ..) => {
if trait_ref_of_method(cx, hir_id).is_some() || already_const(sig.header) {
if trait_ref_of_method(cx, hir_id).is_some()
|| already_const(sig.header)
|| method_accepts_dropable(cx, &sig.decl.inputs)
{
return;
}
},
@ -113,6 +117,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn {
}
}
/// 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_dropable(cx: &LateContext<'_, '_>, param_tys: &HirVec<hir::Ty>) -> bool {
// If any of the params are dropable, return true
param_tys.iter().any(|hir_ty| {
let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty);
has_drop(cx, ty_ty)
})
}
// We don't have to lint on something that's already `const`
fn already_const(header: hir::FnHeader) -> bool {
header.constness == Constness::Const

View File

@ -68,3 +68,25 @@ impl std::ops::Add for Point {
Point(self.0 + other.0, self.1 + other.1)
}
}
mod with_drop {
pub struct A;
pub struct B;
impl Drop for A {
fn drop(&mut self) {}
}
impl A {
// This can not be const because the type implements `Drop`.
pub fn a(self) -> B {
B
}
}
impl B {
// This can not be const because `a` implements `Drop`.
pub fn a(self, a: A) -> B {
B
}
}
}

View File

@ -53,5 +53,20 @@ fn generic_arr<T: Copy>(t: [T; 1]) -> T {
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 fn b(self, a: &A) -> B {
B
}
}
}
// Should not be const
fn main() {}

View File

@ -49,5 +49,13 @@ LL | | t
LL | | }
| |_^
error: aborting due to 6 previous errors
error: this could be a const_fn
--> $DIR/could_be_const.rs:65:9
|
LL | / pub fn b(self, a: &A) -> B {
LL | | B
LL | | }
| |_________^
error: aborting due to 7 previous errors