diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2020-01-15 18:34:30 -0800 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2020-01-19 17:31:33 -0800 |
| commit | 6ba08755dfd9ddbb55248a0263a4e81d3602b410 (patch) | |
| tree | ce02b239a1a7b12bb4ced7592218a9d5cda62ba4 /src/librustc_resolve | |
| parent | 7da653f669dcb97d40ca29b2937518bb8a12b775 (diff) | |
When encountering an undefined named lifetime, point to where it can be
This doesn't mention that using an existing lifetime is possible, but that would hopefully be clear as always being an option. The intention of this is to teach newcomers what the lifetime syntax is.
Diffstat (limited to 'src/librustc_resolve')
| -rw-r--r-- | src/librustc_resolve/lib.rs | 1 | ||||
| -rw-r--r-- | src/librustc_resolve/lifetimes.rs | 41 |
2 files changed, 36 insertions, 6 deletions
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 60a0049f5da..29872be47c2 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -11,6 +11,7 @@ #![feature(crate_visibility_modifier)] #![feature(label_break_value)] #![feature(nll)] +#![feature(slice_patterns)] #![recursion_limit = "256"] pub use rustc_hir::def::{Namespace, PerNS}; diff --git a/src/librustc_resolve/lifetimes.rs b/src/librustc_resolve/lifetimes.rs index 5fae8f33187..f8e2f0cafff 100644 --- a/src/librustc_resolve/lifetimes.rs +++ b/src/librustc_resolve/lifetimes.rs @@ -183,6 +183,10 @@ struct LifetimeContext<'a, 'tcx> { xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>, lifetime_uses: &'a mut DefIdMap<LifetimeUseSet<'tcx>>, + + /// When encountering an undefined named lifetime, we will suggest introducing it in these + /// places. + missing_named_lifetime_spots: Vec<&'tcx hir::Generics<'tcx>>, } #[derive(Debug)] @@ -342,6 +346,7 @@ fn krate(tcx: TyCtxt<'_>) -> NamedRegionMap { labels_in_fn: vec![], xcrate_object_lifetime_defaults: Default::default(), lifetime_uses: &mut Default::default(), + missing_named_lifetime_spots: vec![], }; for (_, item) in &krate.items { visitor.visit_item(item); @@ -384,9 +389,11 @@ 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.visit_early_late(None, &sig.decl, generics, |this| { intravisit::walk_item(this, item); }); + self.missing_named_lifetime_spots.pop(); } hir::ItemKind::ExternCrate(_) @@ -417,6 +424,8 @@ 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); + // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name". // This is not true for other kinds of items.x let track_lifetime_uses = match item.kind { @@ -454,6 +463,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { this.check_lifetime_params(old_scope, &generics.params); intravisit::walk_item(this, item); }); + self.missing_named_lifetime_spots.pop(); } } } @@ -686,6 +696,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); match trait_item.kind { Method(ref sig, _) => { let tcx = self.tcx; @@ -737,10 +748,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { intravisit::walk_trait_item(self, trait_item); } } + self.missing_named_lifetime_spots.pop(); } 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); match impl_item.kind { Method(ref sig, _) => { let tcx = self.tcx; @@ -824,6 +837,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { intravisit::walk_impl_item(self, impl_item); } } + self.missing_named_lifetime_spots.pop(); } fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { @@ -1306,7 +1320,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { where F: for<'b> FnOnce(ScopeRef<'_>, &mut LifetimeContext<'b, 'tcx>), { - let LifetimeContext { tcx, map, lifetime_uses, .. } = self; + let LifetimeContext { tcx, map, lifetime_uses, missing_named_lifetime_spots, .. } = self; let labels_in_fn = take(&mut self.labels_in_fn); let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults); let mut this = LifetimeContext { @@ -1317,7 +1331,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { is_in_fn_syntax: self.is_in_fn_syntax, labels_in_fn, xcrate_object_lifetime_defaults, - lifetime_uses: lifetime_uses, + lifetime_uses, + missing_named_lifetime_spots: missing_named_lifetime_spots.to_vec(), }; debug!("entering scope {:?}", this.scope); f(self.scope, &mut this); @@ -1807,15 +1822,29 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { self.insert_lifetime(lifetime_ref, def); } else { - struct_span_err!( + let mut err = struct_span_err!( self.tcx.sess, lifetime_ref.span, E0261, "use of undeclared lifetime name `{}`", lifetime_ref - ) - .span_label(lifetime_ref.span, "undeclared lifetime") - .emit(); + ); + 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, + ); + } + } + err.emit(); } } |
