Skip to content

Commit 1063c0d

Browse files
authored
Rollup merge of #129207 - GrigorenkoPV:elided-is-named, r=cjgillot
Lint that warns when an elided lifetime ends up being a named lifetime As suggested in #48686 (comment) Fixes #48686
2 parents da65b90 + a9b959a commit 1063c0d

File tree

59 files changed

+628
-61
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+628
-61
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
837837

838838
(hir::ParamName::Fresh, hir::LifetimeParamKind::Elided(kind))
839839
}
840-
LifetimeRes::Static | LifetimeRes::Error => return None,
840+
LifetimeRes::Static { .. } | LifetimeRes::Error => return None,
841841
res => panic!(
842842
"Unexpected lifetime resolution {:?} for {:?} at {:?}",
843843
res, ident, ident.span
@@ -1656,7 +1656,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
16561656
}
16571657

16581658
// Opaques do not capture `'static`
1659-
LifetimeRes::Static | LifetimeRes::Error => {
1659+
LifetimeRes::Static { .. } | LifetimeRes::Error => {
16601660
continue;
16611661
}
16621662

@@ -2069,7 +2069,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
20692069
hir::LifetimeName::Param(param)
20702070
}
20712071
LifetimeRes::Infer => hir::LifetimeName::Infer,
2072-
LifetimeRes::Static => hir::LifetimeName::Static,
2072+
LifetimeRes::Static { .. } => hir::LifetimeName::Static,
20732073
LifetimeRes::Error => hir::LifetimeName::Error,
20742074
res => panic!(
20752075
"Unexpected lifetime resolution {:?} for {:?} at {:?}",

compiler/rustc_ast_lowering/src/lifetime_collector.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl<'ast> LifetimeCollectVisitor<'ast> {
2727
self.collected_lifetimes.insert(lifetime);
2828
}
2929
}
30-
LifetimeRes::Static | LifetimeRes::Error => {
30+
LifetimeRes::Static { .. } | LifetimeRes::Error => {
3131
self.collected_lifetimes.insert(lifetime);
3232
}
3333
LifetimeRes::Infer => {}

compiler/rustc_hir/src/def.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -863,8 +863,13 @@ pub enum LifetimeRes {
863863
/// This variant is used for anonymous lifetimes that we did not resolve during
864864
/// late resolution. Those lifetimes will be inferred by typechecking.
865865
Infer,
866-
/// Explicit `'static` lifetime.
867-
Static,
866+
/// `'static` lifetime.
867+
Static {
868+
/// We do not want to emit `elided_named_lifetimes`
869+
/// when we are inside of a const item or a static,
870+
/// because it would get too annoying.
871+
suppress_elision_warning: bool,
872+
},
868873
/// Resolution failure.
869874
Error,
870875
/// HACK: This is used to recover the NodeId of an elided lifetime.

compiler/rustc_lint/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ lint_duplicate_macro_attribute =
252252
253253
lint_duplicate_matcher_binding = duplicate matcher binding
254254
255+
lint_elided_named_lifetime = elided lifetime has a name
256+
.label_elided = this elided lifetime gets resolved as `{$name}`
257+
.label_named = lifetime `{$name}` declared here
258+
255259
lint_enum_intrinsics_mem_discriminant =
256260
the return value of `mem::discriminant` is unspecified when called with a non-enum type
257261
.note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum

compiler/rustc_lint/src/context/diagnostics.rs

+13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_errors::{
1010
use rustc_middle::middle::stability;
1111
use rustc_session::lint::BuiltinLintDiag;
1212
use rustc_session::Session;
13+
use rustc_span::symbol::kw;
1314
use rustc_span::BytePos;
1415
use tracing::debug;
1516

@@ -441,5 +442,17 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
441442
BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => {
442443
lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag)
443444
}
445+
BuiltinLintDiag::ElidedIsStatic { elided } => {
446+
lints::ElidedNamedLifetime { elided, name: kw::StaticLifetime, named_declaration: None }
447+
.decorate_lint(diag)
448+
}
449+
BuiltinLintDiag::ElidedIsParam { elided, param: (param_name, param_span) } => {
450+
lints::ElidedNamedLifetime {
451+
elided,
452+
name: param_name,
453+
named_declaration: Some(param_span),
454+
}
455+
.decorate_lint(diag)
456+
}
444457
}
445458
}

compiler/rustc_lint/src/lints.rs

+10
Original file line numberDiff line numberDiff line change
@@ -2611,6 +2611,16 @@ pub(crate) struct ElidedLifetimesInPaths {
26112611
pub subdiag: ElidedLifetimeInPathSubdiag,
26122612
}
26132613

2614+
#[derive(LintDiagnostic)]
2615+
#[diag(lint_elided_named_lifetime)]
2616+
pub(crate) struct ElidedNamedLifetime {
2617+
#[label(lint_label_elided)]
2618+
pub elided: Span,
2619+
pub name: Symbol,
2620+
#[label(lint_label_named)]
2621+
pub named_declaration: Option<Span>,
2622+
}
2623+
26142624
#[derive(LintDiagnostic)]
26152625
#[diag(lint_invalid_crate_type_value)]
26162626
pub(crate) struct UnknownCrateTypes {

compiler/rustc_lint_defs/src/builtin.rs

+33
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ declare_lint_pass! {
4242
DUPLICATE_MACRO_ATTRIBUTES,
4343
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
4444
ELIDED_LIFETIMES_IN_PATHS,
45+
ELIDED_NAMED_LIFETIMES,
4546
EXPLICIT_BUILTIN_CFGS_IN_FLAGS,
4647
EXPORTED_PRIVATE_DEPENDENCIES,
4748
FFI_UNWIND_CALLS,
@@ -1862,6 +1863,38 @@ declare_lint! {
18621863
"hidden lifetime parameters in types are deprecated"
18631864
}
18641865

1866+
declare_lint! {
1867+
/// The `elided_named_lifetimes` lint detects when an elided
1868+
/// lifetime ends up being a named lifetime, such as `'static`
1869+
/// or some lifetime parameter `'a`.
1870+
///
1871+
/// ### Example
1872+
///
1873+
/// ```rust,compile_fail
1874+
/// #![deny(elided_named_lifetimes)]
1875+
/// struct Foo;
1876+
/// impl Foo {
1877+
/// pub fn get_mut(&'static self, x: &mut u8) -> &mut u8 {
1878+
/// unsafe { &mut *(x as *mut _) }
1879+
/// }
1880+
/// }
1881+
/// ```
1882+
///
1883+
/// {{produces}}
1884+
///
1885+
/// ### Explanation
1886+
///
1887+
/// Lifetime elision is quite useful, because it frees you from having
1888+
/// to give each lifetime its own name, but sometimes it can produce
1889+
/// somewhat surprising resolutions. In safe code, it is mostly okay,
1890+
/// because the borrow checker prevents any unsoundness, so the worst
1891+
/// case scenario is you get a confusing error message in some other place.
1892+
/// But with `unsafe` code, such unexpected resolutions may lead to unsound code.
1893+
pub ELIDED_NAMED_LIFETIMES,
1894+
Warn,
1895+
"detects when an elided lifetime gets resolved to be `'static` or some named parameter"
1896+
}
1897+
18651898
declare_lint! {
18661899
/// The `bare_trait_objects` lint suggests using `dyn Trait` for trait
18671900
/// objects.

compiler/rustc_lint_defs/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,13 @@ pub enum BuiltinLintDiag {
589589
},
590590
MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
591591
ElidedLifetimesInPaths(usize, Span, bool, Span),
592+
ElidedIsStatic {
593+
elided: Span,
594+
},
595+
ElidedIsParam {
596+
elided: Span,
597+
param: (Symbol, Span),
598+
},
592599
UnknownCrateTypes {
593600
span: Span,
594601
candidate: Option<Symbol>,

compiler/rustc_resolve/src/late.rs

+67-12
Original file line numberDiff line numberDiff line change
@@ -1557,14 +1557,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
15571557
if ident.name == kw::StaticLifetime {
15581558
self.record_lifetime_res(
15591559
lifetime.id,
1560-
LifetimeRes::Static,
1560+
LifetimeRes::Static { suppress_elision_warning: false },
15611561
LifetimeElisionCandidate::Named,
15621562
);
15631563
return;
15641564
}
15651565

15661566
if ident.name == kw::UnderscoreLifetime {
1567-
return self.resolve_anonymous_lifetime(lifetime, false);
1567+
return self.resolve_anonymous_lifetime(lifetime, lifetime.id, false);
15681568
}
15691569

15701570
let mut lifetime_rib_iter = self.lifetime_ribs.iter().rev();
@@ -1667,13 +1667,23 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
16671667
}
16681668

16691669
#[instrument(level = "debug", skip(self))]
1670-
fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) {
1670+
fn resolve_anonymous_lifetime(
1671+
&mut self,
1672+
lifetime: &Lifetime,
1673+
id_for_lint: NodeId,
1674+
elided: bool,
1675+
) {
16711676
debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
16721677

16731678
let kind =
16741679
if elided { MissingLifetimeKind::Ampersand } else { MissingLifetimeKind::Underscore };
1675-
let missing_lifetime =
1676-
MissingLifetime { id: lifetime.id, span: lifetime.ident.span, kind, count: 1 };
1680+
let missing_lifetime = MissingLifetime {
1681+
id: lifetime.id,
1682+
span: lifetime.ident.span,
1683+
kind,
1684+
count: 1,
1685+
id_for_lint,
1686+
};
16771687
let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
16781688
for (i, rib) in self.lifetime_ribs.iter().enumerate().rev() {
16791689
debug!(?rib.kind);
@@ -1697,7 +1707,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
16971707
if lifetimes_in_scope.is_empty() {
16981708
self.record_lifetime_res(
16991709
lifetime.id,
1700-
LifetimeRes::Static,
1710+
// We are inside a const item, so do not warn.
1711+
LifetimeRes::Static { suppress_elision_warning: true },
17011712
elision_candidate,
17021713
);
17031714
return;
@@ -1800,7 +1811,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
18001811
LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) },
18011812
LifetimeElisionCandidate::Ignore,
18021813
);
1803-
self.resolve_anonymous_lifetime(&lt, true);
1814+
self.resolve_anonymous_lifetime(&lt, anchor_id, true);
18041815
}
18051816

18061817
#[instrument(level = "debug", skip(self))]
@@ -1916,6 +1927,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
19161927
};
19171928
let missing_lifetime = MissingLifetime {
19181929
id: node_ids.start,
1930+
id_for_lint: segment_id,
19191931
span: elided_lifetime_span,
19201932
kind,
19211933
count: expected_lifetimes,
@@ -2039,8 +2051,44 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
20392051
if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
20402052
panic!("lifetime {id:?} resolved multiple times ({prev_res:?} before, {res:?} now)")
20412053
}
2054+
2055+
match candidate {
2056+
LifetimeElisionCandidate::Missing(missing @ MissingLifetime { .. }) => {
2057+
debug_assert_eq!(id, missing.id);
2058+
match res {
2059+
LifetimeRes::Static { suppress_elision_warning } => {
2060+
if !suppress_elision_warning {
2061+
self.r.lint_buffer.buffer_lint(
2062+
lint::builtin::ELIDED_NAMED_LIFETIMES,
2063+
missing.id_for_lint,
2064+
missing.span,
2065+
BuiltinLintDiag::ElidedIsStatic { elided: missing.span },
2066+
);
2067+
}
2068+
}
2069+
LifetimeRes::Param { param, binder: _ } => {
2070+
let tcx = self.r.tcx();
2071+
self.r.lint_buffer.buffer_lint(
2072+
lint::builtin::ELIDED_NAMED_LIFETIMES,
2073+
missing.id_for_lint,
2074+
missing.span,
2075+
BuiltinLintDiag::ElidedIsParam {
2076+
elided: missing.span,
2077+
param: (tcx.item_name(param.into()), tcx.source_span(param)),
2078+
},
2079+
);
2080+
}
2081+
LifetimeRes::Fresh { .. }
2082+
| LifetimeRes::Infer
2083+
| LifetimeRes::Error
2084+
| LifetimeRes::ElidedAnchor { .. } => {}
2085+
}
2086+
}
2087+
LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => {}
2088+
}
2089+
20422090
match res {
2043-
LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => {
2091+
LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static { .. } => {
20442092
if let Some(ref mut candidates) = self.lifetime_elision_candidates {
20452093
candidates.push((res, candidate));
20462094
}
@@ -2558,9 +2606,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
25582606

25592607
ItemKind::Static(box ast::StaticItem { ref ty, ref expr, .. }) => {
25602608
self.with_static_rib(def_kind, |this| {
2561-
this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
2562-
this.visit_ty(ty);
2563-
});
2609+
this.with_lifetime_rib(
2610+
LifetimeRibKind::Elided(LifetimeRes::Static {
2611+
suppress_elision_warning: true,
2612+
}),
2613+
|this| {
2614+
this.visit_ty(ty);
2615+
},
2616+
);
25642617
if let Some(expr) = expr {
25652618
// We already forbid generic params because of the above item rib,
25662619
// so it doesn't matter whether this is a trivial constant.
@@ -2589,7 +2642,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
25892642
this.visit_generics(generics);
25902643

25912644
this.with_lifetime_rib(
2592-
LifetimeRibKind::Elided(LifetimeRes::Static),
2645+
LifetimeRibKind::Elided(LifetimeRes::Static {
2646+
suppress_elision_warning: true,
2647+
}),
25932648
|this| this.visit_ty(ty),
25942649
);
25952650

compiler/rustc_resolve/src/late/diagnostics.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
102102
pub(super) struct MissingLifetime {
103103
/// Used to overwrite the resolution with the suggestion, to avoid cascading errors.
104104
pub id: NodeId,
105+
/// As we cannot yet emit lints in this crate and have to buffer them instead,
106+
/// we need to associate each lint with some `NodeId`,
107+
/// however for some `MissingLifetime`s their `NodeId`s are "fake",
108+
/// in a sense that they are temporary and not get preserved down the line,
109+
/// which means that the lints for those nodes will not get emitted.
110+
/// To combat this, we can try to use some other `NodeId`s as a fallback option.
111+
pub id_for_lint: NodeId,
105112
/// Where to suggest adding the lifetime.
106113
pub span: Span,
107114
/// How the lifetime was introduced, to have the correct space and comma.
@@ -3028,7 +3035,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
30283035
maybe_static = true;
30293036
in_scope_lifetimes = vec![(
30303037
Ident::with_dummy_span(kw::StaticLifetime),
3031-
(DUMMY_NODE_ID, LifetimeRes::Static),
3038+
(DUMMY_NODE_ID, LifetimeRes::Static { suppress_elision_warning: false }),
30323039
)];
30333040
}
30343041
} else if elided_len == 0 {
@@ -3040,7 +3047,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
30403047
maybe_static = true;
30413048
in_scope_lifetimes = vec![(
30423049
Ident::with_dummy_span(kw::StaticLifetime),
3043-
(DUMMY_NODE_ID, LifetimeRes::Static),
3050+
(DUMMY_NODE_ID, LifetimeRes::Static { suppress_elision_warning: false }),
30443051
)];
30453052
}
30463053
} else if num_params == 1 {

library/alloc/src/string.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2313,7 +2313,7 @@ impl<'b> Pattern for &'b String {
23132313
}
23142314

23152315
#[inline]
2316-
fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&str>
2316+
fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str>
23172317
where
23182318
Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>,
23192319
{

0 commit comments

Comments
 (0)