Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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() {}