Skip to content

Commit de67990

Browse files
committed
Tweak 'static suggestion code
Fix #71196.
1 parent df768c5 commit de67990

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

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

src/librustc_resolve/late/lifetimes.rs

+13-39
Original file line numberDiff line numberDiff line change
@@ -2385,51 +2385,26 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
23852385
};
23862386

23872387
let mut err = self.report_missing_lifetime_specifiers(span, lifetime_refs.len());
2388-
let mut add_label = true;
23892388

23902389
if let Some(params) = error {
2391-
if lifetime_refs.len() == 1 {
2392-
add_label = add_label && self.report_elision_failure(&mut err, params, span);
2390+
if self.report_elision_failure(&mut err, params) && lifetime_names.is_empty() {
2391+
lifetime_names.insert(ast::Ident::from_str("'static"));
23932392
}
23942393
}
2395-
if add_label {
2396-
self.add_missing_lifetime_specifiers_label(
2397-
&mut err,
2398-
span,
2399-
lifetime_refs.len(),
2400-
&lifetime_names,
2401-
error.map(|p| &p[..]).unwrap_or(&[]),
2402-
);
2403-
}
2404-
2394+
self.add_missing_lifetime_specifiers_label(
2395+
&mut err,
2396+
span,
2397+
lifetime_refs.len(),
2398+
&lifetime_names,
2399+
error.map(|p| &p[..]).unwrap_or(&[]),
2400+
);
24052401
err.emit();
24062402
}
24072403

2408-
fn suggest_lifetime(&self, db: &mut DiagnosticBuilder<'_>, span: Span, msg: &str) -> bool {
2409-
match self.tcx.sess.source_map().span_to_snippet(span) {
2410-
Ok(ref snippet) => {
2411-
let (sugg, applicability) = if snippet == "&" {
2412-
("&'static ".to_owned(), Applicability::MachineApplicable)
2413-
} else if snippet == "'_" {
2414-
("'static".to_owned(), Applicability::MachineApplicable)
2415-
} else {
2416-
(format!("{} + 'static", snippet), Applicability::MaybeIncorrect)
2417-
};
2418-
db.span_suggestion(span, msg, sugg, applicability);
2419-
false
2420-
}
2421-
Err(_) => {
2422-
db.help(msg);
2423-
true
2424-
}
2425-
}
2426-
}
2427-
24282404
fn report_elision_failure(
24292405
&mut self,
24302406
db: &mut DiagnosticBuilder<'_>,
24312407
params: &[ElisionFailureInfo],
2432-
span: Span,
24332408
) -> bool {
24342409
let mut m = String::new();
24352410
let len = params.len();
@@ -2479,29 +2454,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
24792454
"this function's return type contains a borrowed value, \
24802455
but there is no value for it to be borrowed from",
24812456
);
2482-
self.suggest_lifetime(db, span, "consider giving it a 'static lifetime")
2457+
true
24832458
} else if elided_len == 0 {
24842459
db.help(
24852460
"this function's return type contains a borrowed value with \
24862461
an elided lifetime, but the lifetime cannot be derived from \
24872462
the arguments",
24882463
);
2489-
let msg = "consider giving it an explicit bounded or 'static lifetime";
2490-
self.suggest_lifetime(db, span, msg)
2464+
true
24912465
} else if elided_len == 1 {
24922466
db.help(&format!(
24932467
"this function's return type contains a borrowed value, \
24942468
but the signature does not say which {} it is borrowed from",
24952469
m
24962470
));
2497-
true
2471+
false
24982472
} else {
24992473
db.help(&format!(
25002474
"this function's return type contains a borrowed value, \
25012475
but the signature does not say whether it is borrowed from {}",
25022476
m
25032477
));
2504-
true
2478+
false
25052479
}
25062480
}
25072481

src/librustc_typeck/astconv.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17271727
self.ast_region_to_region(lifetime, None)
17281728
} else {
17291729
self.re_infer(None, span).unwrap_or_else(|| {
1730+
// FIXME: these can be redundant with E0106, but not always.
17301731
struct_span_err!(
17311732
tcx.sess,
17321733
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)