diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index c5d15dcc91af9..82fd46c14e05d 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3837,25 +3837,32 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { "instead, you are more likely to want" }; let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand; + let mut sugg_is_str_to_string = false; let mut sugg = vec![(lt.span, String::new())]; if let Some((kind, _span)) = self.diag_metadata.current_function && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = kind - && let ast::FnRetTy::Ty(ty) = &sig.decl.output { let mut lt_finder = LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] }; - lt_finder.visit_ty(&ty); - - if let [Ty { span, kind: TyKind::Ref(_, mut_ty), .. }] = - <_finder.seen[..] - { - // We might have a situation like - // fn g(mut x: impl Iterator) -> Option<&'_ ()> - // but `lt.span` only points at `'_`, so to suggest `-> Option<()>` - // we need to find a more accurate span to end up with - // fn g<'a>(mut x: impl Iterator) -> Option<()> - sugg = vec![(span.with_hi(mut_ty.ty.span.lo()), String::new())]; - owned_sugg = true; + for param in &sig.decl.inputs { + lt_finder.visit_ty(¶m.ty); + } + if let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output { + lt_finder.visit_ty(ret_ty); + let mut ret_lt_finder = + LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] }; + ret_lt_finder.visit_ty(ret_ty); + if let [Ty { span, kind: TyKind::Ref(_, mut_ty), .. }] = + &ret_lt_finder.seen[..] + { + // We might have a situation like + // fn g(mut x: impl Iterator) -> Option<&'_ ()> + // but `lt.span` only points at `'_`, so to suggest `-> Option<()>` + // we need to find a more accurate span to end up with + // fn g<'a>(mut x: impl Iterator) -> Option<()> + sugg = vec![(span.with_hi(mut_ty.ty.span.lo()), String::new())]; + owned_sugg = true; + } } if let Some(ty) = lt_finder.found { if let TyKind::Path(None, path) = &ty.kind { @@ -3875,6 +3882,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { lt.span.with_hi(ty.span.hi()), "String".to_string(), )]; + sugg_is_str_to_string = true; } Some(Res::PrimTy(..)) => {} Some(Res::Def( @@ -3901,6 +3909,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { lt.span.with_hi(ty.span.hi()), "String".to_string(), )]; + sugg_is_str_to_string = true; } Res::PrimTy(..) => {} Res::Def( @@ -3935,6 +3944,12 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } if owned_sugg { + if let Some(span) = + self.find_ref_prefix_span_for_owned_suggestion(lt.span) + && !sugg_is_str_to_string + { + sugg = vec![(span, String::new())]; + } err.multipart_suggestion_verbose( format!("{pre} to return an owned value"), sugg, @@ -3961,6 +3976,23 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } } + + fn find_ref_prefix_span_for_owned_suggestion(&self, lifetime: Span) -> Option { + let mut finder = RefPrefixSpanFinder { lifetime, span: None }; + if let Some(item) = self.diag_metadata.current_item { + finder.visit_item(item); + } else if let Some((kind, _span)) = self.diag_metadata.current_function + && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = kind + { + for param in &sig.decl.inputs { + finder.visit_ty(¶m.ty); + } + if let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output { + finder.visit_ty(ret_ty); + } + } + finder.span + } } fn mk_where_bound_predicate( @@ -4058,6 +4090,26 @@ impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> { } } +struct RefPrefixSpanFinder { + lifetime: Span, + span: Option, +} + +impl<'ast> Visitor<'ast> for RefPrefixSpanFinder { + fn visit_ty(&mut self, t: &'ast Ty) { + if self.span.is_some() { + return; + } + if let TyKind::Ref(_, mut_ty) | TyKind::PinnedRef(_, mut_ty) = &t.kind + && t.span.lo() == self.lifetime.lo() + { + self.span = Some(t.span.with_hi(mut_ty.ty.span.lo())); + return; + } + walk_ty(self, t); + } +} + /// Shadowing involving a label is only a warning for historical reasons. //FIXME: make this a proper lint. pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) { diff --git a/tests/ui/lifetimes/mut-ref-owned-suggestion.rs b/tests/ui/lifetimes/mut-ref-owned-suggestion.rs new file mode 100644 index 0000000000000..ae5e8f6658b63 --- /dev/null +++ b/tests/ui/lifetimes/mut-ref-owned-suggestion.rs @@ -0,0 +1,29 @@ +//! Regression test for +//! Tests that `&mut T` suggests `T`, not `mut T`, `&mut str` suggests `String`, not `str`, +//! when recommending an owned value. +fn with_fn(_f: impl Fn() -> &mut ()) {} +//~^ ERROR: missing lifetime specifier + +fn with_ref_mut_str(_f: impl Fn() -> &mut str) {} +//~^ ERROR: missing lifetime specifier + +fn with_fn_has_return(_f: impl Fn() -> &mut ()) -> i32 { + //~^ ERROR: missing lifetime specifier + 2 +} + +fn with_dyn(_f: Box &mut i32>) {} +//~^ ERROR: missing lifetime specifier + +fn trait_bound &mut i32>(_f: F) {} +//~^ ERROR: missing lifetime specifier + +fn nested_result(_f: impl Fn() -> Result<&mut i32, ()>) {} +//~^ ERROR: missing lifetime specifier + +struct Holder &mut i32> { + //~^ ERROR: missing lifetime specifier + f: F, +} + +fn main() {} diff --git a/tests/ui/lifetimes/mut-ref-owned-suggestion.stderr b/tests/ui/lifetimes/mut-ref-owned-suggestion.stderr new file mode 100644 index 0000000000000..a3e58331342a9 --- /dev/null +++ b/tests/ui/lifetimes/mut-ref-owned-suggestion.stderr @@ -0,0 +1,137 @@ +error[E0106]: missing lifetime specifier + --> $DIR/mut-ref-owned-suggestion.rs:4:29 + | +LL | fn with_fn(_f: impl Fn() -> &mut ()) {} + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` + | +LL | fn with_fn(_f: impl Fn() -> &'static mut ()) {} + | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - fn with_fn(_f: impl Fn() -> &mut ()) {} +LL + fn with_fn(_f: impl Fn() -> ()) {} + | + +error[E0106]: missing lifetime specifier + --> $DIR/mut-ref-owned-suggestion.rs:7:38 + | +LL | fn with_ref_mut_str(_f: impl Fn() -> &mut str) {} + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` + | +LL | fn with_ref_mut_str(_f: impl Fn() -> &'static mut str) {} + | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - fn with_ref_mut_str(_f: impl Fn() -> &mut str) {} +LL + fn with_ref_mut_str(_f: impl Fn() -> String) {} + | + +error[E0106]: missing lifetime specifier + --> $DIR/mut-ref-owned-suggestion.rs:10:40 + | +LL | fn with_fn_has_return(_f: impl Fn() -> &mut ()) -> i32 { + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` + | +LL | fn with_fn_has_return(_f: impl Fn() -> &'static mut ()) -> i32 { + | +++++++ +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL - fn with_fn_has_return(_f: impl Fn() -> &mut ()) -> i32 { +LL + fn with_fn_has_return(_f: impl for<'a> Fn() -> &'a ()) -> i32 { + | +help: consider introducing a named lifetime parameter + | +LL - fn with_fn_has_return(_f: impl Fn() -> &mut ()) -> i32 { +LL + fn with_fn_has_return<'a>(_f: impl Fn() -> &'a ()) -> i32 { + | +help: alternatively, you might want to return an owned value + | +LL - fn with_fn_has_return(_f: impl Fn() -> &mut ()) -> i32 { +LL + fn with_fn_has_return(_f: impl Fn() -> ()) -> i32 { + | + +error[E0106]: missing lifetime specifier + --> $DIR/mut-ref-owned-suggestion.rs:15:33 + | +LL | fn with_dyn(_f: Box &mut i32>) {} + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` + | +LL | fn with_dyn(_f: Box &'static mut i32>) {} + | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - fn with_dyn(_f: Box &mut i32>) {} +LL + fn with_dyn(_f: Box i32>) {} + | + +error[E0106]: missing lifetime specifier + --> $DIR/mut-ref-owned-suggestion.rs:18:27 + | +LL | fn trait_bound &mut i32>(_f: F) {} + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` + | +LL | fn trait_bound &'static mut i32>(_f: F) {} + | +++++++ +help: instead, you are more likely to want to change the argument to be borrowed... + | +LL | fn trait_bound &mut i32>(_f: &F) {} + | + +help: ...or alternatively, you might want to return an owned value + | +LL - fn trait_bound &mut i32>(_f: F) {} +LL + fn trait_bound i32>(_f: F) {} + | + +error[E0106]: missing lifetime specifier + --> $DIR/mut-ref-owned-suggestion.rs:21:42 + | +LL | fn nested_result(_f: impl Fn() -> Result<&mut i32, ()>) {} + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` + | +LL | fn nested_result(_f: impl Fn() -> Result<&'static mut i32, ()>) {} + | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - fn nested_result(_f: impl Fn() -> Result<&mut i32, ()>) {} +LL + fn nested_result(_f: impl Fn() -> Result) {} + | + +error[E0106]: missing lifetime specifier + --> $DIR/mut-ref-owned-suggestion.rs:24:26 + | +LL | struct Holder &mut i32> { + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` + | +LL | struct Holder &'static mut i32> { + | +++++++ +help: instead, you are more likely to want to return an owned value + | +LL - struct Holder &mut i32> { +LL + struct Holder i32> { + | + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0106`.