Check for lifetime uses in closures as well

The `BodyLifetimeChecker` which checks for the use of any non-anonymous
non-static lifetime did not recurse into closures, missing lifetime
uses. This would lead to a bogus elision suggestion.

The `BodyLifetimeChecker` is not refined enough to avoid false
positives, as any conforming lifetime, including one coming from the outer
context, would be considered a hit. The number of false positives might
increase now that we check closures as well, in case those closures
define and use lifetimes themselves.
This commit is contained in:
Samuel Tardieu 2025-04-14 08:50:44 +02:00
parent 69ade776fa
commit 77b3ac3d57
3 changed files with 30 additions and 3 deletions

View file

@ -314,7 +314,7 @@ fn could_use_elision<'tcx>(
return None;
}
let mut checker = BodyLifetimeChecker;
let mut checker = BodyLifetimeChecker::new(cx);
if checker.visit_expr(body.value).is_break() {
return None;
}
@ -911,10 +911,23 @@ fn elision_suggestions(
Some(suggestions)
}
struct BodyLifetimeChecker;
struct BodyLifetimeChecker<'tcx> {
tcx: TyCtxt<'tcx>,
}
impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
impl<'tcx> BodyLifetimeChecker<'tcx> {
fn new(cx: &LateContext<'tcx>) -> Self {
Self { tcx: cx.tcx }
}
}
impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker<'tcx> {
type Result = ControlFlow<()>;
type NestedFilter = middle_nested_filter::OnlyBodies;
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.tcx
}
// for lifetimes as parameters of generics
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) -> ControlFlow<()> {
if !lifetime.is_anonymous() && lifetime.ident.name != kw::StaticLifetime {

View file

@ -534,4 +534,11 @@ mod issue13749bis {
impl<'a, T: 'a> Generic<T> {}
}
pub fn issue14607<'s>(x: &'s u8) {
#[expect(clippy::redundant_closure_call)]
(|| {
let _: &'s u8 = x;
})();
}
fn main() {}

View file

@ -534,4 +534,11 @@ mod issue13749bis {
impl<'a, T: 'a> Generic<T> {}
}
pub fn issue14607<'s>(x: &'s u8) {
#[expect(clippy::redundant_closure_call)]
(|| {
let _: &'s u8 = x;
})();
}
fn main() {}