Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fa696a3

Browse files
Rollup merge of #118391 - compiler-errors:lifetimes-eq, r=lcnr
Add `REDUNDANT_LIFETIMES` lint to detect lifetimes which are semantically redundant There already is a `UNUSED_LIFETIMES` lint which is fired when we detect where clause bounds like `where 'a: 'static`, however, it doesn't use the full power of lexical region resolution to detect failures. Right now `UNUSED_LIFETIMES` is an `Allow` lint, though presumably we could bump it to warn? I can (somewhat) easily implement a structured suggestion so this can be rustfix'd automatically, since we can just walk through the HIR body, replacing instances of the redundant lifetime. Fixes #118376 r? lcnr
2 parents b14d8b2 + da2b714 commit fa696a3

17 files changed

+332
-103
lines changed

‎compiler/rustc_errors/src/diagnostic_impls.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T {
4646
}
4747
}
4848

49+
#[macro_export]
4950
macro_rules! into_diag_arg_using_display {
5051
($( $ty:ty ),+ $(,)?) => {
5152
$(

‎compiler/rustc_hir_analysis/messages.ftl‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,9 @@ hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pa
355355
hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
356356
.label = not allowed in type signatures
357357
358+
hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}`
359+
.note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}`
360+
358361
hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}`
359362
360363
hir_analysis_return_type_notation_equality_bound =

‎compiler/rustc_hir_analysis/src/check/wfcheck.rs‎

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ use rustc_ast as ast;
88
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
99
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed};
1010
use rustc_hir as hir;
11+
use rustc_hir::def::DefKind;
1112
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
1213
use rustc_hir::lang_items::LangItem;
1314
use rustc_hir::ItemKind;
1415
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1516
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
17+
use rustc_macros::LintDiagnostic;
1618
use rustc_middle::query::Providers;
1719
use rustc_middle::ty::print::with_no_trimmed_paths;
1820
use rustc_middle::ty::trait_def::TraitSpecializationKind;
@@ -136,6 +138,8 @@ where
136138
infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false);
137139
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
138140

141+
lint_redundant_lifetimes(tcx, body_def_id, &outlives_env);
142+
139143
let errors = infcx.resolve_regions(&outlives_env);
140144
if errors.is_empty() {
141145
return Ok(());
@@ -2010,6 +2014,137 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error
20102014
res
20112015
}
20122016

2017+
fn lint_redundant_lifetimes<'tcx>(
2018+
tcx: TyCtxt<'tcx>,
2019+
owner_id: LocalDefId,
2020+
outlives_env: &OutlivesEnvironment<'tcx>,
2021+
) {
2022+
let def_kind = tcx.def_kind(owner_id);
2023+
match def_kind {
2024+
DefKind::Struct
2025+
| DefKind::Union
2026+
| DefKind::Enum
2027+
| DefKind::Trait
2028+
| DefKind::TraitAlias
2029+
| DefKind::Fn
2030+
| DefKind::Const
2031+
| DefKind::Impl { of_trait: _ } => {
2032+
// Proceed
2033+
}
2034+
DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
2035+
let parent_def_id = tcx.local_parent(owner_id);
2036+
if matches!(tcx.def_kind(parent_def_id), DefKind::Impl { of_trait: true }) {
2037+
// Don't check for redundant lifetimes for associated items of trait
2038+
// implementations, since the signature is required to be compatible
2039+
// with the trait, even if the implementation implies some lifetimes
2040+
// are redundant.
2041+
return;
2042+
}
2043+
}
2044+
DefKind::Mod
2045+
| DefKind::Variant
2046+
| DefKind::TyAlias
2047+
| DefKind::ForeignTy
2048+
| DefKind::TyParam
2049+
| DefKind::ConstParam
2050+
| DefKind::Static { .. }
2051+
| DefKind::Ctor(_, _)
2052+
| DefKind::Macro(_)
2053+
| DefKind::ExternCrate
2054+
| DefKind::Use
2055+
| DefKind::ForeignMod
2056+
| DefKind::AnonConst
2057+
| DefKind::InlineConst
2058+
| DefKind::OpaqueTy
2059+
| DefKind::Field
2060+
| DefKind::LifetimeParam
2061+
| DefKind::GlobalAsm
2062+
| DefKind::Closure => return,
2063+
}
2064+
2065+
// The ordering of this lifetime map is a bit subtle.
2066+
//
2067+
// Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime,
2068+
// where we can prove that `'candidate = 'victim`.
2069+
//
2070+
// `'static` must come first in this list because we can never replace `'static` with
2071+
// something else, but if we find some lifetime `'a` where `'a = 'static`, we want to
2072+
// suggest replacing `'a` with `'static`.
2073+
let mut lifetimes = vec![tcx.lifetimes.re_static];
2074+
lifetimes.extend(
2075+
ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()),
2076+
);
2077+
// If we are in a function, add its late-bound lifetimes too.
2078+
if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {
2079+
for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() {
2080+
let ty::BoundVariableKind::Region(kind) = var else { continue };
2081+
lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind));
2082+
}
2083+
}
2084+
lifetimes.retain(|candidate| candidate.has_name());
2085+
2086+
// Keep track of lifetimes which have already been replaced with other lifetimes.
2087+
// This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by
2088+
// both `'a` and `'b`.
2089+
let mut shadowed = FxHashSet::default();
2090+
2091+
for (idx, &candidate) in lifetimes.iter().enumerate() {
2092+
// Don't suggest removing a lifetime twice. We only need to check this
2093+
// here and not up in the `victim` loop because equality is transitive,
2094+
// so if A = C and B = C, then A must = B, so it'll be shadowed too in
2095+
// A's victim loop.
2096+
if shadowed.contains(&candidate) {
2097+
continue;
2098+
}
2099+
2100+
for &victim in &lifetimes[(idx + 1)..] {
2101+
// We should only have late-bound lifetimes of the `BrNamed` variety,
2102+
// since we get these signatures straight from `hir_lowering`. And any
2103+
// other regions (ReError/ReStatic/etc.) shouldn't matter, since we
2104+
// can't really suggest to remove them.
2105+
let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
2106+
| ty::ReLateParam(ty::LateParamRegion {
2107+
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
2108+
..
2109+
})) = victim.kind()
2110+
else {
2111+
continue;
2112+
};
2113+
2114+
// Do not rename lifetimes not local to this item since they'll overlap
2115+
// with the lint running on the parent. We still want to consider parent
2116+
// lifetimes which make child lifetimes redundant, otherwise we would
2117+
// have truncated the `identity_for_item` args above.
2118+
if tcx.parent(def_id) != owner_id.to_def_id() {
2119+
continue;
2120+
}
2121+
2122+
// If `candidate <: victim` and `victim <: candidate`, then they're equal.
2123+
if outlives_env.free_region_map().sub_free_regions(tcx, candidate, victim)
2124+
&& outlives_env.free_region_map().sub_free_regions(tcx, victim, candidate)
2125+
{
2126+
shadowed.insert(victim);
2127+
tcx.emit_node_span_lint(
2128+
rustc_lint_defs::builtin::REDUNDANT_LIFETIMES,
2129+
tcx.local_def_id_to_hir_id(def_id.expect_local()),
2130+
tcx.def_span(def_id),
2131+
RedundantLifetimeArgsLint { candidate, victim },
2132+
);
2133+
}
2134+
}
2135+
}
2136+
}
2137+
2138+
#[derive(LintDiagnostic)]
2139+
#[diag(hir_analysis_redundant_lifetime_args)]
2140+
#[note]
2141+
struct RedundantLifetimeArgsLint<'tcx> {
2142+
/// The lifetime we have found to be redundant.
2143+
victim: ty::Region<'tcx>,
2144+
// The lifetime we can replace the victim with.
2145+
candidate: ty::Region<'tcx>,
2146+
}
2147+
20132148
pub fn provide(providers: &mut Providers) {
20142149
*providers = Providers { check_mod_type_wf, check_well_formed, ..*providers };
20152150
}

‎compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs‎

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ use rustc_middle::hir::nested_filter;
2020
use rustc_middle::middle::resolve_bound_vars::*;
2121
use rustc_middle::query::Providers;
2222
use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor};
23-
use rustc_session::lint;
2423
use rustc_span::def_id::DefId;
2524
use rustc_span::symbol::{sym, Ident};
2625
use rustc_span::Span;
@@ -867,31 +866,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
867866
}) => {
868867
self.visit_lifetime(lifetime);
869868
walk_list!(self, visit_param_bound, bounds);
870-
871-
if lifetime.res != hir::LifetimeName::Static {
872-
for bound in bounds {
873-
let hir::GenericBound::Outlives(lt) = bound else {
874-
continue;
875-
};
876-
if lt.res != hir::LifetimeName::Static {
877-
continue;
878-
}
879-
self.insert_lifetime(lt, ResolvedArg::StaticLifetime);
880-
self.tcx.node_span_lint(
881-
lint::builtin::UNUSED_LIFETIMES,
882-
lifetime.hir_id,
883-
lifetime.ident.span,
884-
format!("unnecessary lifetime parameter `{}`", lifetime.ident),
885-
|lint| {
886-
let help = format!(
887-
"you can use the `'static` lifetime directly, in place of `{}`",
888-
lifetime.ident,
889-
);
890-
lint.help(help);
891-
},
892-
);
893-
}
894-
}
895869
}
896870
&hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => {
897871
self.visit_ty(lhs_ty);

‎compiler/rustc_lint_defs/src/builtin.rs‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ declare_lint_pass! {
7979
PROC_MACRO_BACK_COMPAT,
8080
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
8181
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
82+
REDUNDANT_LIFETIMES,
8283
REFINING_IMPL_TRAIT_INTERNAL,
8384
REFINING_IMPL_TRAIT_REACHABLE,
8485
RENAMED_AND_REMOVED_LINTS,
@@ -1707,6 +1708,33 @@ declare_lint! {
17071708
"detects lifetime parameters that are never used"
17081709
}
17091710

1711+
declare_lint! {
1712+
/// The `redundant_lifetimes` lint detects lifetime parameters that are
1713+
/// redundant because they are equal to another named lifetime.
1714+
///
1715+
/// ### Example
1716+
///
1717+
/// ```rust,compile_fail
1718+
/// #[deny(redundant_lifetimes)]
1719+
///
1720+
/// // `'a = 'static`, so all usages of `'a` can be replaced with `'static`
1721+
/// pub fn bar<'a: 'static>() {}
1722+
///
1723+
/// // `'a = 'b`, so all usages of `'b` can be replaced with `'a`
1724+
/// pub fn bar<'a: 'b, 'b: 'a>() {}
1725+
/// ```
1726+
///
1727+
/// {{produces}}
1728+
///
1729+
/// ### Explanation
1730+
///
1731+
/// Unused lifetime parameters may signal a mistake or unfinished code.
1732+
/// Consider removing the parameter.
1733+
pub REDUNDANT_LIFETIMES,
1734+
Allow,
1735+
"detects lifetime parameters that are redundant because they are equal to some other named lifetime"
1736+
}
1737+
17101738
declare_lint! {
17111739
/// The `tyvar_behind_raw_pointer` lint detects raw pointer to an
17121740
/// inference variable.

‎compiler/rustc_middle/src/ty/context.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ pub struct GlobalCtxt<'tcx> {
757757
impl<'tcx> GlobalCtxt<'tcx> {
758758
/// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of
759759
/// `f`.
760-
pub fn enter<'a:'tcx,F, R>(&'a self, f: F) -> R
760+
pub fn enter<F, R>(&'tcx self, f: F) -> R
761761
where
762762
F: FnOnce(TyCtxt<'tcx>) -> R,
763763
{

‎compiler/rustc_middle/src/ty/diagnostics.rs‎

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,23 @@ use std::fmt::Write;
55
use std::ops::ControlFlow;
66

77
use crate::ty::{
8-
AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque,PolyTraitPredicate,
9-
Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,TypeSuperVisitable,TypeVisitable,
10-
TypeVisitor,
8+
self,AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque,
9+
PolyTraitPredicate,Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
10+
TypeSuperVisitable,TypeVisitable,TypeVisitor,
1111
};
1212

1313
use rustc_data_structures::fx::FxHashMap;
14-
use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg};
14+
use rustc_errors::{into_diag_arg_using_display,Applicability, Diag, DiagArgValue, IntoDiagArg};
1515
use rustc_hir as hir;
1616
use rustc_hir::def::DefKind;
1717
use rustc_hir::def_id::DefId;
1818
use rustc_hir::{PredicateOrigin, WherePredicate};
1919
use rustc_span::{BytePos, Span};
2020
use rustc_type_ir::TyKind::*;
2121

22-
impl<'tcx> IntoDiagArg for Ty<'tcx> {
23-
fn into_diag_arg(self) -> DiagArgValue {
24-
self.to_string().into_diag_arg()
25-
}
22+
into_diag_arg_using_display! {
23+
Ty<'_>,
24+
ty::Region<'_>,
2625
}
2726

2827
impl<'tcx> Ty<'tcx> {

‎tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
#![warn(unused_lifetimes)]
1+
#![warn(unused_lifetimes, redundant_lifetimes)]
22

33
pub trait X {
4-
type Y<'a: 'static>;
5-
//~^ WARNING unnecessary lifetime parameter
4+
type Y<'a: 'static>; //~ WARN unnecessary lifetime parameter `'a`
65
}
76

87
impl X for () {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /