about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Macleod <alex@macleod.io>2023-01-31 14:12:03 +0000
committerAlex Macleod <alex@macleod.io>2023-01-31 14:12:03 +0000
commit6a8b20230b4e1ccdc5936d9cde5a6018d3b8fc74 (patch)
tree4ac85e5ae4e59aa7aafc122c94298697f8eda9cf
parent607e3e893e5963c55986880db1cd1031a910d3af (diff)
downloadrust-6a8b20230b4e1ccdc5936d9cde5a6018d3b8fc74.tar.gz
rust-6a8b20230b4e1ccdc5936d9cde5a6018d3b8fc74.zip
Add machine applicable suggestion for `needless_lifetimes`
Also adds a test for #5787
-rw-r--r--clippy_lints/src/lifetimes.rs253
-rw-r--r--tests/ui/crashes/ice-2774.stderr5
-rw-r--r--tests/ui/crashes/needless_lifetimes_impl_trait.stderr5
-rw-r--r--tests/ui/needless_lifetimes.fixed537
-rw-r--r--tests/ui/needless_lifetimes.rs21
-rw-r--r--tests/ui/needless_lifetimes.stderr380
6 files changed, 1019 insertions, 182 deletions
diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs
index ef9ac96ace5..3cccc2cfe2a 100644
--- a/clippy_lints/src/lifetimes.rs
+++ b/clippy_lints/src/lifetimes.rs
@@ -1,22 +1,21 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::trait_ref_of_method;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
 use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 use rustc_hir::intravisit::{
-    walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
+    walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
     walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
 };
-use rustc_hir::lang_items;
 use rustc_hir::FnRetTy::Return;
 use rustc_hir::{
-    BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
-    ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, PolyTraitRef, PredicateOrigin, TraitFn,
-    TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
+    lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics,
+    Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
+    PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter as middle_nested_filter;
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::TyCtxt;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Span;
@@ -35,8 +34,6 @@ declare_clippy_lint! {
     /// ### Known problems
     /// - We bail out if the function has a `where` clause where lifetimes
     /// are mentioned due to potential false positives.
-    /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
-    /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
     ///
     /// ### Example
     /// ```rust
@@ -94,7 +91,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
 impl<'tcx> LateLintPass<'tcx> for Lifetimes {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Fn(ref sig, generics, id) = item.kind {
-            check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
+            check_fn_inner(cx, sig, Some(id), None, generics, item.span, true);
         } else if let ItemKind::Impl(impl_) = item.kind {
             if !item.span.from_expansion() {
                 report_extra_impl_lifetimes(cx, impl_);
@@ -107,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
             let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none();
             check_fn_inner(
                 cx,
-                sig.decl,
+                sig,
                 Some(id),
                 None,
                 item.generics,
@@ -123,22 +120,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
                 TraitFn::Required(sig) => (None, Some(sig)),
                 TraitFn::Provided(id) => (Some(id), None),
             };
-            check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true);
+            check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true);
         }
     }
 }
 
-/// The lifetime of a &-reference.
-#[derive(PartialEq, Eq, Hash, Debug, Clone)]
-enum RefLt {
-    Unnamed,
-    Static,
-    Named(LocalDefId),
-}
-
 fn check_fn_inner<'tcx>(
     cx: &LateContext<'tcx>,
-    decl: &'tcx FnDecl<'_>,
+    sig: &'tcx FnSig<'_>,
     body: Option<BodyId>,
     trait_sig: Option<&[Ident]>,
     generics: &'tcx Generics<'_>,
@@ -164,7 +153,7 @@ fn check_fn_inner<'tcx>(
             for bound in pred.bounds {
                 let mut visitor = RefVisitor::new(cx);
                 walk_param_bound(&mut visitor, bound);
-                if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
+                if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) {
                     return;
                 }
                 if let GenericBound::Trait(ref trait_ref, _) = *bound {
@@ -191,12 +180,12 @@ fn check_fn_inner<'tcx>(
         }
     }
 
-    if let Some(elidable_lts) = could_use_elision(cx, decl, body, trait_sig, generics.params) {
+    if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) {
         let lts = elidable_lts
             .iter()
             // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a
             // `Node::GenericParam`.
-            .filter_map(|&(def_id, _)| cx.tcx.hir().get_by_def_id(def_id).ident())
+            .filter_map(|&def_id| cx.tcx.hir().get_by_def_id(def_id).ident())
             .map(|ident| ident.to_string())
             .collect::<Vec<_>>()
             .join(", ");
@@ -204,21 +193,99 @@ fn check_fn_inner<'tcx>(
         span_lint_and_then(
             cx,
             NEEDLESS_LIFETIMES,
-            span.with_hi(decl.output.span().hi()),
+            span.with_hi(sig.decl.output.span().hi()),
             &format!("the following explicit lifetimes could be elided: {lts}"),
             |diag| {
-                if let Some(span) = elidable_lts.iter().find_map(|&(_, span)| span) {
-                    diag.span_help(span, "replace with `'_` in generic arguments such as here");
+                if sig.header.is_async() {
+                    // async functions have usages whose spans point at the lifetime declaration which messes up
+                    // suggestions
+                    return;
+                };
+
+                if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) {
+                    diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
                 }
             },
         );
     }
 
     if report_extra_lifetimes {
-        self::report_extra_lifetimes(cx, decl, generics);
+        self::report_extra_lifetimes(cx, sig.decl, generics);
     }
 }
 
+fn elision_suggestions(
+    cx: &LateContext<'_>,
+    generics: &Generics<'_>,
+    elidable_lts: &[LocalDefId],
+    usages: &[Lifetime],
+) -> Option<Vec<(Span, String)>> {
+    let explicit_params = generics
+        .params
+        .iter()
+        .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait())
+        .collect::<Vec<_>>();
+
+    let mut suggestions = if elidable_lts.len() == explicit_params.len() {
+        // if all the params are elided remove the whole generic block
+        //
+        // fn x<'a>() {}
+        //     ^^^^
+        vec![(generics.span, String::new())]
+    } else {
+        elidable_lts
+            .iter()
+            .map(|&id| {
+                let pos = explicit_params.iter().position(|param| param.def_id == id)?;
+                let param = explicit_params.get(pos)?;
+
+                let span = if let Some(next) = explicit_params.get(pos + 1) {
+                    // fn x<'prev, 'a, 'next>() {}
+                    //             ^^^^
+                    param.span.until(next.span)
+                } else {
+                    // `pos` should be at least 1 here, because the param in position 0 would either have a `next`
+                    // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch.
+                    let prev = explicit_params.get(pos - 1)?;
+
+                    // fn x<'prev, 'a>() {}
+                    //           ^^^^
+                    param.span.with_lo(prev.span.hi())
+                };
+
+                Some((span, String::new()))
+            })
+            .collect::<Option<Vec<_>>>()?
+    };
+
+    suggestions.extend(
+        usages
+            .iter()
+            .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id)))
+            .map(|usage| {
+                match cx.tcx.hir().get_parent(usage.hir_id) {
+                    Node::Ty(Ty {
+                        kind: TyKind::Ref(..), ..
+                    }) => {
+                        // expand `&'a T` to `&'a T`
+                        //          ^^         ^^^
+                        let span = cx
+                            .sess()
+                            .source_map()
+                            .span_extend_while(usage.ident.span, |ch| ch.is_ascii_whitespace())
+                            .unwrap_or(usage.ident.span);
+
+                        (span, String::new())
+                    },
+                    // `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
+                    _ => (usage.ident.span, String::from("'_")),
+                }
+            }),
+    );
+
+    Some(suggestions)
+}
+
 // elision doesn't work for explicit self types, see rust-lang/rust#69064
 fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
     if_chain! {
@@ -238,13 +305,20 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident:
     }
 }
 
+fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> {
+    match lt.res {
+        LifetimeName::Param(id) if !lt.is_anonymous() => Some(id),
+        _ => None,
+    }
+}
+
 fn could_use_elision<'tcx>(
     cx: &LateContext<'tcx>,
     func: &'tcx FnDecl<'_>,
     body: Option<BodyId>,
     trait_sig: Option<&[Ident]>,
     named_generics: &'tcx [GenericParam<'_>],
-) -> Option<Vec<(LocalDefId, Option<Span>)>> {
+) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
     // There are two scenarios where elision works:
     // * no output references, all input references have different LT
     // * output references, exactly one input reference with same LT
@@ -252,7 +326,7 @@ fn could_use_elision<'tcx>(
     // level of the current item.
 
     // check named LTs
-    let allowed_lts = allowed_lts_from(cx.tcx, named_generics);
+    let allowed_lts = allowed_lts_from(named_generics);
 
     // these will collect all the lifetimes for references in arg/return types
     let mut input_visitor = RefVisitor::new(cx);
@@ -302,32 +376,24 @@ fn could_use_elision<'tcx>(
 
     // check for lifetimes from higher scopes
     for lt in input_lts.iter().chain(output_lts.iter()) {
-        if !allowed_lts.contains(lt) {
+        if let Some(id) = named_lifetime(lt)
+            && !allowed_lts.contains(&id)
+        {
             return None;
         }
     }
 
     // check for higher-ranked trait bounds
     if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() {
-        let allowed_lts: FxHashSet<_> = allowed_lts
-            .iter()
-            .filter_map(|lt| match lt {
-                RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())),
-                _ => None,
-            })
-            .collect();
+        let allowed_lts: FxHashSet<_> = allowed_lts.iter().map(|id| cx.tcx.item_name(id.to_def_id())).collect();
         for lt in input_visitor.nested_elision_site_lts {
-            if let RefLt::Named(def_id) = lt {
-                if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
-                    return None;
-                }
+            if allowed_lts.contains(&lt.ident.name) {
+                return None;
             }
         }
         for lt in output_visitor.nested_elision_site_lts {
-            if let RefLt::Named(def_id) = lt {
-                if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
-                    return None;
-                }
+            if allowed_lts.contains(&lt.ident.name) {
+                return None;
             }
         }
     }
@@ -339,15 +405,10 @@ fn could_use_elision<'tcx>(
     let elidable_lts = named_lifetime_occurrences(&input_lts)
         .into_iter()
         .filter_map(|(def_id, occurrences)| {
-            if occurrences == 1 && (input_lts.len() == 1 || !output_lts.contains(&RefLt::Named(def_id))) {
-                Some((
-                    def_id,
-                    input_visitor
-                        .lifetime_generic_arg_spans
-                        .get(&def_id)
-                        .or_else(|| output_visitor.lifetime_generic_arg_spans.get(&def_id))
-                        .copied(),
-                ))
+            if occurrences == 1
+                && (input_lts.len() == 1 || !output_lts.iter().any(|lt| named_lifetime(lt) == Some(def_id)))
+            {
+                Some(def_id)
             } else {
                 None
             }
@@ -355,31 +416,34 @@ fn could_use_elision<'tcx>(
         .collect::<Vec<_>>();
 
     if elidable_lts.is_empty() {
-        None
-    } else {
-        Some(elidable_lts)
+        return None;
     }
+
+    let usages = itertools::chain(input_lts, output_lts).collect();
+
+    Some((elidable_lts, usages))
 }
 
-fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
-    let mut allowed_lts = FxHashSet::default();
-    for par in named_generics.iter() {
-        if let GenericParamKind::Lifetime { .. } = par.kind {
-            allowed_lts.insert(RefLt::Named(tcx.hir().local_def_id(par.hir_id)));
-        }
-    }
-    allowed_lts.insert(RefLt::Unnamed);
-    allowed_lts.insert(RefLt::Static);
-    allowed_lts
+fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<LocalDefId> {
+    named_generics
+        .iter()
+        .filter_map(|par| {
+            if let GenericParamKind::Lifetime { .. } = par.kind {
+                Some(par.def_id)
+            } else {
+                None
+            }
+        })
+        .collect()
 }
 
 /// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve
 /// relative order.
 #[must_use]
-fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> {
+fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> {
     let mut occurrences = Vec::new();
     for lt in lts {
-        if let &RefLt::Named(curr_def_id) = lt {
+        if let Some(curr_def_id) = named_lifetime(lt) {
             if let Some(pair) = occurrences
                 .iter_mut()
                 .find(|(prev_def_id, _)| *prev_def_id == curr_def_id)
@@ -393,12 +457,10 @@ fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> {
     occurrences
 }
 
-/// A visitor usable for `rustc_front::visit::walk_ty()`.
 struct RefVisitor<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
-    lts: Vec<RefLt>,
-    lifetime_generic_arg_spans: FxHashMap<LocalDefId, Span>,
-    nested_elision_site_lts: Vec<RefLt>,
+    lts: Vec<Lifetime>,
+    nested_elision_site_lts: Vec<Lifetime>,
     unelided_trait_object_lifetime: bool,
 }
 
@@ -407,32 +469,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
         Self {
             cx,
             lts: Vec::new(),
-            lifetime_generic_arg_spans: FxHashMap::default(),
             nested_elision_site_lts: Vec::new(),
             unelided_trait_object_lifetime: false,
         }
     }
 
-    fn record(&mut self, lifetime: &Option<Lifetime>) {
-        if let Some(ref lt) = *lifetime {
-            if lt.is_static() {
-                self.lts.push(RefLt::Static);
-            } else if lt.is_anonymous() {
-                // Fresh lifetimes generated should be ignored.
-                self.lts.push(RefLt::Unnamed);
-            } else if let LifetimeName::Param(def_id) = lt.res {
-                self.lts.push(RefLt::Named(def_id));
-            }
-        } else {
-            self.lts.push(RefLt::Unnamed);
-        }
-    }
-
-    fn all_lts(&self) -> Vec<RefLt> {
+    fn all_lts(&self) -> Vec<Lifetime> {
         self.lts
             .iter()
             .chain(self.nested_elision_site_lts.iter())
-            .cloned()
+            .copied()
             .collect::<Vec<_>>()
     }
 
@@ -444,7 +490,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
 impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
     // for lifetimes as parameters of generics
     fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
-        self.record(&Some(*lifetime));
+        self.lts.push(*lifetime);
     }
 
     fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) {
@@ -469,11 +515,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
                 walk_item(self, item);
                 self.lts.truncate(len);
                 self.lts.extend(bounds.iter().filter_map(|bound| match bound {
-                    GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id) = l.res {
-                        RefLt::Named(def_id)
-                    } else {
-                        RefLt::Unnamed
-                    }),
+                    GenericArg::Lifetime(&l) => Some(l),
                     _ => None,
                 }));
             },
@@ -493,13 +535,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
             _ => walk_ty(self, ty),
         }
     }
-
-    fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) {
-        if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res {
-            self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span);
-        }
-        walk_generic_arg(self, generic_arg);
-    }
 }
 
 /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
@@ -517,14 +552,18 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
                     return true;
                 }
                 // if the bounds define new lifetimes, they are fine to occur
-                let allowed_lts = allowed_lts_from(cx.tcx, pred.bound_generic_params);
+                let allowed_lts = allowed_lts_from(pred.bound_generic_params);
                 // now walk the bounds
                 for bound in pred.bounds.iter() {
                     walk_param_bound(&mut visitor, bound);
                 }
                 // and check that all lifetimes are allowed
-                if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) {
-                    return true;
+                for lt in visitor.all_lts() {
+                    if let Some(id) = named_lifetime(&lt)
+                        && !allowed_lts.contains(&id)
+                    {
+                        return true;
+                    }
                 }
             },
             WherePredicate::EqPredicate(ref pred) => {
diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr
index 1f26c7f4db6..c5ea0b16d1b 100644
--- a/tests/ui/crashes/ice-2774.stderr
+++ b/tests/ui/crashes/ice-2774.stderr
@@ -5,6 +5,11 @@ LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
+help: elide the lifetimes
+   |
+LL - pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
+LL + pub fn add_barfoos_to_foos(bars: &HashSet<&Bar>) {
+   |
 
 error: aborting due to previous error
 
diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr
index 875d5ab4f21..0b0e0ad2684 100644
--- a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr
+++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr
@@ -9,6 +9,11 @@ note: the lint level is defined here
    |
 LL | #![deny(clippy::needless_lifetimes)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: elide the lifetimes
+   |
+LL -     fn baz<'a>(&'a self) -> impl Foo + 'a {
+LL +     fn baz(&self) -> impl Foo + '_ {
+   |
 
 error: aborting due to previous error
 
diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed
new file mode 100644
index 00000000000..270cd1afc67
--- /dev/null
+++ b/tests/ui/needless_lifetimes.fixed
@@ -0,0 +1,537 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::needless_lifetimes)]
+#![allow(
+    unused,
+    clippy::boxed_local,
+    clippy::needless_pass_by_value,
+    clippy::unnecessary_wraps,
+    dyn_drop,
+    clippy::get_first
+)]
+
+#[macro_use]
+extern crate macro_rules;
+
+fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {}
+
+fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {}
+
+// No error; same lifetime on two params.
+fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {}
+
+// No error; static involved.
+fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {}
+
+fn mut_and_static_input(_x: &mut u8, _y: &'static str) {}
+
+fn in_and_out(x: &u8, _y: u8) -> &u8 {
+    x
+}
+
+// No error; multiple input refs.
+fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 {
+    x
+}
+
+// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
+//   fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8
+//                                                ^^^
+fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 {
+    x
+}
+
+// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
+//   fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8
+//                                     ^^^
+fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 {
+    y
+}
+
+// No error; multiple input refs
+async fn func<'a>(args: &[&'a str]) -> Option<&'a str> {
+    args.get(0).cloned()
+}
+
+// No error; static involved.
+fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 {
+    x
+}
+
+// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
+//   fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()>
+//                                           ^^^
+fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> {
+    Ok(x)
+}
+
+// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
+//   fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()>
+//                                ^^^
+fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> {
+    Ok(y)
+}
+
+// No error; two input refs.
+fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 {
+    x.unwrap()
+}
+
+fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> {
+    Ok(x)
+}
+
+// Where-clause, but without lifetimes.
+fn where_clause_without_lt<T>(x: &u8, _y: u8) -> Result<&u8, ()>
+where
+    T: Copy,
+{
+    Ok(x)
+}
+
+type Ref<'r> = &'r u8;
+
+// No error; same lifetime on two params.
+fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {}
+
+fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {}
+
+// No error; bounded lifetime.
+fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {}
+
+// No error; bounded lifetime.
+fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8)
+where
+    'b: 'a,
+{
+}
+
+struct Lt<'a, I: 'static> {
+    x: &'a I,
+}
+
+// No error; fn bound references `'a`.
+fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
+where
+    F: Fn(Lt<'a, I>) -> Lt<'a, I>,
+{
+    unreachable!()
+}
+
+fn fn_bound_2<F, I>(_m: Lt<'_, I>, _f: F) -> Lt<'_, I>
+where
+    for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>,
+{
+    unreachable!()
+}
+
+// No error; see below.
+fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) {
+    f(x);
+}
+
+fn fn_bound_3_cannot_elide() {
+    let x = 42;
+    let p = &x;
+    let mut q = &x;
+    // This will fail if we elide lifetimes of `fn_bound_3`.
+    fn_bound_3(p, |y| q = y);
+}
+
+// No error; multiple input refs.
+fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () {
+    if cond { x } else { f() }
+}
+
+struct X {
+    x: u8,
+}
+
+impl X {
+    fn self_and_out(&self) -> &u8 {
+        &self.x
+    }
+
+    // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
+    //   fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8
+    //                                          ^^^
+    fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 {
+        &self.x
+    }
+
+    // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
+    //   fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8
+    //                            ^^^^^
+    fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 {
+        x
+    }
+
+    fn distinct_self_and_in(&self, _x: &u8) {}
+
+    // No error; same lifetimes on two params.
+    fn self_and_same_in<'s>(&'s self, _x: &'s u8) {}
+}
+
+struct Foo<'a>(&'a u8);
+
+impl<'a> Foo<'a> {
+    // No error; lifetime `'a` not defined in method.
+    fn self_shared_lifetime(&self, _: &'a u8) {}
+    // No error; bounds exist.
+    fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {}
+}
+
+fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 {
+    unimplemented!()
+}
+
+fn struct_with_lt(_foo: Foo<'_>) -> &str {
+    unimplemented!()
+}
+
+// No warning; two input lifetimes (named on the reference, anonymous on `Foo`).
+fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str {
+    unimplemented!()
+}
+
+// No warning; two input lifetimes (anonymous on the reference, named on `Foo`).
+fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str {
+    unimplemented!()
+}
+
+// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
+// valid:
+//   fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str
+//                                         ^^
+fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str {
+    unimplemented!()
+}
+
+// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
+// valid:
+//   fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str
+//                                 ^^^^
+fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str {
+    unimplemented!()
+}
+
+trait WithLifetime<'a> {}
+
+type WithLifetimeAlias<'a> = dyn WithLifetime<'a>;
+
+// Should not warn because it won't build without the lifetime.
+fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str {
+    unimplemented!()
+}
+
+// Should warn because there is no lifetime on `Drop`, so this would be
+// unambiguous if we elided the lifetime.
+fn trait_obj_elided2(_arg: &dyn Drop) -> &str {
+    unimplemented!()
+}
+
+type FooAlias<'a> = Foo<'a>;
+
+fn alias_with_lt(_foo: FooAlias<'_>) -> &str {
+    unimplemented!()
+}
+
+// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`).
+fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str {
+    unimplemented!()
+}
+
+// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`).
+fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str {
+    unimplemented!()
+}
+
+// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
+// valid:
+//   fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str
+//                                             ^^
+fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str {
+    unimplemented!()
+}
+
+// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
+// valid:
+//   fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str
+//                                ^^^^^^^^^
+fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str {
+    unimplemented!()
+}
+
+fn named_input_elided_output(_arg: &str) -> &str {
+    unimplemented!()
+}
+
+fn elided_input_named_output<'a>(_arg: &str) -> &'a str {
+    unimplemented!()
+}
+
+fn trait_bound_ok<T: WithLifetime<'static>>(_: &u8, _: T) {
+    unimplemented!()
+}
+fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) {
+    unimplemented!()
+}
+
+// Don't warn on these; see issue #292.
+fn trait_bound_bug<'a, T: WithLifetime<'a>>() {
+    unimplemented!()
+}
+
+// See issue #740.
+struct Test {
+    vec: Vec<usize>,
+}
+
+impl Test {
+    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> {
+        unimplemented!()
+    }
+}
+
+trait LintContext<'a> {}
+
+fn f<'a, T: LintContext<'a>>(_: &T) {}
+
+fn test<'a>(x: &'a [u8]) -> u8 {
+    let y: &'a u8 = &x[5];
+    *y
+}
+
+// Issue #3284: give hint regarding lifetime in return type.
+struct Cow<'a> {
+    x: &'a str,
+}
+fn out_return_type_lts(e: &str) -> Cow<'_> {
+    unimplemented!()
+}
+
+// Make sure we still warn on implementations
+mod issue4291 {
+    trait BadTrait {
+        fn needless_lt(x: &u8) {}
+    }
+
+    impl BadTrait for () {
+        fn needless_lt(_x: &u8) {}
+    }
+}
+
+mod issue2944 {
+    trait Foo {}
+    struct Bar;
+    struct Baz<'a> {
+        bar: &'a Bar,
+    }
+
+    impl<'a> Foo for Baz<'a> {}
+    impl Bar {
+        fn baz(&self) -> impl Foo + '_ {
+            Baz { bar: self }
+        }
+    }
+}
+
+mod nested_elision_sites {
+    // issue #issue2944
+
+    // closure trait bounds subject to nested elision
+    // don't lint because they refer to outer lifetimes
+    fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
+        move || i
+    }
+    fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 {
+        move || i
+    }
+    fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 {
+        move || i
+    }
+
+    // don't lint
+    fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 {
+        f()
+    }
+    fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
+        move || i
+    }
+    // lint
+    fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
+        f(i)
+    }
+    fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 {
+        f(i)
+    }
+
+    // don't lint
+    fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 {
+        f()
+    }
+    // lint
+    fn generics_elidable<T: Fn(&i32) -> &i32>(i: &i32, f: T) -> &i32 {
+        f(i)
+    }
+
+    // don't lint
+    fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32
+    where
+        T: Fn() -> &'a i32,
+    {
+        f()
+    }
+    // lint
+    fn where_clause_elidadable<T>(i: &i32, f: T) -> &i32
+    where
+        T: Fn(&i32) -> &i32,
+    {
+        f(i)
+    }
+
+    // don't lint
+    fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 {
+        f(i)
+    }
+    fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 {
+        |i| i
+    }
+    // lint
+    fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 {
+        f(i)
+    }
+
+    // don't lint
+    fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 {
+        |f| 42
+    }
+    fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) {
+        |f| ()
+    }
+
+    // lint
+    fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 {
+        |f| 42
+    }
+    fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) {
+        |f| ()
+    }
+}
+
+mod issue6159 {
+    use std::ops::Deref;
+    pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R
+    where
+        T: Deref,
+        F: FnOnce(&'a T::Target) -> R,
+    {
+        f(x.deref())
+    }
+}
+
+mod issue7296 {
+    use std::rc::Rc;
+    use std::sync::Arc;
+
+    struct Foo;
+    impl Foo {
+        fn implicit(&self) -> &() {
+            &()
+        }
+        fn implicit_mut(&mut self) -> &() {
+            &()
+        }
+
+        fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
+            &()
+        }
+        fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
+            &()
+        }
+
+        fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &() {
+            &()
+        }
+    }
+
+    trait Bar {
+        fn implicit(&self) -> &();
+        fn implicit_provided(&self) -> &() {
+            &()
+        }
+
+        fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
+        fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
+            &()
+        }
+
+        fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &();
+        fn lifetime_elsewhere_provided(self: Box<Self>, here: &()) -> &() {
+            &()
+        }
+    }
+}
+
+mod pr_9743_false_negative_fix {
+    #![allow(unused)]
+
+    fn foo(x: &u8, y: &'_ u8) {}
+
+    fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {}
+}
+
+mod pr_9743_output_lifetime_checks {
+    #![allow(unused)]
+
+    // lint: only one input
+    fn one_input(x: &u8) -> &u8 {
+        unimplemented!()
+    }
+
+    // lint: multiple inputs, output would not be elided
+    fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 {
+        unimplemented!()
+    }
+
+    // don't lint: multiple inputs, output would be elided (which would create an ambiguity)
+    fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 {
+        unimplemented!()
+    }
+}
+
+mod in_macro {
+    macro_rules! local_one_input_macro {
+        () => {
+            fn one_input(x: &u8) -> &u8 {
+                unimplemented!()
+            }
+        };
+    }
+
+    // lint local macro expands to function with needless lifetimes
+    local_one_input_macro!();
+
+    // no lint on external macro
+    macro_rules::needless_lifetime!();
+}
+
+mod issue5787 {
+    use std::sync::MutexGuard;
+
+    struct Foo;
+
+    impl Foo {
+        // doesn't get linted without async
+        pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
+            guard
+        }
+    }
+
+    async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str {
+        y
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs
index 78493c6d067..5d4dc971b8d 100644
--- a/tests/ui/needless_lifetimes.rs
+++ b/tests/ui/needless_lifetimes.rs
@@ -1,7 +1,9 @@
+// run-rustfix
 // aux-build:macro_rules.rs
+
 #![warn(clippy::needless_lifetimes)]
 #![allow(
-    dead_code,
+    unused,
     clippy::boxed_local,
     clippy::needless_pass_by_value,
     clippy::unnecessary_wraps,
@@ -515,4 +517,21 @@ mod in_macro {
     macro_rules::needless_lifetime!();
 }
 
+mod issue5787 {
+    use std::sync::MutexGuard;
+
+    struct Foo;
+
+    impl Foo {
+        // doesn't get linted without async
+        pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
+            guard
+        }
+    }
+
+    async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str {
+        y
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr
index 9d02626956e..afe637ac388 100644
--- a/tests/ui/needless_lifetimes.stderr
+++ b/tests/ui/needless_lifetimes.stderr
@@ -1,319 +1,546 @@
 error: the following explicit lifetimes could be elided: 'a, 'b
-  --> $DIR/needless_lifetimes.rs:15:1
+  --> $DIR/needless_lifetimes.rs:17:1
    |
 LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
+help: elide the lifetimes
+   |
+LL - fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
+LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {}
+   |
 
 error: the following explicit lifetimes could be elided: 'a, 'b
-  --> $DIR/needless_lifetimes.rs:17:1
+  --> $DIR/needless_lifetimes.rs:19:1
    |
 LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
+LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {}
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:27:1
+  --> $DIR/needless_lifetimes.rs:29:1
    |
 LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
+LL + fn in_and_out(x: &u8, _y: u8) -> &u8 {
+   |
 
 error: the following explicit lifetimes could be elided: 'b
-  --> $DIR/needless_lifetimes.rs:39:1
+  --> $DIR/needless_lifetimes.rs:41:1
    |
 LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
+LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:46:1
+  --> $DIR/needless_lifetimes.rs:48:1
    |
 LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 {
+LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 {
+   |
 
 error: the following explicit lifetimes could be elided: 'b
-  --> $DIR/needless_lifetimes.rs:63:1
+  --> $DIR/needless_lifetimes.rs:65:1
    |
 LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
+LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:70:1
+  --> $DIR/needless_lifetimes.rs:72:1
    |
 LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> {
+LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:79:1
+  --> $DIR/needless_lifetimes.rs:81:1
    |
 LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
+LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:84:1
+  --> $DIR/needless_lifetimes.rs:86:1
    |
 LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
+LL + fn where_clause_without_lt<T>(x: &u8, _y: u8) -> Result<&u8, ()>
+   |
 
 error: the following explicit lifetimes could be elided: 'a, 'b
-  --> $DIR/needless_lifetimes.rs:96:1
+  --> $DIR/needless_lifetimes.rs:98:1
    |
 LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: replace with `'_` in generic arguments such as here
-  --> $DIR/needless_lifetimes.rs:96:37
+help: elide the lifetimes
+   |
+LL - fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
+LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {}
    |
-LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
-   |                                     ^^
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:120:1
+  --> $DIR/needless_lifetimes.rs:122:1
    |
 LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: replace with `'_` in generic arguments such as here
-  --> $DIR/needless_lifetimes.rs:120:32
+help: elide the lifetimes
+   |
+LL - fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
+LL + fn fn_bound_2<F, I>(_m: Lt<'_, I>, _f: F) -> Lt<'_, I>
    |
-LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
-   |                                ^^
 
 error: the following explicit lifetimes could be elided: 's
-  --> $DIR/needless_lifetimes.rs:150:5
+  --> $DIR/needless_lifetimes.rs:152:5
    |
 LL |     fn self_and_out<'s>(&'s self) -> &'s u8 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn self_and_out<'s>(&'s self) -> &'s u8 {
+LL +     fn self_and_out(&self) -> &u8 {
+   |
 
 error: the following explicit lifetimes could be elided: 't
-  --> $DIR/needless_lifetimes.rs:157:5
+  --> $DIR/needless_lifetimes.rs:159:5
    |
 LL |     fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
+LL +     fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 {
+   |
 
 error: the following explicit lifetimes could be elided: 's
-  --> $DIR/needless_lifetimes.rs:164:5
+  --> $DIR/needless_lifetimes.rs:166:5
    |
 LL |     fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 {
+LL +     fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 {
+   |
 
 error: the following explicit lifetimes could be elided: 's, 't
-  --> $DIR/needless_lifetimes.rs:168:5
+  --> $DIR/needless_lifetimes.rs:170:5
    |
 LL |     fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
+LL +     fn distinct_self_and_in(&self, _x: &u8) {}
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:187:1
+  --> $DIR/needless_lifetimes.rs:189:1
    |
 LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: replace with `'_` in generic arguments such as here
-  --> $DIR/needless_lifetimes.rs:187:33
+help: elide the lifetimes
+   |
+LL - fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
+LL + fn struct_with_lt(_foo: Foo<'_>) -> &str {
    |
-LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
-   |                                 ^^
 
 error: the following explicit lifetimes could be elided: 'b
-  --> $DIR/needless_lifetimes.rs:205:1
+  --> $DIR/needless_lifetimes.rs:207:1
    |
 LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: replace with `'_` in generic arguments such as here
-  --> $DIR/needless_lifetimes.rs:205:43
+help: elide the lifetimes
+   |
+LL - fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
+LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str {
    |
-LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
-   |                                           ^^
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:213:1
+  --> $DIR/needless_lifetimes.rs:215:1
    |
 LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str {
+LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:228:1
+  --> $DIR/needless_lifetimes.rs:230:1
    |
 LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
+LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:234:1
+  --> $DIR/needless_lifetimes.rs:236:1
    |
 LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: replace with `'_` in generic arguments such as here
-  --> $DIR/needless_lifetimes.rs:234:37
+help: elide the lifetimes
+   |
+LL - fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
+LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str {
    |
-LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
-   |                                     ^^
 
 error: the following explicit lifetimes could be elided: 'b
-  --> $DIR/needless_lifetimes.rs:252:1
+  --> $DIR/needless_lifetimes.rs:254:1
    |
 LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: replace with `'_` in generic arguments such as here
-  --> $DIR/needless_lifetimes.rs:252:47
+help: elide the lifetimes
+   |
+LL - fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
+LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str {
    |
-LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
-   |                                               ^^
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:260:1
+  --> $DIR/needless_lifetimes.rs:262:1
    |
 LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str {
+LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:264:1
+  --> $DIR/needless_lifetimes.rs:266:1
    |
 LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
+LL + fn named_input_elided_output(_arg: &str) -> &str {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:272:1
+  --> $DIR/needless_lifetimes.rs:274:1
    |
 LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL - fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
+LL + fn trait_bound_ok<T: WithLifetime<'static>>(_: &u8, _: T) {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:308:1
+  --> $DIR/needless_lifetimes.rs:310:1
    |
 LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: replace with `'_` in generic arguments such as here
-  --> $DIR/needless_lifetimes.rs:308:47
+help: elide the lifetimes
+   |
+LL - fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
+LL + fn out_return_type_lts(e: &str) -> Cow<'_> {
    |
-LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
-   |                                               ^^
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:315:9
+  --> $DIR/needless_lifetimes.rs:317:9
    |
 LL |         fn needless_lt<'a>(x: &'a u8) {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn needless_lt<'a>(x: &'a u8) {}
+LL +         fn needless_lt(x: &u8) {}
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:319:9
+  --> $DIR/needless_lifetimes.rs:321:9
    |
 LL |         fn needless_lt<'a>(_x: &'a u8) {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn needless_lt<'a>(_x: &'a u8) {}
+LL +         fn needless_lt(_x: &u8) {}
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:332:9
+  --> $DIR/needless_lifetimes.rs:334:9
    |
 LL |         fn baz<'a>(&'a self) -> impl Foo + 'a {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn baz<'a>(&'a self) -> impl Foo + 'a {
+LL +         fn baz(&self) -> impl Foo + '_ {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:364:5
+  --> $DIR/needless_lifetimes.rs:366:5
    |
 LL |     fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
+LL +     fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:373:5
+  --> $DIR/needless_lifetimes.rs:375:5
    |
 LL |     fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
+LL +     fn generics_elidable<T: Fn(&i32) -> &i32>(i: &i32, f: T) -> &i32 {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:385:5
+  --> $DIR/needless_lifetimes.rs:387:5
    |
 LL |     fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
+LL +     fn where_clause_elidadable<T>(i: &i32, f: T) -> &i32
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:400:5
+  --> $DIR/needless_lifetimes.rs:402:5
    |
 LL |     fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
+LL +     fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:413:5
+  --> $DIR/needless_lifetimes.rs:415:5
    |
 LL |     fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
+LL +     fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:416:5
+  --> $DIR/needless_lifetimes.rs:418:5
    |
 LL |     fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
+LL +     fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:438:9
+  --> $DIR/needless_lifetimes.rs:440:9
    |
 LL |         fn implicit<'a>(&'a self) -> &'a () {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn implicit<'a>(&'a self) -> &'a () {
+LL +         fn implicit(&self) -> &() {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:441:9
+  --> $DIR/needless_lifetimes.rs:443:9
    |
 LL |         fn implicit_mut<'a>(&'a mut self) -> &'a () {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn implicit_mut<'a>(&'a mut self) -> &'a () {
+LL +         fn implicit_mut(&mut self) -> &() {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:452:9
+  --> $DIR/needless_lifetimes.rs:454:9
    |
 LL |         fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
+LL +         fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &() {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:458:9
+  --> $DIR/needless_lifetimes.rs:460:9
    |
 LL |         fn implicit<'a>(&'a self) -> &'a ();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn implicit<'a>(&'a self) -> &'a ();
+LL +         fn implicit(&self) -> &();
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:459:9
+  --> $DIR/needless_lifetimes.rs:461:9
    |
 LL |         fn implicit_provided<'a>(&'a self) -> &'a () {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn implicit_provided<'a>(&'a self) -> &'a () {
+LL +         fn implicit_provided(&self) -> &() {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:468:9
+  --> $DIR/needless_lifetimes.rs:470:9
    |
 LL |         fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
+LL +         fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &();
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:469:9
+  --> $DIR/needless_lifetimes.rs:471:9
    |
 LL |         fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -         fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
+LL +         fn lifetime_elsewhere_provided(self: Box<Self>, here: &()) -> &() {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:478:5
+  --> $DIR/needless_lifetimes.rs:480:5
    |
 LL |     fn foo<'a>(x: &'a u8, y: &'_ u8) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn foo<'a>(x: &'a u8, y: &'_ u8) {}
+LL +     fn foo(x: &u8, y: &'_ u8) {}
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:480:5
+  --> $DIR/needless_lifetimes.rs:482:5
    |
 LL |     fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {}
+LL +     fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {}
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:487:5
+  --> $DIR/needless_lifetimes.rs:489:5
    |
 LL |     fn one_input<'a>(x: &'a u8) -> &'a u8 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn one_input<'a>(x: &'a u8) -> &'a u8 {
+LL +     fn one_input(x: &u8) -> &u8 {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:492:5
+  --> $DIR/needless_lifetimes.rs:494:5
    |
 LL |     fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: elide the lifetimes
+   |
+LL -     fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 {
+LL +     fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 {
+   |
 
 error: the following explicit lifetimes could be elided: 'a
-  --> $DIR/needless_lifetimes.rs:505:13
+  --> $DIR/needless_lifetimes.rs:507:13
    |
 LL |             fn one_input<'a>(x: &'a u8) -> &'a u8 {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -322,6 +549,11 @@ LL |     local_one_input_macro!();
    |     ------------------------ in this macro invocation
    |
    = note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: elide the lifetimes
+   |
+LL -             fn one_input<'a>(x: &'a u8) -> &'a u8 {
+LL +             fn one_input(x: &u8) -> &u8 {
+   |
 
 error: aborting due to 46 previous errors