Skip to content

Commit

Permalink
Check for 'static lifetime in return type
Browse files Browse the repository at this point in the history
  • Loading branch information
tesuji committed Jul 25, 2024
1 parent e53182a commit 0bc9f00
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 18 deletions.
21 changes: 18 additions & 3 deletions clippy_lints/src/eta_reduction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{
self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TypeVisitableExt,
TypeckResults,
self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults,
};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym;
Expand Down Expand Up @@ -176,6 +175,17 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
}
},
};
if let Some(outer) = outer_receiver
&& ty_has_static(sig.output())
&& let generic_args = typeck.node_args(outer.hir_id)
// HACK: Given a closure in `T.method(|| f())`, where `fn f() -> U where U: 'static`, `T.method(f)`
// will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when
// T is a generic type. For example, return type of `Option<String>::as_deref()` is a generic.
// So we have a hack like this.
&& generic_args.len() > 0
{
return;
}
if check_sig(closure_sig, sig)
&& let generic_args = typeck.node_args(callee.hir_id)
// Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not
Expand Down Expand Up @@ -275,7 +285,7 @@ fn check_sig<'tcx>(closure_sig: FnSig<'tcx>, call_sig: FnSig<'tcx>) -> bool {
/// This is needed because rustc is unable to late bind early-bound regions in a function signature.
fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool {
fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool {
matches!(from_region.kind(), RegionKind::ReBound(..)) && !matches!(to_region.kind(), RegionKind::ReBound(..))
from_region.is_bound() && !to_region.is_bound()
}

fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool {
Expand Down Expand Up @@ -328,3 +338,8 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'
.zip(to_sig.inputs_and_output)
.any(|(from_ty, to_ty)| check_ty(from_ty, to_ty))
}

fn ty_has_static(ty: Ty<'_>) -> bool {
ty.walk()
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if re.is_static()))
}
4 changes: 2 additions & 2 deletions tests/ui/eta.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,9 @@ mod issue_13073 {
pub fn foo() {
// shouldn't lint
let bind: Option<String> = None;
let _field = bind.as_deref().or_else(get_default).unwrap();
let _field = bind.as_deref().or_else(|| get_default()).unwrap();
let bind: Option<&'static str> = None;
let _field = bind.as_deref().or_else(get_default).unwrap();
let _field = bind.as_deref().or_else(|| get_default()).unwrap();
// should lint
let _field = bind.or_else(get_default).unwrap();
}
Expand Down
14 changes: 1 addition & 13 deletions tests/ui/eta.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -202,23 +202,11 @@ error: redundant closure
LL | let x = Box::new(|| None.map(|x| f(x)));
| ^^^^^^^^ help: replace the closure with the function itself: `f`

error: redundant closure
--> tests/ui/eta.rs:495:46
|
LL | let _field = bind.as_deref().or_else(|| get_default()).unwrap();
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default`

error: redundant closure
--> tests/ui/eta.rs:497:46
|
LL | let _field = bind.as_deref().or_else(|| get_default()).unwrap();
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default`

error: redundant closure
--> tests/ui/eta.rs:499:35
|
LL | let _field = bind.or_else(|| get_default()).unwrap();
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default`

error: aborting due to 36 previous errors
error: aborting due to 34 previous errors

0 comments on commit 0bc9f00

Please sign in to comment.