Skip to content

Commit 13c1dae

Browse files
committed
Tweak 'static suggestion code
Fix #71196.
1 parent 82e90d6 commit 13c1dae

17 files changed

+532
-149
lines changed

src/librustc_ast_lowering/path.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
273273
.next();
274274
if !generic_args.parenthesized && !has_lifetimes {
275275
generic_args.args = self
276-
.elided_path_lifetimes(path_span, expected_lifetimes)
276+
.elided_path_lifetimes(
277+
first_generic_span.map(|s| s.shrink_to_lo()).unwrap_or(segment.ident.span),
278+
expected_lifetimes,
279+
)
277280
.map(GenericArg::Lifetime)
278281
.chain(generic_args.args.into_iter())
279282
.collect();

src/librustc_resolve/late/diagnostics.rs

+97-88
Original file line numberDiff line numberDiff line change
@@ -1034,101 +1034,110 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
10341034
lifetime_names: &FxHashSet<ast::Ident>,
10351035
params: &[ElisionFailureInfo],
10361036
) {
1037-
if count > 1 {
1038-
err.span_label(span, format!("expected {} lifetime parameters", count));
1039-
} else {
1040-
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
1041-
let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
1042-
err.span_suggestion(
1043-
span,
1044-
"consider using the named lifetime",
1045-
sugg,
1046-
Applicability::MaybeIncorrect,
1047-
);
1048-
};
1049-
let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| {
1050-
err.span_label(span, "expected named lifetime parameter");
1037+
err.span_label(
1038+
span,
1039+
&format!(
1040+
"expected {} lifetime parameter{}",
1041+
if count == 1 { "named".to_string() } else { count.to_string() },
1042+
pluralize!(count)
1043+
),
1044+
);
10511045

1052-
for missing in self.missing_named_lifetime_spots.iter().rev() {
1053-
let mut introduce_suggestion = vec![];
1054-
let msg;
1055-
let should_break;
1056-
introduce_suggestion.push(match missing {
1057-
MissingLifetimeSpot::Generics(generics) => {
1058-
msg = "consider introducing a named lifetime parameter".to_string();
1059-
should_break = true;
1060-
if let Some(param) = generics.params.iter().find(|p| match p.kind {
1061-
hir::GenericParamKind::Type {
1062-
synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
1063-
..
1064-
} => false,
1065-
_ => true,
1066-
}) {
1067-
(param.span.shrink_to_lo(), "'a, ".to_string())
1068-
} else {
1069-
(generics.span, "<'a>".to_string())
1070-
}
1071-
}
1072-
MissingLifetimeSpot::HigherRanked { span, span_type } => {
1073-
msg = format!(
1074-
"consider making the {} lifetime-generic with a new `'a` lifetime",
1075-
span_type.descr(),
1076-
);
1077-
should_break = false;
1078-
err.note(
1079-
"for more information on higher-ranked polymorphism, visit \
1080-
https://doc.rust-lang.org/nomicon/hrtb.html",
1081-
);
1082-
(*span, span_type.suggestion("'a"))
1083-
}
1084-
});
1085-
for param in params {
1086-
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span)
1087-
{
1088-
if snippet.starts_with('&') && !snippet.starts_with("&'") {
1089-
introduce_suggestion
1090-
.push((param.span, format!("&'a {}", &snippet[1..])));
1091-
} else if snippet.starts_with("&'_ ") {
1092-
introduce_suggestion
1093-
.push((param.span, format!("&'a {}", &snippet[4..])));
1094-
}
1046+
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
1047+
let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
1048+
err.span_suggestion_verbose(
1049+
span,
1050+
"consider using the named lifetime",
1051+
sugg,
1052+
Applicability::MaybeIncorrect,
1053+
);
1054+
};
1055+
let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| {
1056+
for missing in self.missing_named_lifetime_spots.iter().rev() {
1057+
let mut introduce_suggestion = vec![];
1058+
let msg;
1059+
let should_break;
1060+
introduce_suggestion.push(match missing {
1061+
MissingLifetimeSpot::Generics(generics) => {
1062+
msg = "consider introducing a named lifetime parameter".to_string();
1063+
should_break = true;
1064+
if let Some(param) = generics.params.iter().find(|p| match p.kind {
1065+
hir::GenericParamKind::Type {
1066+
synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
1067+
..
1068+
} => false,
1069+
_ => true,
1070+
}) {
1071+
(param.span.shrink_to_lo(), "'a, ".to_string())
1072+
} else {
1073+
(generics.span, "<'a>".to_string())
10951074
}
10961075
}
1097-
introduce_suggestion.push((span, sugg.to_string()));
1098-
err.multipart_suggestion(
1099-
&msg,
1100-
introduce_suggestion,
1101-
Applicability::MaybeIncorrect,
1102-
);
1103-
if should_break {
1104-
break;
1076+
MissingLifetimeSpot::HigherRanked { span, span_type } => {
1077+
msg = format!(
1078+
"consider making the {} lifetime-generic with a new `'a` lifetime",
1079+
span_type.descr(),
1080+
);
1081+
should_break = false;
1082+
err.note(
1083+
"for more information on higher-ranked polymorphism, visit \
1084+
https://doc.rust-lang.org/nomicon/hrtb.html",
1085+
);
1086+
(*span, span_type.suggestion("'a"))
1087+
}
1088+
});
1089+
for param in params {
1090+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) {
1091+
if snippet.starts_with('&') && !snippet.starts_with("&'") {
1092+
introduce_suggestion
1093+
.push((param.span, format!("&'a {}", &snippet[1..])));
1094+
} else if snippet.starts_with("&'_ ") {
1095+
introduce_suggestion
1096+
.push((param.span, format!("&'a {}", &snippet[4..])));
1097+
}
11051098
}
11061099
}
1107-
};
1108-
1109-
match (lifetime_names.len(), lifetime_names.iter().next(), snippet.as_deref()) {
1110-
(1, Some(name), Some("&")) => {
1111-
suggest_existing(err, format!("&{} ", name));
1112-
}
1113-
(1, Some(name), Some("'_")) => {
1114-
suggest_existing(err, name.to_string());
1115-
}
1116-
(1, Some(name), Some(snippet)) if !snippet.ends_with('>') => {
1117-
suggest_existing(err, format!("{}<{}>", snippet, name));
1118-
}
1119-
(0, _, Some("&")) => {
1120-
suggest_new(err, "&'a ");
1121-
}
1122-
(0, _, Some("'_")) => {
1123-
suggest_new(err, "'a");
1124-
}
1125-
(0, _, Some(snippet)) if !snippet.ends_with('>') => {
1126-
suggest_new(err, &format!("{}<'a>", snippet));
1127-
}
1128-
_ => {
1129-
err.span_label(span, "expected lifetime parameter");
1100+
introduce_suggestion.push((span, sugg.to_string()));
1101+
err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
1102+
if should_break {
1103+
break;
11301104
}
11311105
}
1106+
};
1107+
1108+
match (lifetime_names.len(), lifetime_names.iter().next(), snippet.as_deref()) {
1109+
(1, Some(name), Some("&")) => {
1110+
suggest_existing(err, format!("&{} ", name));
1111+
}
1112+
(1, Some(name), Some("'_")) => {
1113+
suggest_existing(err, name.to_string());
1114+
}
1115+
(1, Some(name), Some("")) => {
1116+
suggest_existing(err, format!("{}, ", name).repeat(count));
1117+
}
1118+
(1, Some(name), Some(snippet)) if !snippet.ends_with('>') => {
1119+
suggest_existing(
1120+
err,
1121+
format!(
1122+
"{}<{}>",
1123+
snippet,
1124+
std::iter::repeat(name.to_string())
1125+
.take(count)
1126+
.collect::<Vec<_>>()
1127+
.join(", ")
1128+
),
1129+
);
1130+
}
1131+
(0, _, Some("&")) if count == 1 => {
1132+
suggest_new(err, "&'a ");
1133+
}
1134+
(0, _, Some("'_")) if count == 1 => {
1135+
suggest_new(err, "'a");
1136+
}
1137+
(0, _, Some(snippet)) if !snippet.ends_with('>') && count == 1 => {
1138+
suggest_new(err, &format!("{}<'a>", snippet));
1139+
}
1140+
_ => {}
11321141
}
11331142
}
11341143
}

src/librustc_resolve/late/lifetimes.rs

+13-39
Original file line numberDiff line numberDiff line change
@@ -2388,51 +2388,26 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
23882388
};
23892389

23902390
let mut err = self.report_missing_lifetime_specifiers(span, lifetime_refs.len());
2391-
let mut add_label = true;
23922391

23932392
if let Some(params) = error {
2394-
if lifetime_refs.len() == 1 {
2395-
add_label = add_label && self.report_elision_failure(&mut err, params, span);
2393+
if self.report_elision_failure(&mut err, params) && lifetime_names.is_empty() {
2394+
lifetime_names.insert(ast::Ident::from_str("'static"));
23962395
}
23972396
}
2398-
if add_label {
2399-
self.add_missing_lifetime_specifiers_label(
2400-
&mut err,
2401-
span,
2402-
lifetime_refs.len(),
2403-
&lifetime_names,
2404-
error.map(|p| &p[..]).unwrap_or(&[]),
2405-
);
2406-
}
2407-
2397+
self.add_missing_lifetime_specifiers_label(
2398+
&mut err,
2399+
span,
2400+
lifetime_refs.len(),
2401+
&lifetime_names,
2402+
error.map(|p| &p[..]).unwrap_or(&[]),
2403+
);
24082404
err.emit();
24092405
}
24102406

2411-
fn suggest_lifetime(&self, db: &mut DiagnosticBuilder<'_>, span: Span, msg: &str) -> bool {
2412-
match self.tcx.sess.source_map().span_to_snippet(span) {
2413-
Ok(ref snippet) => {
2414-
let (sugg, applicability) = if snippet == "&" {
2415-
("&'static ".to_owned(), Applicability::MachineApplicable)
2416-
} else if snippet == "'_" {
2417-
("'static".to_owned(), Applicability::MachineApplicable)
2418-
} else {
2419-
(format!("{} + 'static", snippet), Applicability::MaybeIncorrect)
2420-
};
2421-
db.span_suggestion(span, msg, sugg, applicability);
2422-
false
2423-
}
2424-
Err(_) => {
2425-
db.help(msg);
2426-
true
2427-
}
2428-
}
2429-
}
2430-
24312407
fn report_elision_failure(
24322408
&mut self,
24332409
db: &mut DiagnosticBuilder<'_>,
24342410
params: &[ElisionFailureInfo],
2435-
span: Span,
24362411
) -> bool {
24372412
let mut m = String::new();
24382413
let len = params.len();
@@ -2482,29 +2457,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
24822457
"this function's return type contains a borrowed value, \
24832458
but there is no value for it to be borrowed from",
24842459
);
2485-
self.suggest_lifetime(db, span, "consider giving it a 'static lifetime")
2460+
true
24862461
} else if elided_len == 0 {
24872462
db.help(
24882463
"this function's return type contains a borrowed value with \
24892464
an elided lifetime, but the lifetime cannot be derived from \
24902465
the arguments",
24912466
);
2492-
let msg = "consider giving it an explicit bounded or 'static lifetime";
2493-
self.suggest_lifetime(db, span, msg)
2467+
true
24942468
} else if elided_len == 1 {
24952469
db.help(&format!(
24962470
"this function's return type contains a borrowed value, \
24972471
but the signature does not say which {} it is borrowed from",
24982472
m
24992473
));
2500-
true
2474+
false
25012475
} else {
25022476
db.help(&format!(
25032477
"this function's return type contains a borrowed value, \
25042478
but the signature does not say whether it is borrowed from {}",
25052479
m
25062480
));
2507-
true
2481+
false
25082482
}
25092483
}
25102484

src/librustc_typeck/astconv.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1729,6 +1729,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17291729
self.ast_region_to_region(lifetime, None)
17301730
} else {
17311731
self.re_infer(None, span).unwrap_or_else(|| {
1732+
// FIXME: these can be redundant with E0106, but not always.
17321733
struct_span_err!(
17331734
tcx.sess,
17341735
span,

src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
22
--> $DIR/bound-lifetime-in-binding-only.rs:52:23
33
|
44
LL | fn elision<T: Fn() -> &i32>() {
5-
| ^ help: consider giving it a 'static lifetime: `&'static`
5+
| ^ expected named lifetime parameter
66
|
77
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
8+
help: consider using the named lifetime
9+
|
10+
LL | fn elision<T: Fn() -> &'static i32>() {
11+
| ^^^^^^^^
812

913
error: aborting due to previous error
1014

src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
22
--> $DIR/bound-lifetime-in-return-only.rs:34:23
33
|
44
LL | fn elision(_: fn() -> &i32) {
5-
| ^ help: consider giving it a 'static lifetime: `&'static`
5+
| ^ expected named lifetime parameter
66
|
77
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
8+
help: consider using the named lifetime
9+
|
10+
LL | fn elision(_: fn() -> &'static i32) {
11+
| ^^^^^^^^
812

913
error: aborting due to previous error
1014

src/test/ui/async-await/issues/issue-63388-2.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ error[E0106]: missing lifetime specifier
44
LL | foo: &dyn Foo, bar: &'a dyn Foo
55
| -------- -----------
66
LL | ) -> &dyn Foo
7-
| ^ help: consider using the named lifetime: `&'a`
7+
| ^ expected named lifetime parameter
88
|
99
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `foo` or `bar`
10+
help: consider using the named lifetime
11+
|
12+
LL | ) -> &'a dyn Foo
13+
| ^^^
1014

1115
error: aborting due to previous error
1216

src/test/ui/c-variadic/variadic-ffi-6.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
22
--> $DIR/variadic-ffi-6.rs:7:6
33
|
44
LL | ) -> &usize {
5-
| ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
5+
| ^ expected named lifetime parameter
66
|
77
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
8+
help: consider using the named lifetime
9+
|
10+
LL | ) -> &'static usize {
11+
| ^^^^^^^^
812

913
error: aborting due to previous error
1014

src/test/ui/foreign-fn-return-lifetime.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
22
--> $DIR/foreign-fn-return-lifetime.rs:5:19
33
|
44
LL | pub fn f() -> &u8;
5-
| ^ help: consider giving it a 'static lifetime: `&'static`
5+
| ^ expected named lifetime parameter
66
|
77
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
8+
help: consider using the named lifetime
9+
|
10+
LL | pub fn f() -> &'static u8;
11+
| ^^^^^^^^
812

913
error: aborting due to previous error
1014

0 commit comments

Comments
 (0)