about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Moelius <sam@moeli.us>2024-10-21 20:37:19 -0400
committerSamuel Moelius <sam@moeli.us>2024-11-18 20:20:19 -0500
commiteaec0cab7e302263e8791c2724c6f399ddb31ad1 (patch)
treec84d30981b817568f080122b93f6a4f48dba948d
parent53994bda923a81d1c12d3e32dab3c208beaaca63 (diff)
downloadrust-eaec0cab7e302263e8791c2724c6f399ddb31ad1.tar.gz
rust-eaec0cab7e302263e8791c2724c6f399ddb31ad1.zip
Fix 13578 (#13583)
changelog: don't consider lifetimes in bounded types unused (fix `extra_unused_lifetimes` FP #13578)
-rw-r--r--clippy_lints/src/lifetimes.rs56
-rw-r--r--tests/ui/extra_unused_lifetimes.rs7
2 files changed, 55 insertions, 8 deletions
diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs
index ce0e1a24a7b..bf898d0800b 100644
--- a/clippy_lints/src/lifetimes.rs
+++ b/clippy_lints/src/lifetimes.rs
@@ -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;
@@ -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;
@@ -483,6 +484,7 @@ 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,
 }
 
@@ -490,11 +492,15 @@ 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
@@ -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>
@@ -531,6 +557,7 @@ 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,
             });
         }
@@ -538,7 +565,19 @@ where
 
     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;
     }
 
@@ -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,
@@ -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,
@@ -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<'_>,
@@ -623,6 +662,7 @@ fn report_elidable_impl_lifetimes<'tcx>(
                 }
                 | Usage {
                     lifetime,
+                    in_bounded_ty: false,
                     in_generics_arg: false,
                     ..
                 },
diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs
index 17d2ed9f50c..aa964af3fc2 100644
--- a/tests/ui/extra_unused_lifetimes.rs
+++ b/tests/ui/extra_unused_lifetimes.rs
@@ -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() {}