diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs index 3ed0ad16eeb..86252203f1d 100644 --- a/src/librustc_hir/hir.rs +++ b/src/librustc_hir/hir.rs @@ -2260,10 +2260,10 @@ impl TraitRef<'_> { #[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)] pub struct PolyTraitRef<'hir> { - /// The `'a` in `<'a> Foo<&'a T>`. + /// The `'a` in `for<'a> Foo<&'a T>`. pub bound_generic_params: &'hir [GenericParam<'hir>], - /// The `Foo<&'a T>` in `<'a> Foo<&'a T>`. + /// The `Foo<&'a T>` in `for <'a> Foo<&'a T>`. pub trait_ref: TraitRef<'hir>, pub span: Span, diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index a612ad9e783..fafceb1f97c 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -8,7 +8,6 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_feature::BUILTIN_ATTRIBUTES; -use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; @@ -20,6 +19,7 @@ use syntax::ast::{self, Ident, Path}; use syntax::util::lev_distance::find_best_match_for_name; use crate::imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver}; +use crate::lifetimes::{HRLTSpanType, MissingLifetimeSpot}; use crate::path_names_to_string; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind}; use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot}; @@ -1471,7 +1471,7 @@ crate fn add_missing_lifetime_specifiers_label( count: usize, lifetime_names: &FxHashSet<ast::Ident>, snippet: Option<&str>, - missing_named_lifetime_spots: &[&hir::Generics<'_>], + missing_named_lifetime_spots: &[MissingLifetimeSpot<'_>], ) { if count > 1 { err.span_label(span, format!("expected {} lifetime parameters", count)); @@ -1484,21 +1484,41 @@ crate fn add_missing_lifetime_specifiers_label( Applicability::MaybeIncorrect, ); }; - let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg| { + let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| { err.span_label(span, "expected named lifetime parameter"); - if let Some(generics) = missing_named_lifetime_spots.iter().last() { + for missing in missing_named_lifetime_spots.iter().rev() { let mut introduce_suggestion = vec![]; - introduce_suggestion.push(match &generics.params { - [] => (generics.span, "<'lifetime>".to_string()), - [param, ..] => (param.span.shrink_to_lo(), "'lifetime, ".to_string()), + let msg; + let should_break; + introduce_suggestion.push(match missing { + MissingLifetimeSpot::Generics(generics) => { + msg = "consider introducing a named lifetime parameter"; + should_break = true; + match &generics.params { + [] => (generics.span, "<'lifetime>".to_string()), + [param, ..] => (param.span.shrink_to_lo(), "'lifetime, ".to_string()), + } + } + MissingLifetimeSpot::HRLT { span, span_type } => { + msg = "consider introducing a Higher-Ranked lifetime"; + should_break = false; + err.note( + "for more information on Higher-Ranked lifetimes, visit \ + https://doc.rust-lang.org/nomicon/hrtb.html", + ); + let suggestion = match span_type { + HRLTSpanType::Empty => "for<'lifetime> ", + HRLTSpanType::Tail => ", 'lifetime", + }; + (*span, suggestion.to_string()) + } }); - introduce_suggestion.push((span, sugg)); - err.multipart_suggestion( - "consider introducing a named lifetime parameter", - introduce_suggestion, - Applicability::MaybeIncorrect, - ); + introduce_suggestion.push((span, sugg.to_string())); + err.multipart_suggestion(msg, introduce_suggestion, Applicability::MaybeIncorrect); + if should_break { + break; + } } }; @@ -1513,13 +1533,13 @@ crate fn add_missing_lifetime_specifiers_label( suggest_existing(err, format!("{}<{}>", snippet, name)); } (0, _, Some("&")) => { - suggest_new(err, "&'lifetime ".to_string()); + suggest_new(err, "&'lifetime "); } (0, _, Some("'_")) => { - suggest_new(err, "'lifetime".to_string()); + suggest_new(err, "'lifetime"); } (0, _, Some(snippet)) if !snippet.ends_with(">") => { - suggest_new(err, format!("{}<'lifetime>", snippet)); + suggest_new(err, &format!("{}<'lifetime>", snippet)); } _ => { err.span_label(span, "expected lifetime parameter"); diff --git a/src/librustc_resolve/lifetimes.rs b/src/librustc_resolve/lifetimes.rs index 6e9ed5fdc17..a8d6afa0e55 100644 --- a/src/librustc_resolve/lifetimes.rs +++ b/src/librustc_resolve/lifetimes.rs @@ -153,6 +153,22 @@ struct NamedRegionMap { object_lifetime_defaults: HirIdMap<Vec<ObjectLifetimeDefault>>, } +crate enum MissingLifetimeSpot<'tcx> { + Generics(&'tcx hir::Generics<'tcx>), + HRLT { span: Span, span_type: HRLTSpanType }, +} + +crate enum HRLTSpanType { + Empty, + Tail, +} + +impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> { + fn into(self) -> MissingLifetimeSpot<'tcx> { + MissingLifetimeSpot::Generics(self) + } +} + struct LifetimeContext<'a, 'tcx> { tcx: TyCtxt<'tcx>, map: &'a mut NamedRegionMap, @@ -186,7 +202,7 @@ struct LifetimeContext<'a, 'tcx> { /// When encountering an undefined named lifetime, we will suggest introducing it in these /// places. - missing_named_lifetime_spots: Vec<&'tcx hir::Generics<'tcx>>, + missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>, } #[derive(Debug)] @@ -389,7 +405,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { match item.kind { hir::ItemKind::Fn(ref sig, ref generics, _) => { - self.missing_named_lifetime_spots.push(generics); + self.missing_named_lifetime_spots.push(generics.into()); self.visit_early_late(None, &sig.decl, generics, |this| { intravisit::walk_item(this, item); }); @@ -424,7 +440,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { | hir::ItemKind::Trait(_, _, ref generics, ..) | hir::ItemKind::TraitAlias(ref generics, ..) | hir::ItemKind::Impl { ref generics, .. } => { - self.missing_named_lifetime_spots.push(generics); + self.missing_named_lifetime_spots.push(generics.into()); // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name". // This is not true for other kinds of items.x @@ -696,7 +712,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { use self::hir::TraitItemKind::*; - self.missing_named_lifetime_spots.push(&trait_item.generics); + self.missing_named_lifetime_spots.push((&trait_item.generics).into()); match trait_item.kind { Method(ref sig, _) => { let tcx = self.tcx; @@ -753,7 +769,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { use self::hir::ImplItemKind::*; - self.missing_named_lifetime_spots.push(&impl_item.generics); + self.missing_named_lifetime_spots.push((&impl_item.generics).into()); match impl_item.kind { Method(ref sig, _) => { let tcx = self.tcx; @@ -953,6 +969,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { ) { debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref); + let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref); if !self.trait_ref_hack || trait_ref.bound_generic_params.iter().any(|param| match param.kind { GenericParamKind::Lifetime { .. } => true, @@ -988,10 +1005,13 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.with(scope, |old_scope, this| { this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params); walk_list!(this, visit_generic_param, trait_ref.bound_generic_params); - this.visit_trait_ref(&trait_ref.trait_ref) + this.visit_trait_ref(&trait_ref.trait_ref); }) } else { - self.visit_trait_ref(&trait_ref.trait_ref) + self.visit_trait_ref(&trait_ref.trait_ref); + } + if should_pop_missing_lt { + self.missing_named_lifetime_spots.pop(); } } } @@ -1832,18 +1852,41 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { lifetime_ref ); err.span_label(lifetime_ref.span, "undeclared lifetime"); - if !self.is_in_fn_syntax { - for generics in &self.missing_named_lifetime_spots { - let (span, sugg) = match &generics.params { - [] => (generics.span, format!("<{}>", lifetime_ref)), - [param, ..] => (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref)), - }; - err.span_suggestion( - span, - &format!("consider introducing lifetime `{}` here", lifetime_ref), - sugg, - Applicability::MaybeIncorrect, - ); + for missing in &self.missing_named_lifetime_spots { + match missing { + MissingLifetimeSpot::Generics(generics) => { + let (span, sugg) = match &generics.params { + [] => (generics.span, format!("<{}>", lifetime_ref)), + [param, ..] => { + (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref)) + } + }; + err.span_suggestion( + span, + &format!("consider introducing lifetime `{}` here", lifetime_ref), + sugg, + Applicability::MaybeIncorrect, + ); + } + MissingLifetimeSpot::HRLT { span, span_type } => { + err.span_suggestion( + *span, + &format!( + "consider introducing a Higher-Ranked lifetime `{}` here", + lifetime_ref + ), + match span_type { + HRLTSpanType::Empty => format!("for<{}> ", lifetime_ref), + HRLTSpanType::Tail => format!(", {}", lifetime_ref), + } + .to_string(), + Applicability::MaybeIncorrect, + ); + err.note( + "for more information on Higher-Ranked lifetimes, visit \ + https://doc.rust-lang.org/nomicon/hrtb.html", + ); + } } } err.emit(); @@ -2441,6 +2484,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let elided_len = elided_params.len(); + // FIXME: collect spans of the input params when appropriate to use in the diagnostic. for (i, info) in elided_params.into_iter().enumerate() { let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions } = info; @@ -2747,6 +2791,27 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let old_value = self.map.defs.remove(&lifetime_ref.hir_id); assert_eq!(old_value, Some(bad_def)); } + + fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool { + if let Res::Def(_, did) = trait_ref.trait_ref.path.res { + if [ + self.tcx.lang_items().fn_once_trait(), + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + ] + .contains(&Some(did)) + { + let (span, span_type) = match &trait_ref.bound_generic_params { + [] => (trait_ref.span.shrink_to_lo(), HRLTSpanType::Empty), + [.., bound] => (bound.span.shrink_to_hi(), HRLTSpanType::Tail), + }; + self.missing_named_lifetime_spots + .push(MissingLifetimeSpot::HRLT { span, span_type }); + return true; + } + }; + false + } } /// Detects late-bound lifetimes and inserts them into diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 231aed48fb6..6bd120d818d 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1307,12 +1307,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); } }; + // FIXME: point at the type params that don't have appropriate lifetimes: + // struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F); + // ---- ---- ^^^^^^^ struct_span_err!( tcx.sess, binding.span, E0582, "binding for associated type `{}` references lifetime `{}`, \ - which does not appear in the trait input types", + which does not appear in the trait input types", binding.item_name, br_name ) diff --git a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr index bfb20ade035..7d71230e162 100644 --- a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr +++ b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr @@ -9,6 +9,8 @@ LL | let y: &'test u32 = x; error[E0261]: use of undeclared lifetime name `'test` --> $DIR/no_introducing_in_band_in_locals.rs:10:16 | +LL | fn bar() { + | - help: consider introducing lifetime `'test` here: `<'test>` LL | let y: fn(&'test u32) = foo2; | ^^^^^ undeclared lifetime diff --git a/src/test/ui/issues/issue-19707.stderr b/src/test/ui/issues/issue-19707.stderr index 8a627bc0bd4..c5129152aa5 100644 --- a/src/test/ui/issues/issue-19707.stderr +++ b/src/test/ui/issues/issue-19707.stderr @@ -17,6 +17,11 @@ LL | fn bar<F: Fn(&u8, &u8) -> &u8>(f: &F) {} | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2 + = note: for more information on Higher-Ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider introducing a Higher-Ranked lifetime + | +LL | fn bar<F: for<'lifetime> Fn(&u8, &u8) -> &'lifetime u8>(f: &F) {} + | ^^^^^^^^^^^^^^ ^^^^^^^^^^ help: consider introducing a named lifetime parameter | LL | fn bar<'lifetime, F: Fn(&u8, &u8) -> &'lifetime u8>(f: &F) {} diff --git a/src/test/ui/regions/regions-name-undeclared.stderr b/src/test/ui/regions/regions-name-undeclared.stderr index 79ebef41dcc..cb72d1ec9bc 100644 --- a/src/test/ui/regions/regions-name-undeclared.stderr +++ b/src/test/ui/regions/regions-name-undeclared.stderr @@ -88,12 +88,32 @@ error[E0261]: use of undeclared lifetime name `'b` | LL | ... &'b isize, | ^^ undeclared lifetime + | + = note: for more information on Higher-Ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider introducing lifetime `'b` here + | +LL | fn fn_types<'b>(a: &'a isize, + | ^^^^ +help: consider introducing a Higher-Ranked lifetime `'b` here + | +LL | b: Box<dyn for<'a, 'b> FnOnce(&'a isize, + | ^^^^ error[E0261]: use of undeclared lifetime name `'b` --> $DIR/regions-name-undeclared.rs:45:36 | LL | ... &'b isize)>, | ^^ undeclared lifetime + | + = note: for more information on Higher-Ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider introducing lifetime `'b` here + | +LL | fn fn_types<'b>(a: &'a isize, + | ^^^^ +help: consider introducing a Higher-Ranked lifetime `'b` here + | +LL | b: Box<dyn for<'a, 'b> FnOnce(&'a isize, + | ^^^^ error[E0261]: use of undeclared lifetime name `'a` --> $DIR/regions-name-undeclared.rs:46:17