mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-04 19:12:50 +00:00
Rollup merge of #85050 - FabianWolff:issue-84592, r=jackh726
Fix suggestions for missing return type lifetime specifiers
This pull request aims to fix #84592. The issue is that the current code seems to assume that there is only a single relevant span pointing to the missing lifetime, and only looks at the first one:
e5f83d24ae/compiler/rustc_resolve/src/late/lifetimes.rs (L2959)
This is incorrect, though, and leads to incorrect error messages and invalid suggestions. For instance, the example from #84592:
```rust
struct TwoLifetimes<'x, 'y> {
x: &'x (),
y: &'y (),
}
fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> {
TwoLifetimes { x: &(), y: &() }
}
```
currently leads to:
```
error[E0106]: missing lifetime specifiers
--> src/main.rs:6:57
|
6 | fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> {
| --- --- ^^ expected 2 lifetime parameters
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`
help: consider introducing a named lifetime parameter
|
6 | fn two_lifetimes_needed<'a>(a: &'a (), b: &'a ()) -> TwoLifetimes<'_<'a, 'a>, '_> {
| ^^^^ ^^^^^^ ^^^^^^ ^^^^^^^^^^
```
There are two problems:
- The error message is wrong. There is only _one_ lifetime parameter expected at the location pointed to by the error message (and another one at a separate location).
- The suggestion is incorrect and will not lead to correct code.
With the changes in this PR, I get the following output:
```
error[E0106]: missing lifetime specifiers
--> p.rs:6:57
|
6 | fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> {
| --- --- ^^ ^^ expected named lifetime parameter
| |
| 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 `a` or `b`
help: consider introducing a named lifetime parameter
|
6 | fn two_lifetimes_needed<'a>(a: &'a (), b: &'a ()) -> TwoLifetimes<'a, 'a> {
| ^^^^ ^^^^^^ ^^^^^^ ^^ ^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0106`.
```
Mainly, I changed `add_missing_lifetime_specifiers_label()` to receive a _vector_ of spans (and counts) instead of just one, and adjusted its body accordingly.
This commit is contained in:
commit
0740015d59
@ -282,6 +282,22 @@ impl Diagnostic {
|
||||
msg: &str,
|
||||
suggestion: Vec<(Span, String)>,
|
||||
applicability: Applicability,
|
||||
) -> &mut Self {
|
||||
self.multipart_suggestion_with_style(
|
||||
msg,
|
||||
suggestion,
|
||||
applicability,
|
||||
SuggestionStyle::ShowCode,
|
||||
)
|
||||
}
|
||||
|
||||
/// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
|
||||
pub fn multipart_suggestion_with_style(
|
||||
&mut self,
|
||||
msg: &str,
|
||||
suggestion: Vec<(Span, String)>,
|
||||
applicability: Applicability,
|
||||
style: SuggestionStyle,
|
||||
) -> &mut Self {
|
||||
assert!(!suggestion.is_empty());
|
||||
self.suggestions.push(CodeSuggestion {
|
||||
@ -292,7 +308,7 @@ impl Diagnostic {
|
||||
.collect(),
|
||||
}],
|
||||
msg: msg.to_owned(),
|
||||
style: SuggestionStyle::ShowCode,
|
||||
style,
|
||||
applicability,
|
||||
tool_metadata: Default::default(),
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ use rustc_ast::visit::FnKind;
|
||||
use rustc_ast::{self as ast, Expr, ExprKind, Item, ItemKind, NodeId, Path, Ty, TyKind};
|
||||
use rustc_ast_pretty::pprust::path_segment_to_string;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, SuggestionStyle};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Namespace::{self, *};
|
||||
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
|
||||
@ -1687,12 +1687,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||
crate fn report_missing_lifetime_specifiers(
|
||||
&self,
|
||||
span: Span,
|
||||
spans: Vec<Span>,
|
||||
count: usize,
|
||||
) -> DiagnosticBuilder<'tcx> {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
spans,
|
||||
E0106,
|
||||
"missing lifetime specifier{}",
|
||||
pluralize!(count)
|
||||
@ -1821,81 +1821,107 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||
crate fn add_missing_lifetime_specifiers_label(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
span: Span,
|
||||
count: usize,
|
||||
spans_with_counts: Vec<(Span, usize)>,
|
||||
lifetime_names: &FxHashSet<Symbol>,
|
||||
lifetime_spans: Vec<Span>,
|
||||
params: &[ElisionFailureInfo],
|
||||
) {
|
||||
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
|
||||
let snippets: Vec<Option<String>> = spans_with_counts
|
||||
.iter()
|
||||
.map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok())
|
||||
.collect();
|
||||
|
||||
err.span_label(
|
||||
span,
|
||||
&format!(
|
||||
"expected {} lifetime parameter{}",
|
||||
if count == 1 { "named".to_string() } else { count.to_string() },
|
||||
pluralize!(count)
|
||||
),
|
||||
);
|
||||
for (span, count) in &spans_with_counts {
|
||||
err.span_label(
|
||||
*span,
|
||||
format!(
|
||||
"expected {} lifetime parameter{}",
|
||||
if *count == 1 { "named".to_string() } else { count.to_string() },
|
||||
pluralize!(*count),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let suggest_existing = |err: &mut DiagnosticBuilder<'_>,
|
||||
name: &str,
|
||||
formatter: &dyn Fn(&str) -> String| {
|
||||
if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) =
|
||||
self.missing_named_lifetime_spots.iter().rev().next()
|
||||
{
|
||||
// When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest
|
||||
// using `'a`, but also introduce the concept of HRLTs by suggesting
|
||||
// `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404)
|
||||
let mut introduce_suggestion = vec![];
|
||||
let suggest_existing =
|
||||
|err: &mut DiagnosticBuilder<'_>,
|
||||
name: &str,
|
||||
formatters: Vec<Option<Box<dyn Fn(&str) -> String>>>| {
|
||||
if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) =
|
||||
self.missing_named_lifetime_spots.iter().rev().next()
|
||||
{
|
||||
// When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest
|
||||
// using `'a`, but also introduce the concept of HRLTs by suggesting
|
||||
// `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404)
|
||||
let mut introduce_suggestion = vec![];
|
||||
|
||||
let a_to_z_repeat_n = |n| {
|
||||
(b'a'..=b'z').map(move |c| {
|
||||
let mut s = '\''.to_string();
|
||||
s.extend(std::iter::repeat(char::from(c)).take(n));
|
||||
s
|
||||
})
|
||||
};
|
||||
let a_to_z_repeat_n = |n| {
|
||||
(b'a'..=b'z').map(move |c| {
|
||||
let mut s = '\''.to_string();
|
||||
s.extend(std::iter::repeat(char::from(c)).take(n));
|
||||
s
|
||||
})
|
||||
};
|
||||
|
||||
// If all single char lifetime names are present, we wrap around and double the chars.
|
||||
let lt_name = (1..)
|
||||
.flat_map(a_to_z_repeat_n)
|
||||
.find(|lt| !lifetime_names.contains(&Symbol::intern(<)))
|
||||
.unwrap();
|
||||
let msg = format!(
|
||||
"consider making the {} lifetime-generic with a new `{}` lifetime",
|
||||
span_type.descr(),
|
||||
lt_name,
|
||||
);
|
||||
err.note(
|
||||
"for more information on higher-ranked polymorphism, visit \
|
||||
// If all single char lifetime names are present, we wrap around and double the chars.
|
||||
let lt_name = (1..)
|
||||
.flat_map(a_to_z_repeat_n)
|
||||
.find(|lt| !lifetime_names.contains(&Symbol::intern(<)))
|
||||
.unwrap();
|
||||
let msg = format!(
|
||||
"consider making the {} lifetime-generic with a new `{}` lifetime",
|
||||
span_type.descr(),
|
||||
lt_name,
|
||||
);
|
||||
err.note(
|
||||
"for more information on higher-ranked polymorphism, visit \
|
||||
https://doc.rust-lang.org/nomicon/hrtb.html",
|
||||
);
|
||||
let for_sugg = span_type.suggestion(<_name);
|
||||
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("&'") {
|
||||
introduce_suggestion
|
||||
.push((param.span, format!("&{} {}", lt_name, &snippet[1..])));
|
||||
} else if let Some(stripped) = snippet.strip_prefix("&'_ ") {
|
||||
introduce_suggestion
|
||||
.push((param.span, format!("&{} {}", lt_name, stripped)));
|
||||
);
|
||||
let for_sugg = span_type.suggestion(<_name);
|
||||
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("&'") {
|
||||
introduce_suggestion
|
||||
.push((param.span, format!("&{} {}", lt_name, &snippet[1..])));
|
||||
} else if let Some(stripped) = snippet.strip_prefix("&'_ ") {
|
||||
introduce_suggestion
|
||||
.push((param.span, format!("&{} {}", lt_name, stripped)));
|
||||
}
|
||||
}
|
||||
}
|
||||
introduce_suggestion.push((*for_span, for_sugg));
|
||||
for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) {
|
||||
if let Some(formatter) = formatter {
|
||||
introduce_suggestion.push((*span, formatter(<_name)));
|
||||
}
|
||||
}
|
||||
err.multipart_suggestion_with_style(
|
||||
&msg,
|
||||
introduce_suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
SuggestionStyle::ShowAlways,
|
||||
);
|
||||
}
|
||||
introduce_suggestion.push((*for_span, for_sugg));
|
||||
introduce_suggestion.push((span, formatter(<_name)));
|
||||
err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
|
||||
}
|
||||
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
&format!("consider using the `{}` lifetime", lifetime_names.iter().next().unwrap()),
|
||||
formatter(name),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
};
|
||||
let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| {
|
||||
let spans_suggs: Vec<_> = formatters
|
||||
.into_iter()
|
||||
.zip(spans_with_counts.iter())
|
||||
.filter_map(|(fmt, (span, _))| {
|
||||
if let Some(formatter) = fmt { Some((formatter, span)) } else { None }
|
||||
})
|
||||
.map(|(formatter, span)| (*span, formatter(name)))
|
||||
.collect();
|
||||
err.multipart_suggestion_with_style(
|
||||
&format!(
|
||||
"consider using the `{}` lifetime",
|
||||
lifetime_names.iter().next().unwrap()
|
||||
),
|
||||
spans_suggs,
|
||||
Applicability::MaybeIncorrect,
|
||||
SuggestionStyle::ShowAlways,
|
||||
);
|
||||
};
|
||||
let suggest_new = |err: &mut DiagnosticBuilder<'_>, suggs: Vec<Option<String>>| {
|
||||
for missing in self.missing_named_lifetime_spots.iter().rev() {
|
||||
let mut introduce_suggestion = vec![];
|
||||
let msg;
|
||||
@ -1940,38 +1966,44 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||
(*span, span_type.suggestion("'a"))
|
||||
}
|
||||
MissingLifetimeSpot::Static => {
|
||||
let (span, sugg) = match snippet.as_deref() {
|
||||
Some("&") => (span.shrink_to_hi(), "'static ".to_owned()),
|
||||
Some("'_") => (span, "'static".to_owned()),
|
||||
Some(snippet) if !snippet.ends_with('>') => {
|
||||
if snippet == "" {
|
||||
(
|
||||
span,
|
||||
std::iter::repeat("'static")
|
||||
.take(count)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
span.shrink_to_hi(),
|
||||
format!(
|
||||
"<{}>",
|
||||
let mut spans_suggs = Vec::new();
|
||||
for ((span, count), snippet) in
|
||||
spans_with_counts.iter().copied().zip(snippets.iter())
|
||||
{
|
||||
let (span, sugg) = match snippet.as_deref() {
|
||||
Some("&") => (span.shrink_to_hi(), "'static ".to_owned()),
|
||||
Some("'_") => (span, "'static".to_owned()),
|
||||
Some(snippet) if !snippet.ends_with('>') => {
|
||||
if snippet == "" {
|
||||
(
|
||||
span,
|
||||
std::iter::repeat("'static")
|
||||
.take(count)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
)
|
||||
.join(", "),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
span.shrink_to_hi(),
|
||||
format!(
|
||||
"<{}>",
|
||||
std::iter::repeat("'static")
|
||||
.take(count)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
_ => continue,
|
||||
};
|
||||
spans_suggs.push((span, sugg.to_string()));
|
||||
}
|
||||
err.multipart_suggestion_with_style(
|
||||
"consider using the `'static` lifetime",
|
||||
sugg.to_string(),
|
||||
spans_suggs,
|
||||
Applicability::MaybeIncorrect,
|
||||
SuggestionStyle::ShowAlways,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@ -1986,8 +2018,17 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
introduce_suggestion.push((span, sugg.to_string()));
|
||||
err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
|
||||
for ((span, _), sugg) in spans_with_counts.iter().copied().zip(suggs.iter()) {
|
||||
if let Some(sugg) = sugg {
|
||||
introduce_suggestion.push((span, sugg.to_string()));
|
||||
}
|
||||
}
|
||||
err.multipart_suggestion_with_style(
|
||||
&msg,
|
||||
introduce_suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
SuggestionStyle::ShowAlways,
|
||||
);
|
||||
if should_break {
|
||||
break;
|
||||
}
|
||||
@ -1995,68 +2036,75 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||
};
|
||||
|
||||
let lifetime_names: Vec<_> = lifetime_names.iter().collect();
|
||||
match (&lifetime_names[..], snippet.as_deref()) {
|
||||
([name], Some("&")) => {
|
||||
suggest_existing(err, &name.as_str()[..], &|name| format!("&{} ", name));
|
||||
match &lifetime_names[..] {
|
||||
[name] => {
|
||||
let mut suggs: Vec<Option<Box<dyn Fn(&str) -> String>>> = Vec::new();
|
||||
for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied())
|
||||
{
|
||||
suggs.push(match snippet.as_deref() {
|
||||
Some("&") => Some(Box::new(|name| format!("&{} ", name))),
|
||||
Some("'_") => Some(Box::new(|n| n.to_string())),
|
||||
Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))),
|
||||
Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| {
|
||||
format!(
|
||||
"{}<{}>",
|
||||
snippet,
|
||||
std::iter::repeat(name.to_string())
|
||||
.take(count)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
})),
|
||||
_ => None,
|
||||
});
|
||||
}
|
||||
suggest_existing(err, &name.as_str()[..], suggs);
|
||||
}
|
||||
([name], Some("'_")) => {
|
||||
suggest_existing(err, &name.as_str()[..], &|n| n.to_string());
|
||||
}
|
||||
([name], Some("")) => {
|
||||
suggest_existing(err, &name.as_str()[..], &|n| format!("{}, ", n).repeat(count));
|
||||
}
|
||||
([name], Some(snippet)) if !snippet.ends_with('>') => {
|
||||
let f = |name: &str| {
|
||||
format!(
|
||||
"{}<{}>",
|
||||
snippet,
|
||||
std::iter::repeat(name.to_string())
|
||||
.take(count)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
};
|
||||
suggest_existing(err, &name.as_str()[..], &f);
|
||||
}
|
||||
([], Some("&")) if count == 1 => {
|
||||
suggest_new(err, "&'a ");
|
||||
}
|
||||
([], Some("'_")) if count == 1 => {
|
||||
suggest_new(err, "'a");
|
||||
}
|
||||
([], Some(snippet)) if !snippet.ends_with('>') => {
|
||||
if snippet == "" {
|
||||
// This happens when we have `type Bar<'a> = Foo<T>` where we point at the space
|
||||
// before `T`. We will suggest `type Bar<'a> = Foo<'a, T>`.
|
||||
suggest_new(
|
||||
err,
|
||||
&std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""),
|
||||
);
|
||||
} else {
|
||||
suggest_new(
|
||||
err,
|
||||
&format!(
|
||||
[] => {
|
||||
let mut suggs = Vec::new();
|
||||
for (snippet, (_, count)) in
|
||||
snippets.iter().cloned().zip(spans_with_counts.iter().copied())
|
||||
{
|
||||
suggs.push(match snippet.as_deref() {
|
||||
Some("&") => Some("&'a ".to_string()),
|
||||
Some("'_") => Some("'a".to_string()),
|
||||
Some("") => {
|
||||
Some(std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""))
|
||||
}
|
||||
Some(snippet) => Some(format!(
|
||||
"{}<{}>",
|
||||
snippet,
|
||||
std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", ")
|
||||
),
|
||||
);
|
||||
std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "),
|
||||
)),
|
||||
None => None,
|
||||
});
|
||||
}
|
||||
suggest_new(err, suggs);
|
||||
}
|
||||
(lts, ..) if lts.len() > 1 => {
|
||||
lts if lts.len() > 1 => {
|
||||
err.span_note(lifetime_spans, "these named lifetimes are available to use");
|
||||
if Some("") == snippet.as_deref() {
|
||||
|
||||
let mut spans_suggs: Vec<_> = Vec::new();
|
||||
for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) {
|
||||
match snippet.as_deref() {
|
||||
Some("") => spans_suggs.push((span, "'lifetime, ".to_string())),
|
||||
Some("&") => spans_suggs.push((span, "&'lifetime ".to_string())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if spans_suggs.len() > 0 {
|
||||
// This happens when we have `Foo<T>` where we point at the space before `T`,
|
||||
// but this can be confusing so we give a suggestion with placeholders.
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
err.multipart_suggestion_with_style(
|
||||
"consider using one of the available lifetimes here",
|
||||
"'lifetime, ".repeat(count),
|
||||
spans_suggs,
|
||||
Applicability::HasPlaceholders,
|
||||
SuggestionStyle::ShowAlways,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2956,7 +2956,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
let span = lifetime_refs[0].span;
|
||||
let mut late_depth = 0;
|
||||
let mut scope = self.scope;
|
||||
let mut lifetime_names = FxHashSet::default();
|
||||
@ -3035,7 +3034,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut err = self.report_missing_lifetime_specifiers(span, lifetime_refs.len());
|
||||
let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
|
||||
spans.sort();
|
||||
let mut spans_dedup = spans.clone();
|
||||
spans_dedup.dedup();
|
||||
let spans_with_counts: Vec<_> = spans_dedup
|
||||
.into_iter()
|
||||
.map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
|
||||
.collect();
|
||||
|
||||
let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len());
|
||||
|
||||
if let Some(params) = error {
|
||||
// If there's no lifetime available, suggest `'static`.
|
||||
@ -3043,10 +3051,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||
lifetime_names.insert(kw::StaticLifetime);
|
||||
}
|
||||
}
|
||||
|
||||
self.add_missing_lifetime_specifiers_label(
|
||||
&mut err,
|
||||
span,
|
||||
lifetime_refs.len(),
|
||||
spans_with_counts,
|
||||
&lifetime_names,
|
||||
lifetime_spans,
|
||||
error.unwrap_or(&[]),
|
||||
|
17
src/test/ui/suggestions/issue-84592.rs
Normal file
17
src/test/ui/suggestions/issue-84592.rs
Normal file
@ -0,0 +1,17 @@
|
||||
/* Checks whether issue #84592 has been resolved. The issue was
|
||||
* that in this example, there are two expected/missing lifetime
|
||||
* parameters with *different spans*, leading to incorrect
|
||||
* suggestions from rustc.
|
||||
*/
|
||||
|
||||
struct TwoLifetimes<'x, 'y> {
|
||||
x: &'x (),
|
||||
y: &'y (),
|
||||
}
|
||||
|
||||
fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> {
|
||||
//~^ ERROR missing lifetime specifiers [E0106]
|
||||
TwoLifetimes { x: &(), y: &() }
|
||||
}
|
||||
|
||||
fn main() {}
|
17
src/test/ui/suggestions/issue-84592.stderr
Normal file
17
src/test/ui/suggestions/issue-84592.stderr
Normal file
@ -0,0 +1,17 @@
|
||||
error[E0106]: missing lifetime specifiers
|
||||
--> $DIR/issue-84592.rs:12:57
|
||||
|
|
||||
LL | fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> {
|
||||
| --- --- ^^ ^^ expected named lifetime parameter
|
||||
| |
|
||||
| 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 `a` or `b`
|
||||
help: consider introducing a named lifetime parameter
|
||||
|
|
||||
LL | fn two_lifetimes_needed<'a>(a: &'a (), b: &'a ()) -> TwoLifetimes<'a, 'a> {
|
||||
| ^^^^ ^^^^^^ ^^^^^^ ^^ ^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0106`.
|
@ -44,6 +44,10 @@ note: these named lifetimes are available to use
|
||||
|
|
||||
LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X);
|
||||
| ^^ ^^
|
||||
help: consider using one of the available lifetimes here
|
||||
|
|
||||
LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &'lifetime X);
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/missing-lt-for-hrtb.rs:5:41
|
||||
|
37
src/test/ui/suggestions/return-elided-lifetime.rs
Normal file
37
src/test/ui/suggestions/return-elided-lifetime.rs
Normal file
@ -0,0 +1,37 @@
|
||||
/* Checks all four scenarios possible in report_elision_failure() of
|
||||
* rustc_resolve::late::lifetimes::LifetimeContext related to returning
|
||||
* borrowed values, in various configurations.
|
||||
*/
|
||||
|
||||
fn f1() -> &i32 { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
fn f1_() -> (&i32, &i32) { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
//~^^ ERROR missing lifetime specifier [E0106]
|
||||
|
||||
fn f2(a: i32, b: i32) -> &i32 { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
//~^^ ERROR missing lifetime specifier [E0106]
|
||||
|
||||
struct S<'a, 'b> { a: &'a i32, b: &'b i32 }
|
||||
fn f3(s: &S) -> &i32 { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
//~^^ ERROR missing lifetime specifier [E0106]
|
||||
|
||||
fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
//~^^ ERROR missing lifetime specifier [E0106]
|
||||
|
||||
fn f5<'a>(a: &'a i32, b: &i32) -> &i32 { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} }
|
||||
//~^ ERROR missing lifetime specifier [E0106]
|
||||
//~^^ ERROR missing lifetime specifier [E0106]
|
||||
|
||||
fn main() {}
|
198
src/test/ui/suggestions/return-elided-lifetime.stderr
Normal file
198
src/test/ui/suggestions/return-elided-lifetime.stderr
Normal file
@ -0,0 +1,198 @@
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:6:12
|
||||
|
|
||||
LL | fn f1() -> &i32 { loop {} }
|
||||
| ^ 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
|
||||
|
|
||||
LL | fn f1() -> &'static i32 { loop {} }
|
||||
| ^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:8:14
|
||||
|
|
||||
LL | fn f1_() -> (&i32, &i32) { loop {} }
|
||||
| ^ 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
|
||||
|
|
||||
LL | fn f1_() -> (&'static i32, &i32) { loop {} }
|
||||
| ^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:8:20
|
||||
|
|
||||
LL | fn f1_() -> (&i32, &i32) { loop {} }
|
||||
| ^ 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
|
||||
|
|
||||
LL | fn f1_() -> (&i32, &'static i32) { loop {} }
|
||||
| ^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:12:26
|
||||
|
|
||||
LL | fn f2(a: i32, b: i32) -> &i32 { loop {} }
|
||||
| ^ expected named lifetime parameter
|
||||
|
|
||||
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
|
||||
help: consider using the `'static` lifetime
|
||||
|
|
||||
LL | fn f2(a: i32, b: i32) -> &'static i32 { loop {} }
|
||||
| ^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:14:28
|
||||
|
|
||||
LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
|
||||
| ^ expected named lifetime parameter
|
||||
|
|
||||
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
|
||||
help: consider using the `'static` lifetime
|
||||
|
|
||||
LL | fn f2_(a: i32, b: i32) -> (&'static i32, &i32) { loop {} }
|
||||
| ^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:14:34
|
||||
|
|
||||
LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} }
|
||||
| ^ expected named lifetime parameter
|
||||
|
|
||||
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
|
||||
help: consider using the `'static` lifetime
|
||||
|
|
||||
LL | fn f2_(a: i32, b: i32) -> (&i32, &'static i32) { loop {} }
|
||||
| ^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:19:17
|
||||
|
|
||||
LL | fn f3(s: &S) -> &i32 { loop {} }
|
||||
| -- ^ expected named lifetime parameter
|
||||
|
|
||||
= help: this function's return type contains a borrowed value, but the signature does not say which one of `s`'s 3 lifetimes it is borrowed from
|
||||
help: consider introducing a named lifetime parameter
|
||||
|
|
||||
LL | fn f3<'a>(s: &'a S) -> &'a i32 { loop {} }
|
||||
| ^^^^ ^^^^^ ^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:21:26
|
||||
|
|
||||
LL | fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} }
|
||||
| -- -- ^ 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 one of `s`'s 3 lifetimes or one of `t`'s 3 lifetimes
|
||||
help: consider introducing a named lifetime parameter
|
||||
|
|
||||
LL | fn f3_<'a>(s: &'a S, t: &'a S) -> (&'a i32, &i32) { loop {} }
|
||||
| ^^^^ ^^^^^ ^^^^^ ^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:21:32
|
||||
|
|
||||
LL | fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} }
|
||||
| -- -- ^ 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 one of `s`'s 3 lifetimes or one of `t`'s 3 lifetimes
|
||||
help: consider introducing a named lifetime parameter
|
||||
|
|
||||
LL | fn f3_<'a>(s: &'a S, t: &'a S) -> (&i32, &'a i32) { loop {} }
|
||||
| ^^^^ ^^^^^ ^^^^^ ^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:25:42
|
||||
|
|
||||
LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} }
|
||||
| ------- ------- ^ 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 `a` or `b`
|
||||
note: these named lifetimes are available to use
|
||||
--> $DIR/return-elided-lifetime.rs:25:7
|
||||
|
|
||||
LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} }
|
||||
| ^^ ^^
|
||||
help: consider using one of the available lifetimes here
|
||||
|
|
||||
LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &'lifetime i32 { loop {} }
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:27:44
|
||||
|
|
||||
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
|
||||
| ------- ------- ^ 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 `a` or `b`
|
||||
note: these named lifetimes are available to use
|
||||
--> $DIR/return-elided-lifetime.rs:27:8
|
||||
|
|
||||
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
|
||||
| ^^ ^^
|
||||
help: consider using one of the available lifetimes here
|
||||
|
|
||||
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&'lifetime i32, &i32) { loop {} }
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:27:50
|
||||
|
|
||||
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
|
||||
| ------- ------- ^ 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 `a` or `b`
|
||||
note: these named lifetimes are available to use
|
||||
--> $DIR/return-elided-lifetime.rs:27:8
|
||||
|
|
||||
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} }
|
||||
| ^^ ^^
|
||||
help: consider using one of the available lifetimes here
|
||||
|
|
||||
LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &'lifetime i32) { loop {} }
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:31:35
|
||||
|
|
||||
LL | fn f5<'a>(a: &'a i32, b: &i32) -> &i32 { loop {} }
|
||||
| ------- ---- ^ 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 `a` or `b`
|
||||
help: consider using the `'a` lifetime
|
||||
|
|
||||
LL | fn f5<'a>(a: &'a i32, b: &i32) -> &'a i32 { loop {} }
|
||||
| ^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:33:37
|
||||
|
|
||||
LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} }
|
||||
| ------- ---- ^ 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 `a` or `b`
|
||||
help: consider using the `'a` lifetime
|
||||
|
|
||||
LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&'a i32, &i32) { loop {} }
|
||||
| ^^^
|
||||
|
||||
error[E0106]: missing lifetime specifier
|
||||
--> $DIR/return-elided-lifetime.rs:33:43
|
||||
|
|
||||
LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} }
|
||||
| ------- ---- ^ 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 `a` or `b`
|
||||
help: consider using the `'a` lifetime
|
||||
|
|
||||
LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &'a i32) { loop {} }
|
||||
| ^^^
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0106`.
|
Loading…
Reference in New Issue
Block a user