Skip to content

Commit 933349e

Browse files
committed
Account for HKTB when suggesting introduction of named lifetime
1 parent 9ed29b6 commit 933349e

File tree

7 files changed

+153
-38
lines changed

7 files changed

+153
-38
lines changed

src/librustc_hir/hir.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2259,10 +2259,10 @@ impl TraitRef<'_> {
22592259

22602260
#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
22612261
pub struct PolyTraitRef<'hir> {
2262-
/// The `'a` in `<'a> Foo<&'a T>`.
2262+
/// The `'a` in `for<'a> Foo<&'a T>`.
22632263
pub bound_generic_params: &'hir [GenericParam<'hir>],
22642264

2265-
/// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.
2265+
/// The `Foo<&'a T>` in `for <'a> Foo<&'a T>`.
22662266
pub trait_ref: TraitRef<'hir>,
22672267

22682268
pub span: Span,

src/librustc_resolve/diagnostics.rs

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use rustc::ty::{self, DefIdTree};
77
use rustc_data_structures::fx::FxHashSet;
88
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
99
use rustc_feature::BUILTIN_ATTRIBUTES;
10-
use rustc_hir as hir;
1110
use rustc_hir::def::Namespace::{self, *};
1211
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind};
1312
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
@@ -20,6 +19,7 @@ use syntax::print::pprust;
2019
use syntax::util::lev_distance::find_best_match_for_name;
2120

2221
use crate::imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
22+
use crate::lifetimes::{HRLTSpanType, MissingLifetimeSpot};
2323
use crate::path_names_to_string;
2424
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind};
2525
use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot};
@@ -1466,7 +1466,7 @@ crate fn add_missing_lifetime_specifiers_label(
14661466
count: usize,
14671467
lifetime_names: &FxHashSet<ast::Ident>,
14681468
snippet: Option<&str>,
1469-
missing_named_lifetime_spots: &[&hir::Generics<'_>],
1469+
missing_named_lifetime_spots: &[MissingLifetimeSpot<'_>],
14701470
) {
14711471
if count > 1 {
14721472
err.span_label(span, format!("expected {} lifetime parameters", count));
@@ -1479,21 +1479,41 @@ crate fn add_missing_lifetime_specifiers_label(
14791479
Applicability::MaybeIncorrect,
14801480
);
14811481
};
1482-
let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg| {
1482+
let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| {
14831483
err.span_label(span, "expected named lifetime parameter");
14841484

1485-
if let Some(generics) = missing_named_lifetime_spots.iter().last() {
1485+
for missing in missing_named_lifetime_spots.iter().rev() {
14861486
let mut introduce_suggestion = vec![];
1487-
introduce_suggestion.push(match &generics.params {
1488-
[] => (generics.span, "<'lifetime>".to_string()),
1489-
[param, ..] => (param.span.shrink_to_lo(), "'lifetime, ".to_string()),
1487+
let msg;
1488+
let should_break;
1489+
introduce_suggestion.push(match missing {
1490+
MissingLifetimeSpot::Generics(generics) => {
1491+
msg = "consider introducing a named lifetime parameter";
1492+
should_break = true;
1493+
match &generics.params {
1494+
[] => (generics.span, "<'lifetime>".to_string()),
1495+
[param, ..] => (param.span.shrink_to_lo(), "'lifetime, ".to_string()),
1496+
}
1497+
}
1498+
MissingLifetimeSpot::HRLT { span, span_type } => {
1499+
msg = "consider introducing a Higher-Ranked lifetime";
1500+
should_break = false;
1501+
err.note(
1502+
"for more information on Higher-Ranked lifetimes, visit \
1503+
https://doc.rust-lang.org/nomicon/hrtb.html",
1504+
);
1505+
let suggestion = match span_type {
1506+
HRLTSpanType::Empty => "for<'lifetime> ",
1507+
HRLTSpanType::Tail => ", 'lifetime",
1508+
};
1509+
(*span, suggestion.to_string())
1510+
}
14901511
});
1491-
introduce_suggestion.push((span, sugg));
1492-
err.multipart_suggestion(
1493-
"consider introducing a named lifetime parameter",
1494-
introduce_suggestion,
1495-
Applicability::MaybeIncorrect,
1496-
);
1512+
introduce_suggestion.push((span, sugg.to_string()));
1513+
err.multipart_suggestion(msg, introduce_suggestion, Applicability::MaybeIncorrect);
1514+
if should_break {
1515+
break;
1516+
}
14971517
}
14981518
};
14991519

@@ -1508,13 +1528,13 @@ crate fn add_missing_lifetime_specifiers_label(
15081528
suggest_existing(err, format!("{}<{}>", snippet, name));
15091529
}
15101530
(0, _, Some("&")) => {
1511-
suggest_new(err, "&'lifetime ".to_string());
1531+
suggest_new(err, "&'lifetime ");
15121532
}
15131533
(0, _, Some("'_")) => {
1514-
suggest_new(err, "'lifetime".to_string());
1534+
suggest_new(err, "'lifetime");
15151535
}
15161536
(0, _, Some(snippet)) if !snippet.ends_with(">") => {
1517-
suggest_new(err, format!("{}<'lifetime>", snippet));
1537+
suggest_new(err, &format!("{}<'lifetime>", snippet));
15181538
}
15191539
_ => {
15201540
err.span_label(span, "expected lifetime parameter");

src/librustc_resolve/lifetimes.rs

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,22 @@ struct NamedRegionMap {
153153
object_lifetime_defaults: HirIdMap<Vec<ObjectLifetimeDefault>>,
154154
}
155155

156+
crate enum MissingLifetimeSpot<'tcx> {
157+
Generics(&'tcx hir::Generics<'tcx>),
158+
HRLT { span: Span, span_type: HRLTSpanType },
159+
}
160+
161+
crate enum HRLTSpanType {
162+
Empty,
163+
Tail,
164+
}
165+
166+
impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> {
167+
fn into(self) -> MissingLifetimeSpot<'tcx> {
168+
MissingLifetimeSpot::Generics(self)
169+
}
170+
}
171+
156172
struct LifetimeContext<'a, 'tcx> {
157173
tcx: TyCtxt<'tcx>,
158174
map: &'a mut NamedRegionMap,
@@ -186,7 +202,7 @@ struct LifetimeContext<'a, 'tcx> {
186202

187203
/// When encountering an undefined named lifetime, we will suggest introducing it in these
188204
/// places.
189-
missing_named_lifetime_spots: Vec<&'tcx hir::Generics<'tcx>>,
205+
missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>,
190206
}
191207

192208
#[derive(Debug)]
@@ -389,7 +405,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
389405
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
390406
match item.kind {
391407
hir::ItemKind::Fn(ref sig, ref generics, _) => {
392-
self.missing_named_lifetime_spots.push(generics);
408+
self.missing_named_lifetime_spots.push(generics.into());
393409
self.visit_early_late(None, &sig.decl, generics, |this| {
394410
intravisit::walk_item(this, item);
395411
});
@@ -424,7 +440,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
424440
| hir::ItemKind::Trait(_, _, ref generics, ..)
425441
| hir::ItemKind::TraitAlias(ref generics, ..)
426442
| hir::ItemKind::Impl { ref generics, .. } => {
427-
self.missing_named_lifetime_spots.push(generics);
443+
self.missing_named_lifetime_spots.push(generics.into());
428444

429445
// Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name".
430446
// This is not true for other kinds of items.x
@@ -696,7 +712,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
696712

697713
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
698714
use self::hir::TraitItemKind::*;
699-
self.missing_named_lifetime_spots.push(&trait_item.generics);
715+
self.missing_named_lifetime_spots.push((&trait_item.generics).into());
700716
match trait_item.kind {
701717
Method(ref sig, _) => {
702718
let tcx = self.tcx;
@@ -753,7 +769,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
753769

754770
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
755771
use self::hir::ImplItemKind::*;
756-
self.missing_named_lifetime_spots.push(&impl_item.generics);
772+
self.missing_named_lifetime_spots.push((&impl_item.generics).into());
757773
match impl_item.kind {
758774
Method(ref sig, _) => {
759775
let tcx = self.tcx;
@@ -953,6 +969,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
953969
) {
954970
debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);
955971

972+
let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref);
956973
if !self.trait_ref_hack
957974
|| trait_ref.bound_generic_params.iter().any(|param| match param.kind {
958975
GenericParamKind::Lifetime { .. } => true,
@@ -988,10 +1005,13 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
9881005
self.with(scope, |old_scope, this| {
9891006
this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
9901007
walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
991-
this.visit_trait_ref(&trait_ref.trait_ref)
1008+
this.visit_trait_ref(&trait_ref.trait_ref);
9921009
})
9931010
} else {
994-
self.visit_trait_ref(&trait_ref.trait_ref)
1011+
self.visit_trait_ref(&trait_ref.trait_ref);
1012+
}
1013+
if should_pop_missing_lt {
1014+
self.missing_named_lifetime_spots.pop();
9951015
}
9961016
}
9971017
}
@@ -1832,18 +1852,41 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
18321852
lifetime_ref
18331853
);
18341854
err.span_label(lifetime_ref.span, "undeclared lifetime");
1835-
if !self.is_in_fn_syntax {
1836-
for generics in &self.missing_named_lifetime_spots {
1837-
let (span, sugg) = match &generics.params {
1838-
[] => (generics.span, format!("<{}>", lifetime_ref)),
1839-
[param, ..] => (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref)),
1840-
};
1841-
err.span_suggestion(
1842-
span,
1843-
&format!("consider introducing lifetime `{}` here", lifetime_ref),
1844-
sugg,
1845-
Applicability::MaybeIncorrect,
1846-
);
1855+
for missing in &self.missing_named_lifetime_spots {
1856+
match missing {
1857+
MissingLifetimeSpot::Generics(generics) => {
1858+
let (span, sugg) = match &generics.params {
1859+
[] => (generics.span, format!("<{}>", lifetime_ref)),
1860+
[param, ..] => {
1861+
(param.span.shrink_to_lo(), format!("{}, ", lifetime_ref))
1862+
}
1863+
};
1864+
err.span_suggestion(
1865+
span,
1866+
&format!("consider introducing lifetime `{}` here", lifetime_ref),
1867+
sugg,
1868+
Applicability::MaybeIncorrect,
1869+
);
1870+
}
1871+
MissingLifetimeSpot::HRLT { span, span_type } => {
1872+
err.span_suggestion(
1873+
*span,
1874+
&format!(
1875+
"consider introducing a Higher-Ranked lifetime `{}` here",
1876+
lifetime_ref
1877+
),
1878+
match span_type {
1879+
HRLTSpanType::Empty => format!("for<{}> ", lifetime_ref),
1880+
HRLTSpanType::Tail => format!(", {}", lifetime_ref),
1881+
}
1882+
.to_string(),
1883+
Applicability::MaybeIncorrect,
1884+
);
1885+
err.note(
1886+
"for more information on Higher-Ranked lifetimes, visit \
1887+
https://doc.rust-lang.org/nomicon/hrtb.html",
1888+
);
1889+
}
18471890
}
18481891
}
18491892
err.emit();
@@ -2441,6 +2484,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
24412484

24422485
let elided_len = elided_params.len();
24432486

2487+
// FIXME: collect spans of the input params when appropriate to use in the diagnostic.
24442488
for (i, info) in elided_params.into_iter().enumerate() {
24452489
let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions } = info;
24462490

@@ -2747,6 +2791,27 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
27472791
let old_value = self.map.defs.remove(&lifetime_ref.hir_id);
27482792
assert_eq!(old_value, Some(bad_def));
27492793
}
2794+
2795+
fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool {
2796+
if let Res::Def(_, did) = trait_ref.trait_ref.path.res {
2797+
if [
2798+
self.tcx.lang_items().fn_once_trait(),
2799+
self.tcx.lang_items().fn_trait(),
2800+
self.tcx.lang_items().fn_mut_trait(),
2801+
]
2802+
.contains(&Some(did))
2803+
{
2804+
let (span, span_type) = match &trait_ref.bound_generic_params {
2805+
[] => (trait_ref.span.shrink_to_lo(), HRLTSpanType::Empty),
2806+
[.., bound] => (bound.span.shrink_to_hi(), HRLTSpanType::Tail),
2807+
};
2808+
self.missing_named_lifetime_spots
2809+
.push(MissingLifetimeSpot::HRLT { span, span_type });
2810+
return true;
2811+
}
2812+
};
2813+
false
2814+
}
27502815
}
27512816

27522817
/// Detects late-bound lifetimes and inserts them into

src/librustc_typeck/astconv.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1307,12 +1307,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
13071307
);
13081308
}
13091309
};
1310+
// FIXME: point at the type params that don't have appropriate lifetimes:
1311+
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
1312+
// ---- ---- ^^^^^^^
13101313
struct_span_err!(
13111314
tcx.sess,
13121315
binding.span,
13131316
E0582,
13141317
"binding for associated type `{}` references lifetime `{}`, \
1315-
which does not appear in the trait input types",
1318+
which does not appear in the trait input types",
13161319
binding.item_name,
13171320
br_name
13181321
)

src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ LL | let y: &'test u32 = x;
99
error[E0261]: use of undeclared lifetime name `'test`
1010
--> $DIR/no_introducing_in_band_in_locals.rs:10:16
1111
|
12+
LL | fn bar() {
13+
| - help: consider introducing lifetime `'test` here: `<'test>`
1214
LL | let y: fn(&'test u32) = foo2;
1315
| ^^^^^ undeclared lifetime
1416

src/test/ui/issues/issue-19707.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ LL | fn bar<F: Fn(&u8, &u8) -> &u8>(f: &F) {}
1717
| ^ expected named lifetime parameter
1818
|
1919
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2
20+
= note: for more information on Higher-Ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
21+
help: consider introducing a Higher-Ranked lifetime
22+
|
23+
LL | fn bar<F: for<'lifetime> Fn(&u8, &u8) -> &'lifetime u8>(f: &F) {}
24+
| ^^^^^^^^^^^^^^ ^^^^^^^^^^
2025
help: consider introducing a named lifetime parameter
2126
|
2227
LL | fn bar<'lifetime, F: Fn(&u8, &u8) -> &'lifetime u8>(f: &F) {}

src/test/ui/regions/regions-name-undeclared.stderr

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,32 @@ error[E0261]: use of undeclared lifetime name `'b`
8888
|
8989
LL | ... &'b isize,
9090
| ^^ undeclared lifetime
91+
|
92+
= note: for more information on Higher-Ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
93+
help: consider introducing lifetime `'b` here
94+
|
95+
LL | fn fn_types<'b>(a: &'a isize,
96+
| ^^^^
97+
help: consider introducing a Higher-Ranked lifetime `'b` here
98+
|
99+
LL | b: Box<dyn for<'a, 'b> FnOnce(&'a isize,
100+
| ^^^^
91101

92102
error[E0261]: use of undeclared lifetime name `'b`
93103
--> $DIR/regions-name-undeclared.rs:45:36
94104
|
95105
LL | ... &'b isize)>,
96106
| ^^ undeclared lifetime
107+
|
108+
= note: for more information on Higher-Ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
109+
help: consider introducing lifetime `'b` here
110+
|
111+
LL | fn fn_types<'b>(a: &'a isize,
112+
| ^^^^
113+
help: consider introducing a Higher-Ranked lifetime `'b` here
114+
|
115+
LL | b: Box<dyn for<'a, 'b> FnOnce(&'a isize,
116+
| ^^^^
97117

98118
error[E0261]: use of undeclared lifetime name `'a`
99119
--> $DIR/regions-name-undeclared.rs:46:17

0 commit comments

Comments
 (0)