rust-lang/rust · #163684

suggest named lifetimes instead of `'_` in `use<...>` for E0700

Albab-Hasan · merged Oct 23, 20268 files · 461 + / 82 −
compiler/rustc_trait_selection/src/diagnostics.rs14 + / 0 −
@@ -1921,6 +1921,20 @@ impl Subdiagnostic for AddPreciseCapturingAndParams {     } } +pub(crate) struct AddPreciseCapturingAndNameLifetimes {+    pub suggs: Vec<(Span, String)>,+}++impl Subdiagnostic for AddPreciseCapturingAndNameLifetimes {+    fn add_to_diag(self, diag: &mut Diag<'_>) {+        diag.multipart_suggestion(+            msg!("add `use<...>` bound and name the elided lifetimes to explicitly capture them"),+            self.suggs,+            Applicability::MachineApplicable,+        );+    }+}+ /// Given a set of captured `DefId` for an RPIT (opaque_def_id) and a given /// function (fn_def_id), try to suggest adding `+ use<...>` to capture just /// the specified parameters. If one of those parameters is an APIT, then try
compiler/rustc_trait_selection/src/error_reporting/infer/region.rs233 + / 69 −
@@ -1,13 +1,15 @@ use std::iter; -use rustc_data_structures::fx::FxIndexSet;+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{     Applicability, Diag, E0309, E0310, E0311, E0803, Subdiagnostic, msg, struct_span_code_err, }; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, ParamName};+use rustc_middle::hir::nested_filter;+use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::RegionHighlightMode;@@ -934,72 +936,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {         lifetime: Region<'tcx>,         add_lt_suggs: &mut Vec<(Span, String)>,     ) -> Option<String> {-        struct LifetimeReplaceVisitor<'a> {-            needle: hir::LifetimeKind,-            new_lt: &'a str,-            add_lt_suggs: &'a mut Vec<(Span, String)>,-        }--        impl<'hir> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_> {-            fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {-                if lt.kind == self.needle {-                    self.add_lt_suggs.push(lt.suggestion(self.new_lt));-                }-            }-        }--        let (lifetime_def_id, lifetime_scope) =-            match self.tcx.is_suitable_region(generic_param_scope, lifetime) {-                Some(info) if !lifetime.is_named(self.tcx) => {-                    (info.region_def_id.expect_local(), info.scope)-                }-                _ => return Some(lifetime.get_name(self.tcx)?.to_string()),-            };--        let new_lt = {-            let generics = self.tcx.generics_of(lifetime_scope);-            let mut used_names =-                iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))-                    .flat_map(|g| &g.own_params)-                    .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))-                    .map(|p| p.name)-                    .collect::<Vec<_>>();-            let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope);-            // consider late-bound lifetimes ...-            used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(-                |p| match p {-                    ty::BoundVariableKind::Region(lt) => lt.get_name(self.tcx),-                    _ => None,-                },-            ));-            (b'a'..=b'z')-                .map(|c| format!("'{}", c as char))-                .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))-                .unwrap_or_else(|| "'lt".to_string())-        };--        let mut visitor = LifetimeReplaceVisitor {-            needle: hir::LifetimeKind::Param(lifetime_def_id),-            add_lt_suggs,-            new_lt: &new_lt,-        };-        match self.tcx.expect_hir_owner_node(lifetime_scope) {-            hir::OwnerNode::Item(i) => visitor.visit_item(i),-            hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),-            hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),-            hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),-            hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),-            hir::OwnerNode::Synthetic => unreachable!(),-        }--        let ast_generics = self.tcx.hir_get_generics(lifetime_scope).unwrap();-        let sugg = ast_generics-            .span_for_lifetime_suggestion()-            .map(|span| (span, format!("{new_lt}, ")))-            .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));-        add_lt_suggs.push(sugg);--        Some(new_lt)+        let [name] =+            suggest_name_regions(self.tcx, generic_param_scope, &[lifetime], add_lt_suggs)?+                .try_into()+                .unwrap();+        Some(name)     }      fn report_sub_sup_conflict(@@ -1169,6 +1110,101 @@ pub(super) fn note_and_explain_region<'tcx>(     emit_msg_span(err, prefix, description, span, suffix); } +fn suggest_name_regions<'tcx>(+    tcx: TyCtxt<'tcx>,+    generic_param_scope: LocalDefId,+    lifetimes: &[Region<'tcx>],+    add_lt_suggs: &mut Vec<(Span, String)>,+) -> Option<Vec<String>> {+    struct LifetimeReplaceVisitor<'a> {+        new_lts: &'a [(LocalDefId, String)],+        add_lt_suggs: &'a mut Vec<(Span, String)>,+    }++    impl<'hir> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_> {+        fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {+            if let hir::LifetimeKind::Param(def_id) = lt.kind+                && let Some((_, new_lt)) = self.new_lts.iter().find(|(id, _)| *id == def_id)+            {+                self.add_lt_suggs.push(lt.suggestion(new_lt));+            }+        }+    }++    let mut lifetime_scope = None;+    let mut used_names = vec![];+    let mut new_lts: Vec<(LocalDefId, String)> = vec![];+    let names: Vec<_> = lifetimes+        .iter()+        .map(|&lifetime| {+            let (lifetime_def_id, scope) =+                match tcx.is_suitable_region(generic_param_scope, lifetime) {+                    Some(info) if !lifetime.is_named(tcx) => {+                        (info.region_def_id.expect_local(), info.scope)+                    }+                    _ => return Some(lifetime.get_name(tcx)?.to_string()),+                };+            if *lifetime_scope.get_or_insert_with(|| {+                used_names.extend(+                    iter::successors(Some(tcx.generics_of(scope)), |g| {+                        g.parent.map(|p| tcx.generics_of(p))+                    })+                    .flat_map(|g| &g.own_params)+                    .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))+                    .map(|p| p.name),+                );+                // consider late-bound lifetimes ...+                used_names.extend(+                    tcx.late_bound_vars(tcx.local_def_id_to_hir_id(scope)).into_iter().filter_map(+                        |p| match p {+                            ty::BoundVariableKind::Region(lt) => lt.get_name(tcx),+                            _ => None,+                        },+                    ),+                );+                scope+            }) != scope+            {+                return Some(lifetime.get_name(tcx)?.to_string());+            }+            if let Some((_, name)) = new_lts.iter().find(|(id, _)| *id == lifetime_def_id) {+                return Some(name.clone());+            }+            let new_lt = (b'a'..=b'z')+                .map(|c| format!("'{}", c as char))+                .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))+                .unwrap_or_else(|| "'lt".to_string());+            used_names.push(Symbol::intern(&new_lt));+            new_lts.push((lifetime_def_id, new_lt.clone()));+            Some(new_lt)+        })+        .collect::<Option<_>>()?;++    let Some(lifetime_scope) = lifetime_scope else {+        return Some(names);+    };++    let mut visitor = LifetimeReplaceVisitor { new_lts: &new_lts, add_lt_suggs };+    match tcx.expect_hir_owner_node(lifetime_scope) {+        hir::OwnerNode::Item(i) => visitor.visit_item(i),+        hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),+        hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),+        hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),+        hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),+        hir::OwnerNode::Synthetic => unreachable!(),+    }++    let new_lt = new_lts.into_iter().map(|(_, name)| name).collect::<Vec<_>>().join(", ");+    let ast_generics = tcx.hir_get_generics(lifetime_scope).unwrap();+    let sugg = ast_generics+        .span_for_lifetime_suggestion()+        .map(|span| (span, format!("{new_lt}, ")))+        .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));+    add_lt_suggs.push(sugg);++    Some(names)+}+ fn explain_free_region<'tcx>(     tcx: TyCtxt<'tcx>,     err: &mut Diag<'_>,@@ -1319,7 +1355,14 @@ pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>(                 "",             );             if let Some(_) = tcx.is_suitable_region(generic_param_scope, hidden_region) {-                suggest_precise_capturing(tcx, opaque_ty_key.def_id, hidden_region, &mut err);+                suggest_precise_capturing(+                    tcx,+                    generic_param_scope,+                    opaque_ty_key.def_id,+                    hidden_ty.value,+                    hidden_region,+                    &mut err,+                );             }         }         ty::RePlaceholder(_) => {@@ -1368,7 +1411,9 @@ pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>(  fn suggest_precise_capturing<'tcx>(     tcx: TyCtxt<'tcx>,+    generic_param_scope: LocalDefId,     opaque_def_id: LocalDefId,+    hidden_ty: Ty<'tcx>,     captured_lifetime: ty::Region<'tcx>,     diag: &mut Diag<'_>, ) {@@ -1455,11 +1500,30 @@ fn suggest_precise_capturing<'tcx>(             }         } -        if !captured_lifetimes.insert(new_lifetime) {+        if captured_lifetimes.contains(&new_lifetime) {             // Uh, strange. This lifetime appears to already be captured...             return;         } +        // `'_` in a capture list only resolves when the signature has a single anonymous+        // lifetime, so when there are several the suggestion has to name them first.+        if synthetics.is_empty()+            && let Some(suggs) = name_anon_lifetimes_suggestion(+                tcx,+                generic_param_scope,+                opaque_def_id,+                fn_def_id,+                hidden_ty,+                &captured_lifetimes,+                &captured_non_lifetimes,+            )+        {+            diag.subdiagnostic(diagnostics::AddPreciseCapturingAndNameLifetimes { suggs });+            return;+        }++        captured_lifetimes.insert(new_lifetime);+         if synthetics.is_empty() {             let concatenated_bounds = captured_lifetimes                 .into_iter()@@ -1543,3 +1607,103 @@ fn suggest_precise_capturing<'tcx>(         }     } }++/// Build a suggestion that names every anonymous lifetime the hidden type captures and then+/// lists them in a `use<..>` bound.+///+/// This exists because a `use<..>` list cannot refer to more than one anonymous lifetime: `'_`+/// resolves like any other elided lifetime, so with several in the signature it either picks the+/// wrong one or fails to resolve at all. Naming them is the only way to write the bound the hidden+/// type actually needs.+///+/// Returns `None` whenever the plain `'_` suggestion is still correct, so that the common+/// single-lifetime case keeps its shorter form.+fn name_anon_lifetimes_suggestion<'tcx>(+    tcx: TyCtxt<'tcx>,+    generic_param_scope: LocalDefId,+    opaque_def_id: LocalDefId,+    fn_def_id: LocalDefId,+    hidden_ty: Ty<'tcx>,+    captured_lifetimes: &FxIndexSet<Symbol>,+    captured_non_lifetimes: &FxIndexSet<Symbol>,+) -> Option<Vec<(Span, String)>> {+    let fn_sig = tcx.hir_node_by_def_id(fn_def_id).fn_sig()?;++    let mut collector = AnonLifetimeCollector { tcx, anon_lifetimes: Default::default() };+    for input in fn_sig.decl.inputs {+        collector.visit_ty_unambig(input);+    }+    // With a single anonymous lifetime in the signature `'_` resolves to it, so leave that case+    // to the caller.+    if collector.anon_lifetimes.len() < 2 {+        return None;+    }++    let mut hidden_regions = FxIndexMap::default();+    tcx.for_each_free_region(&hidden_ty, |region| {+        if let Some(info) = tcx.is_suitable_region(generic_param_scope, region) {+            hidden_regions.entry(info.region_def_id).or_insert(region);+        }+    });++    // A lifetime the user already named stays as it is; only the anonymous ones need inventing+    // a name, and they are named in the order they appear in the signature.+    let named = hidden_regions+        .iter()+        .filter(|(def_id, _)| !collector.anon_lifetimes.contains(*def_id))+        .map(|(_, &region)| region);+    let anon: Vec<_> = collector+        .anon_lifetimes+        .iter()+        .filter_map(|def_id| hidden_regions.get(def_id).copied())+        .collect();+    if anon.is_empty() {+        return None;+    }+    let regions: Vec<_> = named.chain(anon).collect();++    let mut suggs = vec![];+    let mut bounds = captured_lifetimes.clone();+    bounds.extend(+        suggest_name_regions(tcx, generic_param_scope, &regions, &mut suggs)?+            .iter()+            .map(|name| Symbol::intern(name)),+    );++    let concatenated_bounds = bounds+        .into_iter()+        .chain(captured_non_lifetimes.iter().copied())+        .map(|sym| sym.to_string())+        .collect::<Vec<_>>()+        .join(", ");+    suggs.push((+        tcx.def_span(opaque_def_id).shrink_to_hi(),+        format!(" + use<{concatenated_bounds}>"),+    ));++    Some(suggs)+}++/// Collects every anonymous lifetime written in a function signature.+struct AnonLifetimeCollector<'tcx> {+    tcx: TyCtxt<'tcx>,+    anon_lifetimes: FxIndexSet<DefId>,+}++impl<'tcx> Visitor<'tcx> for AnonLifetimeCollector<'tcx> {+    type NestedFilter = nested_filter::OnlyBodies;++    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {+        self.tcx+    }++    fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) {+        if lifetime.is_anonymous()+            && let Some(+                rbv::ResolvedArg::EarlyBound(def_id) | rbv::ResolvedArg::LateBound(_, _, def_id),+            ) = self.tcx.named_bound_var(lifetime.hir_id)+        {+            self.anon_lifetimes.insert(def_id.to_def_id());+        }+    }+}
tests/ui/impl-trait/precise-capturing/name-anon-lifetime-suggestion-issue-132199.fixedadded48 + / 0 −
@@ -0,0 +1,48 @@+//@ edition: 2021+//@ run-rustfix++// regression test for https://github.com/rust-lang/rust/issues/132199.+// in edition 2024 impl Trait captures every in-scope lifetime by default,+// so none of these shapes error there. edition 2021 is used throughout.++#![allow(dead_code)]++struct T;+struct Foo<'a>(&'a u8);++impl T {+    // two_anon: two elided lifetimes, use<'_> captures only the first so a+    // second E0700 follows with no suggestion.+    fn two_anon<'a, 'b>(&'a self, t: &'b T) -> impl Sized + use<'a, 'b> {+        (self, t)+        //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+    }++    // one_anon: one elided lifetime, use<'_> is correct and must stay byte-identical.+    fn one_anon(&self) -> impl Sized + use<'_> {+        self+        //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+    }++    // named: an already-named lifetime, unaffected by this change.+    fn named<'a>(&'a self, t: &'a T) -> impl Sized + use<'a> {+        (self, t)+        //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+    }+}++// second_anon: two elided lifetimes where use<'_> does not resolve and+// produces E0106 on top of the original error.+fn second_anon<'a>(_a: &u8, b: &'a T) -> impl Sized + use<'a> {+    b+    //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++// path_anon: elision inside a path argument, so the name goes in as Foo<'a>+// and exercises the other arm of hir::LifetimeName matching.+fn path_anon<'a, 'b>(x: Foo<'a>, y: Foo<'b>) -> impl Sized + use<'a, 'b> {+    (x, y)+    //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn main() {}
tests/ui/impl-trait/precise-capturing/name-anon-lifetime-suggestion-issue-132199.rsadded48 + / 0 −
@@ -0,0 +1,48 @@+//@ edition: 2021+//@ run-rustfix++// regression test for https://github.com/rust-lang/rust/issues/132199.+// in edition 2024 impl Trait captures every in-scope lifetime by default,+// so none of these shapes error there. edition 2021 is used throughout.++#![allow(dead_code)]++struct T;+struct Foo<'a>(&'a u8);++impl T {+    // two_anon: two elided lifetimes, use<'_> captures only the first so a+    // second E0700 follows with no suggestion.+    fn two_anon(&self, t: &T) -> impl Sized {+        (self, t)+        //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+    }++    // one_anon: one elided lifetime, use<'_> is correct and must stay byte-identical.+    fn one_anon(&self) -> impl Sized {+        self+        //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+    }++    // named: an already-named lifetime, unaffected by this change.+    fn named<'a>(&'a self, t: &'a T) -> impl Sized {+        (self, t)+        //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+    }+}++// second_anon: two elided lifetimes where use<'_> does not resolve and+// produces E0106 on top of the original error.+fn second_anon(_a: &u8, b: &T) -> impl Sized {+    b+    //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++// path_anon: elision inside a path argument, so the name goes in as Foo<'a>+// and exercises the other arm of hir::LifetimeName matching.+fn path_anon(x: Foo, y: Foo) -> impl Sized {+    (x, y)+    //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds+}++fn main() {}
tests/ui/impl-trait/precise-capturing/name-anon-lifetime-suggestion-issue-132199.stderradded78 + / 0 −
@@ -0,0 +1,78 @@+error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+  --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:17:9+   |+LL |     fn two_anon(&self, t: &T) -> impl Sized {+   |                 -----            ---------- opaque type defined here+   |                 |+   |                 hidden type `(&T, &T)` captures the anonymous lifetime defined here+LL |         (self, t)+   |         ^^^^^^^^^+   |+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them+   |+LL |     fn two_anon<'a, 'b>(&'a self, t: &'b T) -> impl Sized + use<'a, 'b> {+   |                ++++++++  ++           ++                  +++++++++++++++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+  --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:23:9+   |+LL |     fn one_anon(&self) -> impl Sized {+   |                 -----     ---------- opaque type defined here+   |                 |+   |                 hidden type `&T` captures the anonymous lifetime defined here+LL |         self+   |         ^^^^+   |+help: add a `use<...>` bound to explicitly capture `'_`+   |+LL |     fn one_anon(&self) -> impl Sized + use<'_> {+   |                                      +++++++++++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+  --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:29:9+   |+LL |     fn named<'a>(&'a self, t: &'a T) -> impl Sized {+   |              --                         ---------- opaque type defined here+   |              |+   |              hidden type `(&'a T, &'a T)` captures the lifetime `'a` as defined here+LL |         (self, t)+   |         ^^^^^^^^^+   |+help: add a `use<...>` bound to explicitly capture `'a`+   |+LL |     fn named<'a>(&'a self, t: &'a T) -> impl Sized + use<'a> {+   |                                                    +++++++++++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+  --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:37:5+   |+LL | fn second_anon(_a: &u8, b: &T) -> impl Sized {+   |                            --     ---------- opaque type defined here+   |                            |+   |                            hidden type `&T` captures the anonymous lifetime defined here+LL |     b+   |     ^+   |+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them+   |+LL | fn second_anon<'a>(_a: &u8, b: &'a T) -> impl Sized + use<'a> {+   |               ++++              ++                  +++++++++++error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds+  --> $DIR/name-anon-lifetime-suggestion-issue-132199.rs:44:5+   |+LL | fn path_anon(x: Foo, y: Foo) -> impl Sized {+   |                 ---             ---------- opaque type defined here+   |                 |+   |                 hidden type `(Foo<'_>, Foo<'_>)` captures the anonymous lifetime defined here+LL |     (x, y)+   |     ^^^^^^+   |+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them+   |+LL | fn path_anon<'a, 'b>(x: Foo<'a>, y: Foo<'b>) -> impl Sized + use<'a, 'b> {+   |             ++++++++       ++++        ++++                +++++++++++++++error: aborting due to 5 previous errors++For more information about this error, try `rustc --explain E0700`.
tests/ui/lifetimes/issue-105227.fixedadded26 + / 0 −
@@ -0,0 +1,26 @@+// Regression test for issue #105227.++//@ run-rustfix++#![allow(unused)]++fn chars0<'a, 'b>(v :(&'a  str, &'b str)) -> impl Iterator<Item = char> + use<'a, 'b> {+    //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them+    v.0.chars().chain(v.1.chars())+    //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds+}++fn chars1<'a, 'b>(v0 : &'a  str, v1 : &'b str) -> impl Iterator<Item = char> + use<'a, 'b> {+    //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them+    v0.chars().chain(v1.chars())+    //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound+}++fn chars2<'a, 'c, 'b>(v0 : &'a str, v1 : &'c str, v2 : &'b str) -> (impl Iterator<Item = char> + use<'a, 'c>, &'b str) {+    //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them+    (v0.chars().chain(v1.chars()), v2)+    //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound+}++fn main() {+}
tests/ui/lifetimes/issue-105227.rs4 + / 4 −
@@ -1,23 +1,23 @@ // Regression test for issue #105227. -// FIXME(precise_capturing): Add rustfix here after dealing w/ elided lifetimes+//@ run-rustfix  #![allow(unused)]  fn chars0(v :(& str, &str)) -> impl Iterator<Item = char> {-    //~^ HELP add a `use<...>` bound+    //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them     v.0.chars().chain(v.1.chars())     //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds }  fn chars1(v0 : & str, v1 : &str) -> impl Iterator<Item = char> {-    //~^ HELP add a `use<...>` bound+    //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them     v0.chars().chain(v1.chars())     //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound }  fn chars2<'b>(v0 : &str, v1 : &'_ str, v2 : &'b str) -> (impl Iterator<Item = char>, &'b str) {-    //~^ HELP add a `use<...>` bound+    //~^ HELP add `use<...>` bound and name the elided lifetimes to explicitly capture them     (v0.chars().chain(v1.chars()), v2)     //~^ ERROR hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bound }
tests/ui/lifetimes/issue-105227.stderr10 + / 9 −
@@ -9,10 +9,10 @@ LL | LL |     v.0.chars().chain(v.1.chars())    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^    |-help: add a `use<...>` bound to explicitly capture `'_`+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them    |-LL | fn chars0(v :(& str, &str)) -> impl Iterator<Item = char> + use<'_> {-   |                                                           ++++++++++LL | fn chars0<'a, 'b>(v :(&'a  str, &'b str)) -> impl Iterator<Item = char> + use<'a, 'b> {+   |          ++++++++      ++        ++                                     +++++++++++++  error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds   --> $DIR/issue-105227.rs:15:5@@ -25,10 +25,10 @@ LL | LL |     v0.chars().chain(v1.chars())    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^    |-help: add a `use<...>` bound to explicitly capture `'_`+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them    |-LL | fn chars1(v0 : & str, v1 : &str) -> impl Iterator<Item = char> + use<'_> {-   |                                                                ++++++++++LL | fn chars1<'a, 'b>(v0 : &'a  str, v1 : &'b str) -> impl Iterator<Item = char> + use<'a, 'b> {+   |          ++++++++       ++             ++                                    +++++++++++++  error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime that does not appear in bounds   --> $DIR/issue-105227.rs:21:5@@ -41,10 +41,11 @@ LL | LL |     (v0.chars().chain(v1.chars()), v2)    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^    |-help: add a `use<...>` bound to explicitly capture `'_`+help: add `use<...>` bound and name the elided lifetimes to explicitly capture them+   |+LL - fn chars2<'b>(v0 : &str, v1 : &'_ str, v2 : &'b str) -> (impl Iterator<Item = char>, &'b str) {+LL + fn chars2<'a, 'c, 'b>(v0 : &'a str, v1 : &'c str, v2 : &'b str) -> (impl Iterator<Item = char> + use<'a, 'c>, &'b str) {    |-LL | fn chars2<'b>(v0 : &str, v1 : &'_ str, v2 : &'b str) -> (impl Iterator<Item = char> + use<'_>, &'b str) {-   |                                                                                     +++++++++  error: aborting due to 3 previous errors