Skip to content

Commit

Permalink
When encountering an undefined named lifetime, point to where it can be
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
estebank committed Jan 20, 2020
1 parent 7da653f commit 6ba0875
Show file tree
Hide file tree
Showing 14 changed files with 249 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/librustc_resolve/lib.rs
Expand Up @@ -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};
Expand Down
41 changes: 35 additions & 6 deletions src/librustc_resolve/lifetimes.rs
Expand Up @@ -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)]
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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(_)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
}
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
Expand Down Expand Up @@ -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();
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/test/ui/error-codes/E0261.stderr
Expand Up @@ -2,11 +2,15 @@ error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/E0261.rs:1:12
|
LL | fn foo(x: &'a str) { }
| ^^ undeclared lifetime
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'a` here: `<'a>`

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/E0261.rs:5:9
|
LL | struct Foo {
| - help: consider introducing lifetime `'a` here: `<'a>`
LL | x: &'a str,
| ^^ undeclared lifetime

Expand Down
118 changes: 111 additions & 7 deletions src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr
Expand Up @@ -2,103 +2,207 @@ error[E0261]: use of undeclared lifetime name `'x`
--> $DIR/feature-gate-in_band_lifetimes.rs:3:12
|
LL | fn foo(x: &'x u8) -> &'x u8 { x }
| ^^ undeclared lifetime
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'x` here: `<'x>`

error[E0261]: use of undeclared lifetime name `'x`
--> $DIR/feature-gate-in_band_lifetimes.rs:3:23
|
LL | fn foo(x: &'x u8) -> &'x u8 { x }
| ^^ undeclared lifetime
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'x` here: `<'x>`

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:15:12
|
LL | impl<'a> X<'b> {
| ^^ undeclared lifetime
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'b` here: `'b,`

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:17:27
|
LL | fn inner_2(&self) -> &'b u8 {
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'b` here
|
LL | impl<'b, 'a> X<'b> {
| ^^^
help: consider introducing lifetime `'b` here
|
LL | fn inner_2<'b>(&self) -> &'b u8 {
| ^^^^

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:23:8
|
LL | impl X<'b> {
| ^^ undeclared lifetime
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'b` here: `<'b>`

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:25:27
|
LL | fn inner_3(&self) -> &'b u8 {
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'b` here
|
LL | impl<'b> X<'b> {
| ^^^^
help: consider introducing lifetime `'b` here
|
LL | fn inner_3<'b>(&self) -> &'b u8 {
| ^^^^

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/feature-gate-in_band_lifetimes.rs:33:9
|
LL | impl Y<&'a u8> {
| ^^ undeclared lifetime
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'a` here: `<'a>`

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/feature-gate-in_band_lifetimes.rs:35:25
|
LL | fn inner(&self) -> &'a u8 {
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | impl<'a> Y<&'a u8> {
| ^^^^
help: consider introducing lifetime `'a` here
|
LL | fn inner<'a>(&self) -> &'a u8 {
| ^^^^

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:43:27
|
LL | fn any_lifetime() -> &'b u8;
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'b` here
|
LL | trait MyTrait<'b, 'a> {
| ^^^
help: consider introducing lifetime `'b` here
|
LL | fn any_lifetime<'b>() -> &'b u8;
| ^^^^

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:45:27
|
LL | fn borrowed_lifetime(&'b self) -> &'b u8;
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'b` here
|
LL | trait MyTrait<'b, 'a> {
| ^^^
help: consider introducing lifetime `'b` here
|
LL | fn borrowed_lifetime<'b>(&'b self) -> &'b u8;
| ^^^^

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:45:40
|
LL | fn borrowed_lifetime(&'b self) -> &'b u8;
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'b` here
|
LL | trait MyTrait<'b, 'a> {
| ^^^
help: consider introducing lifetime `'b` here
|
LL | fn borrowed_lifetime<'b>(&'b self) -> &'b u8;
| ^^^^

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/feature-gate-in_band_lifetimes.rs:50:14
|
LL | impl MyTrait<'a> for Y<&'a u8> {
| ^^ undeclared lifetime
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'a` here: `<'a>`

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/feature-gate-in_band_lifetimes.rs:50:25
|
LL | impl MyTrait<'a> for Y<&'a u8> {
| ^^ undeclared lifetime
| - ^^ undeclared lifetime
| |
| help: consider introducing lifetime `'a` here: `<'a>`

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/feature-gate-in_band_lifetimes.rs:53:31
|
LL | fn my_lifetime(&self) -> &'a u8 { self.0 }
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'a` here
|
LL | impl<'a> MyTrait<'a> for Y<&'a u8> {
| ^^^^
help: consider introducing lifetime `'a` here
|
LL | fn my_lifetime<'a>(&self) -> &'a u8 { self.0 }
| ^^^^

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:55:27
|
LL | fn any_lifetime() -> &'b u8 { &0 }
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'b` here
|
LL | impl<'b> MyTrait<'a> for Y<&'a u8> {
| ^^^^
help: consider introducing lifetime `'b` here
|
LL | fn any_lifetime<'b>() -> &'b u8 { &0 }
| ^^^^

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:57:27
|
LL | fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 }
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'b` here
|
LL | impl<'b> MyTrait<'a> for Y<&'a u8> {
| ^^^^
help: consider introducing lifetime `'b` here
|
LL | fn borrowed_lifetime<'b>(&'b self) -> &'b u8 { &*self.0 }
| ^^^^

error[E0261]: use of undeclared lifetime name `'b`
--> $DIR/feature-gate-in_band_lifetimes.rs:57:40
|
LL | fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 }
| ^^ undeclared lifetime
|
help: consider introducing lifetime `'b` here
|
LL | impl<'b> MyTrait<'a> for Y<&'a u8> {
| ^^^^
help: consider introducing lifetime `'b` here
|
LL | fn borrowed_lifetime<'b>(&'b self) -> &'b u8 { &*self.0 }
| ^^^^

error: aborting due to 17 previous errors

Expand Down

0 comments on commit 6ba0875

Please sign in to comment.