Skip to content

Commit

Permalink
Auto merge of #4450 - phansch:fix_const_fn_fp, r=flip1995
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bors committed Aug 29, 2019
2 parents 888b736 + 5e1fdf9 commit 28a8a6a
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 4 deletions.
20 changes: 17 additions & 3 deletions clippy_lints/src/missing_const_for_fn.rs
@@ -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! {
Expand Down Expand Up @@ -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;
}
},
Expand All @@ -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
Expand Down
22 changes: 22 additions & 0 deletions tests/ui/missing_const_for_fn/cant_be_const.rs
Expand Up @@ -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
}
}
}
15 changes: 15 additions & 0 deletions tests/ui/missing_const_for_fn/could_be_const.rs
Expand Up @@ -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() {}
10 changes: 9 additions & 1 deletion tests/ui/missing_const_for_fn/could_be_const.stderr
Expand Up @@ -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

0 comments on commit 28a8a6a

Please sign in to comment.