Skip to content

Commit 1ba8b13

Browse files
authored
Rollup merge of rust-lang#71235 - estebank:lt-sugg-2, r=ecstatic-morse
Tweak `'static` suggestion code Fix rust-lang#71196.
2 parents d8c235d + 25f8966 commit 1ba8b13

25 files changed

+572
-170
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

+111-87
Original file line numberDiff line numberDiff line change
@@ -1034,101 +1034,125 @@ 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+
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
10511038

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-
}
1039+
err.span_label(
1040+
span,
1041+
&format!(
1042+
"expected {} lifetime parameter{}",
1043+
if count == 1 { "named".to_string() } else { count.to_string() },
1044+
pluralize!(count)
1045+
),
1046+
);
1047+
1048+
let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
1049+
err.span_suggestion_verbose(
1050+
span,
1051+
&format!("consider using the `{}` lifetime", lifetime_names.iter().next().unwrap()),
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())
10951075
}
10961076
}
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;
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+
}
11051099
}
11061100
}
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));
1101+
introduce_suggestion.push((span, sugg.to_string()));
1102+
err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
1103+
if should_break {
1104+
break;
11271105
}
1128-
_ => {
1129-
err.span_label(span, "expected lifetime parameter");
1106+
}
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+
(n, ..) if n > 1 => {
1142+
let spans: Vec<Span> = lifetime_names.iter().map(|lt| lt.span).collect();
1143+
err.span_note(spans, "these named lifetimes are available to use");
1144+
if Some("") == snippet.as_deref() {
1145+
// This happens when we have `Foo<T>` where we point at the space before `T`,
1146+
// but this can be confusing so we give a suggestion with placeholders.
1147+
err.span_suggestion_verbose(
1148+
span,
1149+
"consider using one of the available lifetimes here",
1150+
"'lifetime, ".repeat(count),
1151+
Applicability::HasPlaceholders,
1152+
);
11301153
}
11311154
}
1155+
_ => {}
11321156
}
11331157
}
11341158
}

src/librustc_resolve/late/lifetimes.rs

+15-40
Original file line numberDiff line numberDiff line change
@@ -2388,52 +2388,28 @@ 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 there's no lifetime available, suggest `'static`.
2394+
if self.report_elision_failure(&mut err, params) && lifetime_names.is_empty() {
2395+
lifetime_names.insert(ast::Ident::from_str("'static"));
23962396
}
23972397
}
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-
2398+
self.add_missing_lifetime_specifiers_label(
2399+
&mut err,
2400+
span,
2401+
lifetime_refs.len(),
2402+
&lifetime_names,
2403+
error.map(|p| &p[..]).unwrap_or(&[]),
2404+
);
24082405
err.emit();
24092406
}
24102407

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-
24312408
fn report_elision_failure(
24322409
&mut self,
24332410
db: &mut DiagnosticBuilder<'_>,
24342411
params: &[ElisionFailureInfo],
2435-
span: Span,
2436-
) -> bool {
2412+
) -> bool /* add `'static` lifetime to lifetime list */ {
24372413
let mut m = String::new();
24382414
let len = params.len();
24392415

@@ -2482,29 +2458,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
24822458
"this function's return type contains a borrowed value, \
24832459
but there is no value for it to be borrowed from",
24842460
);
2485-
self.suggest_lifetime(db, span, "consider giving it a 'static lifetime")
2461+
true
24862462
} else if elided_len == 0 {
24872463
db.help(
24882464
"this function's return type contains a borrowed value with \
24892465
an elided lifetime, but the lifetime cannot be derived from \
24902466
the arguments",
24912467
);
2492-
let msg = "consider giving it an explicit bounded or 'static lifetime";
2493-
self.suggest_lifetime(db, span, msg)
2468+
true
24942469
} else if elided_len == 1 {
24952470
db.help(&format!(
24962471
"this function's return type contains a borrowed value, \
24972472
but the signature does not say which {} it is borrowed from",
24982473
m
24992474
));
2500-
true
2475+
false
25012476
} else {
25022477
db.help(&format!(
25032478
"this function's return type contains a borrowed value, \
25042479
but the signature does not say whether it is borrowed from {}",
25052480
m
25062481
));
2507-
true
2482+
false
25082483
}
25092484
}
25102485

src/librustc_span/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,8 @@ impl MultiSpan {
657657
MultiSpan { primary_spans: vec![primary_span], span_labels: vec![] }
658658
}
659659

660-
pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
660+
pub fn from_spans(mut vec: Vec<Span>) -> MultiSpan {
661+
vec.sort();
661662
MultiSpan { primary_spans: vec, span_labels: vec![] }
662663
}
663664

src/librustc_typeck/astconv.rs

+2-11
Original file line numberDiff line numberDiff line change
@@ -1017,18 +1017,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10171017

10181018
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1);
10191019

1020-
let path_span = if let [segment] = &trait_ref.path.segments[..] {
1021-
// FIXME: `trait_ref.path.span` can point to a full path with multiple
1022-
// segments, even though `trait_ref.path.segments` is of length `1`. Work
1023-
// around that bug here, even though it should be fixed elsewhere.
1024-
// This would otherwise cause an invalid suggestion. For an example, look at
1025-
// `src/test/ui/issues/issue-28344.rs`.
1026-
segment.ident.span
1027-
} else {
1028-
trait_ref.path.span
1029-
};
10301020
let (substs, assoc_bindings, arg_count_correct) = self.create_substs_for_ast_trait_ref(
1031-
path_span,
1021+
trait_ref.path.span,
10321022
trait_def_id,
10331023
self_ty,
10341024
trait_ref.path.segments.last().unwrap(),
@@ -1729,6 +1719,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
17291719
self.ast_region_to_region(lifetime, None)
17301720
} else {
17311721
self.re_infer(None, span).unwrap_or_else(|| {
1722+
// FIXME: these can be redundant with E0106, but not always.
17321723
struct_span_err!(
17331724
tcx.sess,
17341725
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 `'static` 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 `'static` lifetime
9+
|
10+
LL | fn elision(_: fn() -> &'static i32) {
11+
| ^^^^^^^^
812

913
error: aborting due to previous error
1014

0 commit comments

Comments
 (0)