Skip to content

Commit 5ea7ea8

Browse files
committed
Lint elided lifetimes in path during lifetime resolution.
1 parent ac03470 commit 5ea7ea8

File tree

13 files changed

+181
-78
lines changed

13 files changed

+181
-78
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
19281928
let param_name = match lt.name {
19291929
hir::LifetimeName::Param(param_name) => param_name,
19301930
hir::LifetimeName::Implicit
1931+
| hir::LifetimeName::ImplicitMissing
19311932
| hir::LifetimeName::Underscore
19321933
| hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()),
19331934
hir::LifetimeName::ImplicitObjectLifetimeDefault => {
@@ -2322,11 +2323,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
23222323
&'s mut self,
23232324
span: Span,
23242325
count: usize,
2326+
param_mode: ParamMode,
23252327
) -> impl Iterator<Item = hir::Lifetime> + Captures<'a> + Captures<'s> + Captures<'hir> {
2326-
(0..count).map(move |_| self.elided_path_lifetime(span))
2328+
(0..count).map(move |_| self.elided_path_lifetime(span, param_mode))
23272329
}
23282330

2329-
fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime {
2331+
fn elided_path_lifetime(&mut self, span: Span, param_mode: ParamMode) -> hir::Lifetime {
23302332
match self.anonymous_lifetime_mode {
23312333
AnonymousLifetimeMode::CreateParameter => {
23322334
// We should have emitted E0726 when processing this path above
@@ -2342,7 +2344,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
23422344
// lifetime. Instead, we simply create an implicit lifetime, which will be checked
23432345
// later, at which point a suitable error will be emitted.
23442346
AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
2345-
self.new_implicit_lifetime(span)
2347+
if param_mode == ParamMode::Explicit {
2348+
let id = self.resolver.next_node_id();
2349+
self.new_named_lifetime(id, span, hir::LifetimeName::ImplicitMissing)
2350+
} else {
2351+
self.new_implicit_lifetime(span)
2352+
}
23462353
}
23472354
}
23482355
}
@@ -2536,7 +2543,9 @@ fn lifetimes_from_impl_trait_bounds(
25362543

25372544
fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
25382545
let name = match lifetime.name {
2539-
hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
2546+
hir::LifetimeName::Implicit
2547+
| hir::LifetimeName::ImplicitMissing
2548+
| hir::LifetimeName::Underscore => {
25402549
if self.collect_elided_lifetimes {
25412550
// Use `'_` for both implicit and underscore lifetimes in
25422551
// `type Foo<'_> = impl SomeTrait<'_>;`.

compiler/rustc_ast_lowering/src/path.rs

+3-19
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ use rustc_hir as hir;
77
use rustc_hir::def::{DefKind, PartialRes, Res};
88
use rustc_hir::def_id::DefId;
99
use rustc_hir::GenericArg;
10-
use rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS;
11-
use rustc_session::lint::BuiltinLintDiagnostics;
1210
use rustc_span::symbol::Ident;
1311
use rustc_span::{BytePos, Span, DUMMY_SP};
1412

@@ -275,7 +273,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
275273
// See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
276274
let elided_lifetime_span = if generic_args.span.is_empty() {
277275
// If there are no brackets, use the identifier span.
278-
segment.ident.span
276+
path_span
279277
} else if generic_args.is_empty() {
280278
// If there are brackets, but not generic arguments, then use the opening bracket
281279
generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
@@ -284,7 +282,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
284282
generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
285283
};
286284
generic_args.args = self
287-
.elided_path_lifetimes(elided_lifetime_span, expected_lifetimes)
285+
.elided_path_lifetimes(elided_lifetime_span, expected_lifetimes, param_mode)
288286
.map(GenericArg::Lifetime)
289287
.chain(generic_args.args.into_iter())
290288
.collect();
@@ -329,21 +327,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
329327
err.note("assuming a `'static` lifetime...");
330328
err.emit();
331329
}
332-
AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
333-
self.resolver.lint_buffer().buffer_lint_with_diagnostic(
334-
ELIDED_LIFETIMES_IN_PATHS,
335-
CRATE_NODE_ID,
336-
path_span,
337-
"hidden lifetime parameters in types are deprecated",
338-
BuiltinLintDiagnostics::ElidedLifetimesInPaths(
339-
expected_lifetimes,
340-
path_span,
341-
incl_angl_brckt,
342-
insertion_sp,
343-
suggestion,
344-
),
345-
);
346-
}
330+
AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {}
347331
}
348332
}
349333
}

compiler/rustc_borrowck/src/diagnostics/region_name.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
584584
Some(RegionNameHighlight::MatchedAdtAndSegment(lifetime_span))
585585
}
586586

587-
hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Implicit => {
587+
hir::LifetimeName::ImplicitObjectLifetimeDefault
588+
| hir::LifetimeName::Implicit
589+
| hir::LifetimeName::ImplicitMissing => {
588590
// In this case, the user left off the lifetime; so
589591
// they wrote something like:
590592
//

compiler/rustc_hir/src/hir.rs

+5
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ pub enum LifetimeName {
9494
/// User wrote nothing (e.g., the lifetime in `&u32`).
9595
Implicit,
9696

97+
/// User wrote nothing, but should have provided something.
98+
ImplicitMissing,
99+
97100
/// Implicit lifetime in a context like `dyn Foo`. This is
98101
/// distinguished from implicit lifetimes elsewhere because the
99102
/// lifetime that they default to must appear elsewhere within the
@@ -123,6 +126,7 @@ impl LifetimeName {
123126
match *self {
124127
LifetimeName::ImplicitObjectLifetimeDefault
125128
| LifetimeName::Implicit
129+
| LifetimeName::ImplicitMissing
126130
| LifetimeName::Error => Ident::empty(),
127131
LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime),
128132
LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime),
@@ -134,6 +138,7 @@ impl LifetimeName {
134138
match self {
135139
LifetimeName::ImplicitObjectLifetimeDefault
136140
| LifetimeName::Implicit
141+
| LifetimeName::ImplicitMissing
137142
| LifetimeName::Underscore => true,
138143

139144
// It might seem surprising that `Fresh(_)` counts as

compiler/rustc_hir/src/intravisit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
546546
| LifetimeName::Static
547547
| LifetimeName::Error
548548
| LifetimeName::Implicit
549+
| LifetimeName::ImplicitMissing
549550
| LifetimeName::ImplicitObjectLifetimeDefault
550551
| LifetimeName::Underscore => {}
551552
}

compiler/rustc_resolve/src/late/diagnostics.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -1950,6 +1950,41 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
19501950
}
19511951
}
19521952

1953+
crate fn report_elided_lifetime_in_ty(&self, lifetime_refs: &[&hir::Lifetime]) {
1954+
let missing_lifetimes = lifetime_refs
1955+
.iter()
1956+
.filter(|a| matches!(a, hir::Lifetime { name: hir::LifetimeName::ImplicitMissing, .. }))
1957+
.count();
1958+
1959+
if missing_lifetimes > 0 {
1960+
let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
1961+
spans.sort();
1962+
let mut spans_dedup = spans.clone();
1963+
spans_dedup.dedup();
1964+
let spans_with_counts: Vec<_> = spans_dedup
1965+
.into_iter()
1966+
.map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
1967+
.collect();
1968+
1969+
self.tcx.struct_span_lint_hir(
1970+
rustc_session::lint::builtin::ELIDED_LIFETIMES_IN_PATHS,
1971+
hir::CRATE_HIR_ID,
1972+
spans,
1973+
|lint| {
1974+
let mut db = lint.build("hidden lifetime parameters in types are deprecated");
1975+
self.add_missing_lifetime_specifiers_label(
1976+
&mut db,
1977+
spans_with_counts,
1978+
&FxHashSet::from_iter([kw::UnderscoreLifetime]),
1979+
Vec::new(),
1980+
&[],
1981+
);
1982+
db.emit()
1983+
},
1984+
);
1985+
}
1986+
}
1987+
19531988
// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const
19541989
// generics. We are disallowing this until we can decide on how we want to handle non-'static
19551990
// lifetimes in const generics. See issue #74052 for discussion.
@@ -2376,7 +2411,10 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
23762411
);
23772412
let is_allowed_lifetime = matches!(
23782413
lifetime_ref.name,
2379-
hir::LifetimeName::Implicit | hir::LifetimeName::Static | hir::LifetimeName::Underscore
2414+
hir::LifetimeName::Implicit
2415+
| hir::LifetimeName::ImplicitMissing
2416+
| hir::LifetimeName::Static
2417+
| hir::LifetimeName::Underscore
23802418
);
23812419

23822420
if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime {

compiler/rustc_resolve/src/late/lifetimes.rs

+18-8
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
923923
}
924924
});
925925
match lifetime.name {
926-
LifetimeName::Implicit => {
926+
LifetimeName::Implicit | hir::LifetimeName::ImplicitMissing => {
927927
// For types like `dyn Foo`, we should
928928
// generate a special form of elided.
929929
span_bug!(ty.span, "object-lifetime-default expected, not implicit",);
@@ -3057,9 +3057,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
30573057
let error = loop {
30583058
match *scope {
30593059
// Do not assign any resolution, it will be inferred.
3060-
Scope::Body { .. } => return,
3060+
Scope::Body { .. } => break Ok(()),
30613061

3062-
Scope::Root => break None,
3062+
Scope::Root => break Err(None),
30633063

30643064
Scope::Binder { s, ref lifetimes, scope_type, .. } => {
30653065
// collect named lifetimes for suggestions
@@ -3086,15 +3086,15 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
30863086

30873087
self.insert_lifetime(lifetime_ref, lifetime);
30883088
}
3089-
return;
3089+
break Ok(());
30903090
}
30913091

30923092
Scope::Elision { elide: Elide::Exact(l), .. } => {
30933093
let lifetime = l.shifted(late_depth);
30943094
for lifetime_ref in lifetime_refs {
30953095
self.insert_lifetime(lifetime_ref, lifetime);
30963096
}
3097-
return;
3097+
break Ok(());
30983098
}
30993099

31003100
Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => {
@@ -3119,10 +3119,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
31193119
_ => break,
31203120
}
31213121
}
3122-
break Some(&e[..]);
3122+
break Err(Some(&e[..]));
31233123
}
31243124

3125-
Scope::Elision { elide: Elide::Forbid, .. } => break None,
3125+
Scope::Elision { elide: Elide::Forbid, .. } => break Err(None),
31263126

31273127
Scope::ObjectLifetimeDefault { s, .. }
31283128
| Scope::Supertrait { s, .. }
@@ -3132,6 +3132,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
31323132
}
31333133
};
31343134

3135+
let error = match error {
3136+
Ok(()) => {
3137+
self.report_elided_lifetime_in_ty(lifetime_refs);
3138+
return;
3139+
}
3140+
Err(error) => error,
3141+
};
3142+
31353143
// If we specifically need the `scope_for_path` map, then we're in the
31363144
// diagnostic pass and we don't want to emit more errors.
31373145
if self.map.scope_for_path.is_some() {
@@ -3274,7 +3282,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
32743282
))
32753283
.emit();
32763284
}
3277-
hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
3285+
hir::LifetimeName::Param(_)
3286+
| hir::LifetimeName::Implicit
3287+
| hir::LifetimeName::ImplicitMissing => {
32783288
self.resolve_lifetime_ref(lt);
32793289
}
32803290
hir::LifetimeName::ImplicitObjectLifetimeDefault => {

src/test/ui/in-band-lifetimes/elided-lifetimes.fixed

+19-12
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,24 @@
55
#![deny(elided_lifetimes_in_paths)]
66
//~^ NOTE the lint level is defined here
77

8-
use std::cell::{RefCell, Ref};
8+
use std::cell::{Ref, RefCell};
99

10-
11-
struct Foo<'a> { x: &'a u32 }
10+
struct Foo<'a> {
11+
x: &'a u32,
12+
}
1213

1314
fn foo(x: &Foo<'_>) {
1415
//~^ ERROR hidden lifetime parameters in types are deprecated
15-
//~| HELP indicate the anonymous lifetime
16+
//~| NOTE expected named lifetime parameter
17+
//~| HELP consider using the `'_` lifetime
1618
}
1719

1820
fn bar(x: &Foo<'_>) {}
1921

20-
2122
struct Wrapped<'a>(&'a str);
2223

2324
struct WrappedWithBow<'a> {
24-
gift: &'a str
25+
gift: &'a str,
2526
}
2627

2728
struct MatchedSet<'a, 'b> {
@@ -31,19 +32,22 @@ struct MatchedSet<'a, 'b> {
3132

3233
fn wrap_gift(gift: &str) -> Wrapped<'_> {
3334
//~^ ERROR hidden lifetime parameters in types are deprecated
34-
//~| HELP indicate the anonymous lifetime
35+
//~| NOTE expected named lifetime parameter
36+
//~| HELP consider using the `'_` lifetime
3537
Wrapped(gift)
3638
}
3739

3840
fn wrap_gift_with_bow(gift: &str) -> WrappedWithBow<'_> {
3941
//~^ ERROR hidden lifetime parameters in types are deprecated
40-
//~| HELP indicate the anonymous lifetime
42+
//~| NOTE expected named lifetime parameter
43+
//~| HELP consider using the `'_` lifetime
4144
WrappedWithBow { gift }
4245
}
4346

4447
fn inspect_matched_set(set: MatchedSet<'_, '_>) {
4548
//~^ ERROR hidden lifetime parameters in types are deprecated
46-
//~| HELP indicate the anonymous lifetime
49+
//~| NOTE expected 2 lifetime parameters
50+
//~| HELP consider using the `'_` lifetime
4751
println!("{} {}", set.one, set.another);
4852
}
4953

@@ -55,7 +59,8 @@ macro_rules! autowrapper {
5559

5660
fn $fn_name(gift: &str) -> $type_name<'_> {
5761
//~^ ERROR hidden lifetime parameters in types are deprecated
58-
//~| HELP indicate the anonymous lifetime
62+
//~| NOTE expected named lifetime parameter
63+
//~| HELP consider using the `'_` lifetime
5964
$type_name { gift }
6065
}
6166
}
@@ -69,15 +74,17 @@ macro_rules! anytuple_ref_ty {
6974
($($types:ty),*) => {
7075
Ref<'_, ($($types),*)>
7176
//~^ ERROR hidden lifetime parameters in types are deprecated
72-
//~| HELP indicate the anonymous lifetime
77+
//~| NOTE expected named lifetime parameter
78+
//~| HELP consider using the `'_` lifetime
7379
}
7480
}
7581

7682
fn main() {
7783
let honesty = RefCell::new((4, 'e'));
7884
let loyalty: Ref<'_, (u32, char)> = honesty.borrow();
7985
//~^ ERROR hidden lifetime parameters in types are deprecated
80-
//~| HELP indicate the anonymous lifetime
86+
//~| NOTE expected named lifetime parameter
87+
//~| HELP consider using the `'_` lifetime
8188
let generosity = Ref::map(loyalty, |t| &t.0);
8289

8390
let laughter = RefCell::new((true, "magic"));

0 commit comments

Comments
 (0)