Skip to content

Commit

Permalink
Fix suggestions for missing return type lifetime parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
FabianWolff committed May 7, 2021
1 parent e5f83d2 commit 439ef6d
Show file tree
Hide file tree
Showing 9 changed files with 514 additions and 151 deletions.
24 changes: 24 additions & 0 deletions compiler/rustc_errors/src/diagnostic.rs
Expand Up @@ -299,6 +299,30 @@ impl Diagnostic {
self
}

/// [`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 {
substitutions: vec![Substitution {
parts: suggestion
.into_iter()
.map(|(span, snippet)| SubstitutionPart { snippet, span })
.collect(),
}],
msg: msg.to_owned(),
style,
applicability,
tool_metadata: Default::default(),
});
self
}

/// Prints out a message with for a multipart suggestion without showing the suggested code.
///
/// This is intended to be used for suggestions that are obvious in what the changes need to
Expand Down
351 changes: 205 additions & 146 deletions compiler/rustc_resolve/src/late/diagnostics.rs

Large diffs are not rendered by default.

15 changes: 11 additions & 4 deletions compiler/rustc_resolve/src/late/lifetimes.rs
Expand Up @@ -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();
Expand Down Expand Up @@ -3035,18 +3034,26 @@ 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 counts: Vec<_> =
spans_dedup.iter().map(|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`.
if self.report_elision_failure(&mut err, params) && lifetime_names.is_empty() {
lifetime_names.insert(kw::StaticLifetime);
}
}

self.add_missing_lifetime_specifiers_label(
&mut err,
span,
lifetime_refs.len(),
spans,
counts,
&lifetime_names,
lifetime_spans,
error.unwrap_or(&[]),
Expand Down
17 changes: 17 additions & 0 deletions src/test/ui/issues/issue-84592.rs
@@ -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 changes: 17 additions & 0 deletions src/test/ui/issues/issue-84592.stderr
@@ -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`.
37 changes: 37 additions & 0 deletions src/test/ui/return-elided-lifetime.rs
@@ -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 changes: 198 additions & 0 deletions src/test/ui/return-elided-lifetime.stderr
@@ -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`.
4 changes: 4 additions & 0 deletions src/test/ui/suggestions/missing-lt-for-hrtb.stderr
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/tools/tidy/src/ui_tests.rs
Expand Up @@ -8,7 +8,7 @@ use std::path::Path;
const ENTRY_LIMIT: usize = 1000;
// FIXME: The following limits should be reduced eventually.
const ROOT_ENTRY_LIMIT: usize = 1388;
const ISSUES_ENTRY_LIMIT: usize = 2551;
const ISSUES_ENTRY_LIMIT: usize = 2557;

fn check_entries(path: &Path, bad: &mut bool) {
let dirs = walkdir::WalkDir::new(&path.join("test/ui"))
Expand Down

0 comments on commit 439ef6d

Please sign in to comment.