Skip to content

Commit

Permalink
Don't consider lifetimes in bounded types unused (fix `extra_unused_l…
Browse files Browse the repository at this point in the history
…ifetimes` FP) (#13583)

Fixes #13578

r? @Alexendoo

changelog: don't consider lifetimes in bounded types unused (fix
`extra_unused_lifetimes` FP #13578)
  • Loading branch information
Alexendoo authored Nov 19, 2024
2 parents 53994bd + eaec0ca commit 10677c3
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 8 deletions.
56 changes: 48 additions & 8 deletions clippy_lints/src/lifetimes.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::trait_ref_of_method;
use itertools::Itertools;
use rustc_ast::visit::{try_visit, walk_list};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::Applicability;
use rustc_hir::FnRetTy::Return;
Expand All @@ -11,8 +12,8 @@ use rustc_hir::intravisit::{
};
use rustc_hir::{
BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics,
Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, lang_items,
HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate, lang_items,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
Expand Down Expand Up @@ -483,18 +484,23 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
struct Usage {
lifetime: Lifetime,
in_where_predicate: bool,
in_bounded_ty: bool,
in_generics_arg: bool,
}

struct LifetimeChecker<'cx, 'tcx, F> {
cx: &'cx LateContext<'tcx>,
map: FxIndexMap<LocalDefId, Vec<Usage>>,
where_predicate_depth: usize,
bounded_ty_depth: usize,
generic_args_depth: usize,
phantom: std::marker::PhantomData<F>,
}

impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F>
where
F: NestedFilter<'tcx>,
{
fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'_>) -> LifetimeChecker<'cx, 'tcx, F> {
let map = generics
.params
Expand All @@ -510,10 +516,30 @@ impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
cx,
map,
where_predicate_depth: 0,
bounded_ty_depth: 0,
generic_args_depth: 0,
phantom: std::marker::PhantomData,
}
}

// `visit_where_bound_predicate` is based on:
// https://github.com/rust-lang/rust/blob/864cee3ea383cc8254ba394ba355e648faa9cfa5/compiler/rustc_hir/src/intravisit.rs#L936-L939
fn visit_where_bound_predicate(
&mut self,
hir_id: HirId,
bounded_ty: &'tcx Ty<'tcx>,
bounds: &'tcx [GenericBound<'tcx>],
bound_generic_params: &'tcx [GenericParam<'tcx>],
) {
try_visit!(self.visit_id(hir_id));

self.bounded_ty_depth += 1;
try_visit!(self.visit_ty(bounded_ty));
self.bounded_ty_depth -= 1;

walk_list!(self, visit_param_bound, bounds);
walk_list!(self, visit_generic_param, bound_generic_params);
}
}

impl<'tcx, F> Visitor<'tcx> for LifetimeChecker<'_, 'tcx, F>
Expand All @@ -531,14 +557,27 @@ where
usages.push(Usage {
lifetime: *lifetime,
in_where_predicate: self.where_predicate_depth != 0,
in_bounded_ty: self.bounded_ty_depth != 0,
in_generics_arg: self.generic_args_depth != 0,
});
}
}

fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
self.where_predicate_depth += 1;
walk_where_predicate(self, predicate);
if let &WherePredicate::BoundPredicate(WhereBoundPredicate {
hir_id,
bounded_ty,
bounds,
bound_generic_params,
origin: _,
span: _,
}) = predicate
{
self.visit_where_bound_predicate(hir_id, bounded_ty, bounds, bound_generic_params);
} else {
walk_where_predicate(self, predicate);
}
self.where_predicate_depth -= 1;
}

Expand All @@ -562,7 +601,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>,
for (def_id, usages) in checker.map {
if usages
.iter()
.all(|usage| usage.in_where_predicate && !usage.in_generics_arg)
.all(|usage| usage.in_where_predicate && !usage.in_bounded_ty && !usage.in_generics_arg)
{
span_lint(
cx,
Expand All @@ -589,7 +628,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
for (&def_id, usages) in &checker.map {
if usages
.iter()
.all(|usage| usage.in_where_predicate && !usage.in_generics_arg)
.all(|usage| usage.in_where_predicate && !usage.in_bounded_ty && !usage.in_generics_arg)
{
span_lint(
cx,
Expand All @@ -605,8 +644,8 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'

// An `impl` lifetime is elidable if it satisfies the following conditions:
// - It is used exactly once.
// - That single use is not in `GenericArgs` in a `WherePredicate`. (Note that `GenericArgs` are
// different from `GenericParam`s.)
// - That single use is not in a bounded type or `GenericArgs` in a `WherePredicate`. (Note that
// `GenericArgs` are different from `GenericParam`s.)
fn report_elidable_impl_lifetimes<'tcx>(
cx: &LateContext<'tcx>,
impl_: &'tcx Impl<'_>,
Expand All @@ -623,6 +662,7 @@ fn report_elidable_impl_lifetimes<'tcx>(
}
| Usage {
lifetime,
in_bounded_ty: false,
in_generics_arg: false,
..
},
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/extra_unused_lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,11 @@ struct Human<'a> {
pub name: &'a str,
}

// https://github.com/rust-lang/rust-clippy/issues/13578
mod issue_13578 {
pub trait Foo {}

impl<'a, T: 'a> Foo for Option<T> where &'a T: Foo {}
}

fn main() {}

0 comments on commit 10677c3

Please sign in to comment.