From 0bc9f003a3fdf2d860647a22768297aaa07611e4 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 17 Jul 2024 11:30:29 +0700 Subject: [PATCH] Check for 'static lifetime in return type --- clippy_lints/src/eta_reduction.rs | 21 ++++++++++++++++++--- tests/ui/eta.fixed | 4 ++-- tests/ui/eta.stderr | 14 +------------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index d03ae101823..5a7226d590c 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -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; @@ -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::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 @@ -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 { @@ -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())) +} diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 5323425de47..ca422ee29c1 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -492,9 +492,9 @@ mod issue_13073 { pub fn foo() { // shouldn't lint let bind: Option = 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(); } diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index d35c37af37c..5540261fc57 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -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