diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index c86b414184759..cb9d536914efb 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -1212,7 +1212,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { let mut introduce_suggestion = vec![]; let msg; let should_break; - introduce_suggestion.push(match missing { + let missing_lifetime = match missing { MissingLifetimeSpot::Generics(generics) => { msg = "consider introducing a named lifetime parameter".to_string(); should_break = true; @@ -1223,9 +1223,14 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } => false, _ => true, }) { - (param.span.shrink_to_lo(), "'a, ".to_string()) + if param.name.ident().as_str().contains("'a") { + // Do not add duplicate lifetime. + None + } else { + Some((param.span.shrink_to_lo(), "'a, ".to_string())) + } } else { - (generics.span, "<'a>".to_string()) + Some((generics.span, "<'a>".to_string())) } } MissingLifetimeSpot::HigherRanked { span, span_type } => { @@ -1238,9 +1243,12 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { "for more information on higher-ranked polymorphism, visit \ https://doc.rust-lang.org/nomicon/hrtb.html", ); - (*span, span_type.suggestion("'a")) + Some((*span, span_type.suggestion("'a"))) } - }); + }; + if let Some((span, snip)) = missing_lifetime { + introduce_suggestion.push((span, snip)); + } for param in params { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { if snippet.starts_with('&') && !snippet.starts_with("&'") { diff --git a/src/test/ui/suggestions/missing-lifetime-with-named-lifetime.rs b/src/test/ui/suggestions/missing-lifetime-with-named-lifetime.rs new file mode 100644 index 0000000000000..c2549c6e2d29a --- /dev/null +++ b/src/test/ui/suggestions/missing-lifetime-with-named-lifetime.rs @@ -0,0 +1,11 @@ +struct X<'a>(&'a ()); +struct S<'a>(&'a dyn Fn(&X) -> &X); +//~^ ERROR: missing lifetime specifier +//~| ERROR: missing lifetime specifier + +fn main() { + let x = S(&|x| { + x + }); + x.0(&X(&())); +} diff --git a/src/test/ui/suggestions/missing-lifetime-with-named-lifetime.stderr b/src/test/ui/suggestions/missing-lifetime-with-named-lifetime.stderr new file mode 100644 index 0000000000000..a61d5de87cf83 --- /dev/null +++ b/src/test/ui/suggestions/missing-lifetime-with-named-lifetime.stderr @@ -0,0 +1,37 @@ +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-with-named-lifetime.rs:2:32 + | +LL | struct S<'a>(&'a dyn Fn(&X) -> &X); + | -- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | struct S<'a>(&'a dyn for<'a> Fn(&'a X) -> &'a X); + | ^^^^^^^ ^^^^^ ^^^ +help: consider introducing a named lifetime parameter + | +LL | struct S<'a>(&'a dyn Fn(&'a X) -> &'a X); + | ^^^^^ ^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/missing-lifetime-with-named-lifetime.rs:2:33 + | +LL | struct S<'a>(&'a dyn Fn(&X) -> &X); + | -- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | struct S<'a>(&'a dyn for<'a> Fn(&'a X) -> &X<'a>); + | ^^^^^^^ ^^^^^ ^^^^^ +help: consider introducing a named lifetime parameter + | +LL | struct S<'a>(&'a dyn Fn(&'a X) -> &X<'a>); + | ^^^^^ ^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0106`.