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
This commit is contained in:
Guillaume Gomez 2024-04-10 16:15:22 +02:00 committed by GitHub
commit fa696a3629
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 332 additions and 103 deletions

View File

@ -46,6 +46,7 @@ impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T {
} }
} }
#[macro_export]
macro_rules! into_diag_arg_using_display { macro_rules! into_diag_arg_using_display {
($( $ty:ty ),+ $(,)?) => { ($( $ty:ty ),+ $(,)?) => {
$( $(

View File

@ -355,6 +355,9 @@ hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pa
hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
.label = not allowed in type signatures .label = not allowed in type signatures
hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}`
.note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}`
hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}`
hir_analysis_return_type_notation_equality_bound = hir_analysis_return_type_notation_equality_bound =

View File

@ -8,11 +8,13 @@ use rustc_ast as ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_hir::ItemKind; use rustc_hir::ItemKind;
use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_macros::LintDiagnostic;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::trait_def::TraitSpecializationKind;
@ -136,6 +138,8 @@ where
infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
lint_redundant_lifetimes(tcx, body_def_id, &outlives_env);
let errors = infcx.resolve_regions(&outlives_env); let errors = infcx.resolve_regions(&outlives_env);
if errors.is_empty() { if errors.is_empty() {
return Ok(()); return Ok(());
@ -2010,6 +2014,137 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error
res res
} }
fn lint_redundant_lifetimes<'tcx>(
tcx: TyCtxt<'tcx>,
owner_id: LocalDefId,
outlives_env: &OutlivesEnvironment<'tcx>,
) {
let def_kind = tcx.def_kind(owner_id);
match def_kind {
DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::Trait
| DefKind::TraitAlias
| DefKind::Fn
| DefKind::Const
| DefKind::Impl { of_trait: _ } => {
// Proceed
}
DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
let parent_def_id = tcx.local_parent(owner_id);
if matches!(tcx.def_kind(parent_def_id), DefKind::Impl { of_trait: true }) {
// Don't check for redundant lifetimes for associated items of trait
// implementations, since the signature is required to be compatible
// with the trait, even if the implementation implies some lifetimes
// are redundant.
return;
}
}
DefKind::Mod
| DefKind::Variant
| DefKind::TyAlias
| DefKind::ForeignTy
| DefKind::TyParam
| DefKind::ConstParam
| DefKind::Static { .. }
| DefKind::Ctor(_, _)
| DefKind::Macro(_)
| DefKind::ExternCrate
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::OpaqueTy
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::GlobalAsm
| DefKind::Closure => return,
}
// The ordering of this lifetime map is a bit subtle.
//
// Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime,
// where we can prove that `'candidate = 'victim`.
//
// `'static` must come first in this list because we can never replace `'static` with
// something else, but if we find some lifetime `'a` where `'a = 'static`, we want to
// suggest replacing `'a` with `'static`.
let mut lifetimes = vec![tcx.lifetimes.re_static];
lifetimes.extend(
ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()),
);
// If we are in a function, add its late-bound lifetimes too.
if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {
for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() {
let ty::BoundVariableKind::Region(kind) = var else { continue };
lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind));
}
}
lifetimes.retain(|candidate| candidate.has_name());
// Keep track of lifetimes which have already been replaced with other lifetimes.
// This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by
// both `'a` and `'b`.
let mut shadowed = FxHashSet::default();
for (idx, &candidate) in lifetimes.iter().enumerate() {
// Don't suggest removing a lifetime twice. We only need to check this
// here and not up in the `victim` loop because equality is transitive,
// so if A = C and B = C, then A must = B, so it'll be shadowed too in
// A's victim loop.
if shadowed.contains(&candidate) {
continue;
}
for &victim in &lifetimes[(idx + 1)..] {
// We should only have late-bound lifetimes of the `BrNamed` variety,
// since we get these signatures straight from `hir_lowering`. And any
// other regions (ReError/ReStatic/etc.) shouldn't matter, since we
// can't really suggest to remove them.
let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
| ty::ReLateParam(ty::LateParamRegion {
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
..
})) = victim.kind()
else {
continue;
};
// Do not rename lifetimes not local to this item since they'll overlap
// with the lint running on the parent. We still want to consider parent
// lifetimes which make child lifetimes redundant, otherwise we would
// have truncated the `identity_for_item` args above.
if tcx.parent(def_id) != owner_id.to_def_id() {
continue;
}
// If `candidate <: victim` and `victim <: candidate`, then they're equal.
if outlives_env.free_region_map().sub_free_regions(tcx, candidate, victim)
&& outlives_env.free_region_map().sub_free_regions(tcx, victim, candidate)
{
shadowed.insert(victim);
tcx.emit_node_span_lint(
rustc_lint_defs::builtin::REDUNDANT_LIFETIMES,
tcx.local_def_id_to_hir_id(def_id.expect_local()),
tcx.def_span(def_id),
RedundantLifetimeArgsLint { candidate, victim },
);
}
}
}
}
#[derive(LintDiagnostic)]
#[diag(hir_analysis_redundant_lifetime_args)]
#[note]
struct RedundantLifetimeArgsLint<'tcx> {
/// The lifetime we have found to be redundant.
victim: ty::Region<'tcx>,
// The lifetime we can replace the victim with.
candidate: ty::Region<'tcx>,
}
pub fn provide(providers: &mut Providers) { pub fn provide(providers: &mut Providers) {
*providers = Providers { check_mod_type_wf, check_well_formed, ..*providers }; *providers = Providers { check_mod_type_wf, check_well_formed, ..*providers };
} }

View File

@ -20,7 +20,6 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_bound_vars::*; use rustc_middle::middle::resolve_bound_vars::*;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor};
use rustc_session::lint;
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_span::symbol::{sym, Ident}; use rustc_span::symbol::{sym, Ident};
use rustc_span::Span; use rustc_span::Span;
@ -867,31 +866,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
}) => { }) => {
self.visit_lifetime(lifetime); self.visit_lifetime(lifetime);
walk_list!(self, visit_param_bound, bounds); walk_list!(self, visit_param_bound, bounds);
if lifetime.res != hir::LifetimeName::Static {
for bound in bounds {
let hir::GenericBound::Outlives(lt) = bound else {
continue;
};
if lt.res != hir::LifetimeName::Static {
continue;
}
self.insert_lifetime(lt, ResolvedArg::StaticLifetime);
self.tcx.node_span_lint(
lint::builtin::UNUSED_LIFETIMES,
lifetime.hir_id,
lifetime.ident.span,
format!("unnecessary lifetime parameter `{}`", lifetime.ident),
|lint| {
let help = format!(
"you can use the `'static` lifetime directly, in place of `{}`",
lifetime.ident,
);
lint.help(help);
},
);
}
}
} }
&hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => {
self.visit_ty(lhs_ty); self.visit_ty(lhs_ty);

View File

@ -79,6 +79,7 @@ declare_lint_pass! {
PROC_MACRO_BACK_COMPAT, PROC_MACRO_BACK_COMPAT,
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
PUB_USE_OF_PRIVATE_EXTERN_CRATE, PUB_USE_OF_PRIVATE_EXTERN_CRATE,
REDUNDANT_LIFETIMES,
REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_INTERNAL,
REFINING_IMPL_TRAIT_REACHABLE, REFINING_IMPL_TRAIT_REACHABLE,
RENAMED_AND_REMOVED_LINTS, RENAMED_AND_REMOVED_LINTS,
@ -1707,6 +1708,33 @@ declare_lint! {
"detects lifetime parameters that are never used" "detects lifetime parameters that are never used"
} }
declare_lint! {
/// The `redundant_lifetimes` lint detects lifetime parameters that are
/// redundant because they are equal to another named lifetime.
///
/// ### Example
///
/// ```rust,compile_fail
/// #[deny(redundant_lifetimes)]
///
/// // `'a = 'static`, so all usages of `'a` can be replaced with `'static`
/// pub fn bar<'a: 'static>() {}
///
/// // `'a = 'b`, so all usages of `'b` can be replaced with `'a`
/// pub fn bar<'a: 'b, 'b: 'a>() {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Unused lifetime parameters may signal a mistake or unfinished code.
/// Consider removing the parameter.
pub REDUNDANT_LIFETIMES,
Allow,
"detects lifetime parameters that are redundant because they are equal to some other named lifetime"
}
declare_lint! { declare_lint! {
/// The `tyvar_behind_raw_pointer` lint detects raw pointer to an /// The `tyvar_behind_raw_pointer` lint detects raw pointer to an
/// inference variable. /// inference variable.

View File

@ -757,7 +757,7 @@ pub struct GlobalCtxt<'tcx> {
impl<'tcx> GlobalCtxt<'tcx> { impl<'tcx> GlobalCtxt<'tcx> {
/// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of
/// `f`. /// `f`.
pub fn enter<'a: 'tcx, F, R>(&'a self, f: F) -> R pub fn enter<F, R>(&'tcx self, f: F) -> R
where where
F: FnOnce(TyCtxt<'tcx>) -> R, F: FnOnce(TyCtxt<'tcx>) -> R,
{ {

View File

@ -5,13 +5,13 @@ use std::fmt::Write;
use std::ops::ControlFlow; use std::ops::ControlFlow;
use crate::ty::{ use crate::ty::{
AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, PolyTraitPredicate, self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque,
Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
TypeVisitor, TypeSuperVisitable, TypeVisitable, TypeVisitor,
}; };
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg}; use rustc_errors::{into_diag_arg_using_display, Applicability, Diag, DiagArgValue, IntoDiagArg};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -19,10 +19,9 @@ use rustc_hir::{PredicateOrigin, WherePredicate};
use rustc_span::{BytePos, Span}; use rustc_span::{BytePos, Span};
use rustc_type_ir::TyKind::*; use rustc_type_ir::TyKind::*;
impl<'tcx> IntoDiagArg for Ty<'tcx> { into_diag_arg_using_display! {
fn into_diag_arg(self) -> DiagArgValue { Ty<'_>,
self.to_string().into_diag_arg() ty::Region<'_>,
}
} }
impl<'tcx> Ty<'tcx> { impl<'tcx> Ty<'tcx> {

View File

@ -1,8 +1,7 @@
#![warn(unused_lifetimes)] #![warn(unused_lifetimes, redundant_lifetimes)]
pub trait X { pub trait X {
type Y<'a: 'static>; type Y<'a: 'static>; //~ WARN unnecessary lifetime parameter `'a`
//~^ WARNING unnecessary lifetime parameter
} }
impl X for () { impl X for () {

View File

@ -1,18 +1,5 @@
warning: unnecessary lifetime parameter `'a`
--> $DIR/unsatisfied-item-lifetime-bound.rs:4:12
|
LL | type Y<'a: 'static>;
| ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
note: the lint level is defined here
--> $DIR/unsatisfied-item-lifetime-bound.rs:1:9
|
LL | #![warn(unused_lifetimes)]
| ^^^^^^^^^^^^^^^^
error[E0478]: lifetime bound not satisfied error[E0478]: lifetime bound not satisfied
--> $DIR/unsatisfied-item-lifetime-bound.rs:9:18 --> $DIR/unsatisfied-item-lifetime-bound.rs:8:18
| |
LL | type Y<'a: 'static>; LL | type Y<'a: 'static>;
| ------------------- definition of `Y` from trait | ------------------- definition of `Y` from trait
@ -21,7 +8,7 @@ LL | type Y<'a> = &'a ();
| ^^^^^^ | ^^^^^^
| |
note: lifetime parameter instantiated with the lifetime `'a` as defined here note: lifetime parameter instantiated with the lifetime `'a` as defined here
--> $DIR/unsatisfied-item-lifetime-bound.rs:9:12 --> $DIR/unsatisfied-item-lifetime-bound.rs:8:12
| |
LL | type Y<'a> = &'a (); LL | type Y<'a> = &'a ();
| ^^ | ^^
@ -32,44 +19,57 @@ LL | type Y<'a> = &'a () where 'a: 'static;
| +++++++++++++++++ | +++++++++++++++++
error[E0478]: lifetime bound not satisfied error[E0478]: lifetime bound not satisfied
--> $DIR/unsatisfied-item-lifetime-bound.rs:14:8 --> $DIR/unsatisfied-item-lifetime-bound.rs:13:8
| |
LL | f: <T as X>::Y<'a>, LL | f: <T as X>::Y<'a>,
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
| |
note: lifetime parameter instantiated with the lifetime `'a` as defined here note: lifetime parameter instantiated with the lifetime `'a` as defined here
--> $DIR/unsatisfied-item-lifetime-bound.rs:13:10 --> $DIR/unsatisfied-item-lifetime-bound.rs:12:10
| |
LL | struct B<'a, T: for<'r> X<Y<'r> = &'r ()>> { LL | struct B<'a, T: for<'r> X<Y<'r> = &'r ()>> {
| ^^ | ^^
= note: but lifetime parameter must outlive the static lifetime = note: but lifetime parameter must outlive the static lifetime
error[E0478]: lifetime bound not satisfied error[E0478]: lifetime bound not satisfied
--> $DIR/unsatisfied-item-lifetime-bound.rs:19:8 --> $DIR/unsatisfied-item-lifetime-bound.rs:18:8
| |
LL | f: <T as X>::Y<'a>, LL | f: <T as X>::Y<'a>,
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
| |
note: lifetime parameter instantiated with the lifetime `'a` as defined here note: lifetime parameter instantiated with the lifetime `'a` as defined here
--> $DIR/unsatisfied-item-lifetime-bound.rs:18:10 --> $DIR/unsatisfied-item-lifetime-bound.rs:17:10
| |
LL | struct C<'a, T: X> { LL | struct C<'a, T: X> {
| ^^ | ^^
= note: but lifetime parameter must outlive the static lifetime = note: but lifetime parameter must outlive the static lifetime
error[E0478]: lifetime bound not satisfied error[E0478]: lifetime bound not satisfied
--> $DIR/unsatisfied-item-lifetime-bound.rs:24:8 --> $DIR/unsatisfied-item-lifetime-bound.rs:23:8
| |
LL | f: <() as X>::Y<'a>, LL | f: <() as X>::Y<'a>,
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
| |
note: lifetime parameter instantiated with the lifetime `'a` as defined here note: lifetime parameter instantiated with the lifetime `'a` as defined here
--> $DIR/unsatisfied-item-lifetime-bound.rs:23:10 --> $DIR/unsatisfied-item-lifetime-bound.rs:22:10
| |
LL | struct D<'a> { LL | struct D<'a> {
| ^^ | ^^
= note: but lifetime parameter must outlive the static lifetime = note: but lifetime parameter must outlive the static lifetime
warning: unnecessary lifetime parameter `'a`
--> $DIR/unsatisfied-item-lifetime-bound.rs:4:12
|
LL | type Y<'a: 'static>;
| ^^
|
= note: you can use the `'static` lifetime directly, in place of `'a`
note: the lint level is defined here
--> $DIR/unsatisfied-item-lifetime-bound.rs:1:27
|
LL | #![warn(unused_lifetimes, redundant_lifetimes)]
| ^^^^^^^^^^^^^^^^^^^
error: aborting due to 4 previous errors; 1 warning emitted error: aborting due to 4 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0478`. For more information about this error, try `rustc --explain E0478`.

View File

@ -8,10 +8,10 @@
// //
// 'a : 'b // 'a : 'b
#![warn(unused_lifetimes)] #![warn(redundant_lifetimes)]
fn test<'a,'b>(x: &'a i32) -> &'b i32 fn test<'a,'b>(x: &'a i32) -> &'b i32 //~ WARN unnecessary lifetime parameter `'a`
where 'a: 'static //~ WARN unnecessary lifetime parameter `'a` where 'a: 'static
{ {
x x
} }

View File

@ -1,15 +1,15 @@
warning: unnecessary lifetime parameter `'a` warning: unnecessary lifetime parameter `'a`
--> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:14:11 --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:13:9
| |
LL | where 'a: 'static LL | fn test<'a,'b>(x: &'a i32) -> &'b i32
| ^^ | ^^
| |
= help: you can use the `'static` lifetime directly, in place of `'a` = note: you can use the `'static` lifetime directly, in place of `'a`
note: the lint level is defined here note: the lint level is defined here
--> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:11:9 --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:11:9
| |
LL | #![warn(unused_lifetimes)] LL | #![warn(redundant_lifetimes)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
warning: 1 warning emitted warning: 1 warning emitted

View File

@ -1,18 +1,19 @@
//@ run-pass //@ run-pass
#![warn(unused_lifetimes)] #![warn(redundant_lifetimes)]
fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a () fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a ()
where 'a: 'static { t }
//~^ WARN unnecessary lifetime parameter `'a` //~^ WARN unnecessary lifetime parameter `'a`
where 'a: 'static { t }
fn static_id<'a>(t: &'a ()) -> &'static () fn static_id<'a>(t: &'a ()) -> &'static ()
where 'a: 'static { t }
//~^ WARN unnecessary lifetime parameter `'a` //~^ WARN unnecessary lifetime parameter `'a`
where 'a: 'static { t }
fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
//~^ WARN unnecessary lifetime parameter `'a`
//~| WARN unnecessary lifetime parameter `'b`
where 'a: 'b, 'b: 'static { t } where 'a: 'b, 'b: 'static { t }
//~^ WARN unnecessary lifetime parameter `'b`
fn ref_id<'a>(t: &'a ()) -> &'a () where 'static: 'a { t } fn ref_id<'a>(t: &'a ()) -> &'a () where 'static: 'a { t }

View File

@ -1,31 +1,39 @@
warning: unnecessary lifetime parameter `'a` warning: unnecessary lifetime parameter `'a`
--> $DIR/regions-static-bound-rpass.rs:6:11 --> $DIR/regions-static-bound-rpass.rs:5:17
| |
LL | where 'a: 'static { t } LL | fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a ()
| ^^ | ^^
| |
= help: you can use the `'static` lifetime directly, in place of `'a` = note: you can use the `'static` lifetime directly, in place of `'a`
note: the lint level is defined here note: the lint level is defined here
--> $DIR/regions-static-bound-rpass.rs:3:9 --> $DIR/regions-static-bound-rpass.rs:3:9
| |
LL | #![warn(unused_lifetimes)] LL | #![warn(redundant_lifetimes)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
warning: unnecessary lifetime parameter `'a` warning: unnecessary lifetime parameter `'a`
--> $DIR/regions-static-bound-rpass.rs:10:11 --> $DIR/regions-static-bound-rpass.rs:9:14
| |
LL | where 'a: 'static { t } LL | fn static_id<'a>(t: &'a ()) -> &'static ()
| ^^ | ^^
| |
= help: you can use the `'static` lifetime directly, in place of `'a` = note: you can use the `'static` lifetime directly, in place of `'a`
warning: unnecessary lifetime parameter `'a`
--> $DIR/regions-static-bound-rpass.rs:13:23
|
LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
| ^^
|
= note: you can use the `'static` lifetime directly, in place of `'a`
warning: unnecessary lifetime parameter `'b` warning: unnecessary lifetime parameter `'b`
--> $DIR/regions-static-bound-rpass.rs:14:19 --> $DIR/regions-static-bound-rpass.rs:13:26
| |
LL | where 'a: 'b, 'b: 'static { t } LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
| ^^ | ^^
| |
= help: you can use the `'static` lifetime directly, in place of `'b` = note: you can use the `'static` lifetime directly, in place of `'b`
warning: 3 warnings emitted warning: 4 warnings emitted

View File

@ -1,12 +1,13 @@
#![warn(unused_lifetimes)] #![warn(unused_lifetimes, redundant_lifetimes)]
fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t }
//~^ WARN lifetime parameter `'b` never used //~^ WARN unnecessary lifetime parameter `'a`
//~| WARN unnecessary lifetime parameter `'a` //~| WARN lifetime parameter `'b` never used
fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
//~^ WARN unnecessary lifetime parameter `'a`
//~| WARN unnecessary lifetime parameter `'b`
where 'a: 'b, 'b: 'static { t } where 'a: 'b, 'b: 'static { t }
//~^ WARN unnecessary lifetime parameter `'b`
fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
t t

View File

@ -9,27 +9,40 @@ LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t }
note: the lint level is defined here note: the lint level is defined here
--> $DIR/regions-static-bound.rs:1:9 --> $DIR/regions-static-bound.rs:1:9
| |
LL | #![warn(unused_lifetimes)] LL | #![warn(unused_lifetimes, redundant_lifetimes)]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
warning: unnecessary lifetime parameter `'a` warning: unnecessary lifetime parameter `'a`
--> $DIR/regions-static-bound.rs:3:53 --> $DIR/regions-static-bound.rs:3:14
| |
LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t }
| ^^ | ^^
| |
= help: you can use the `'static` lifetime directly, in place of `'a` = note: you can use the `'static` lifetime directly, in place of `'a`
note: the lint level is defined here
--> $DIR/regions-static-bound.rs:1:27
|
LL | #![warn(unused_lifetimes, redundant_lifetimes)]
| ^^^^^^^^^^^^^^^^^^^
warning: unnecessary lifetime parameter `'a`
--> $DIR/regions-static-bound.rs:7:23
|
LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
| ^^
|
= note: you can use the `'static` lifetime directly, in place of `'a`
warning: unnecessary lifetime parameter `'b` warning: unnecessary lifetime parameter `'b`
--> $DIR/regions-static-bound.rs:8:19 --> $DIR/regions-static-bound.rs:7:26
| |
LL | where 'a: 'b, 'b: 'static { t } LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
| ^^ | ^^
| |
= help: you can use the `'static` lifetime directly, in place of `'b` = note: you can use the `'static` lifetime directly, in place of `'b`
error: lifetime may not live long enough error: lifetime may not live long enough
--> $DIR/regions-static-bound.rs:12:5 --> $DIR/regions-static-bound.rs:13:5
| |
LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
| -- lifetime `'a` defined here | -- lifetime `'a` defined here
@ -37,7 +50,7 @@ LL | t
| ^ returning this value requires that `'a` must outlive `'static` | ^ returning this value requires that `'a` must outlive `'static`
error[E0521]: borrowed data escapes outside of function error[E0521]: borrowed data escapes outside of function
--> $DIR/regions-static-bound.rs:17:5 --> $DIR/regions-static-bound.rs:18:5
| |
LL | fn error(u: &(), v: &()) { LL | fn error(u: &(), v: &()) {
| - - let's call the lifetime of this reference `'1` | - - let's call the lifetime of this reference `'1`
@ -50,7 +63,7 @@ LL | static_id(&u);
| argument requires that `'1` must outlive `'static` | argument requires that `'1` must outlive `'static`
error[E0521]: borrowed data escapes outside of function error[E0521]: borrowed data escapes outside of function
--> $DIR/regions-static-bound.rs:19:5 --> $DIR/regions-static-bound.rs:20:5
| |
LL | fn error(u: &(), v: &()) { LL | fn error(u: &(), v: &()) {
| - - let's call the lifetime of this reference `'2` | - - let's call the lifetime of this reference `'2`
@ -63,6 +76,6 @@ LL | static_id_indirect(&v);
| `v` escapes the function body here | `v` escapes the function body here
| argument requires that `'2` must outlive `'static` | argument requires that `'2` must outlive `'static`
error: aborting due to 3 previous errors; 3 warnings emitted error: aborting due to 3 previous errors; 4 warnings emitted
For more information about this error, try `rustc --explain E0521`. For more information about this error, try `rustc --explain E0521`.

View File

@ -0,0 +1,20 @@
#![deny(redundant_lifetimes)]
fn a<'a, 'b>(x: &'a &'b &'a ()) {} //~ ERROR unnecessary lifetime parameter `'b`
fn b<'a: 'b, 'b: 'a>() {} //~ ERROR unnecessary lifetime parameter `'b`
struct Foo<T: 'static>(T);
fn c<'a>(_: Foo<&'a ()>) {} //~ ERROR unnecessary lifetime parameter `'a`
struct Bar<'a>(&'a ());
impl<'a> Bar<'a> {
fn d<'b: 'a>(&'b self) {} //~ ERROR unnecessary lifetime parameter `'b`
}
fn ok(x: &'static &()) {}
trait Tr<'a> {}
impl<'a: 'static> Tr<'a> for () {} //~ ERROR unnecessary lifetime parameter `'a`
fn main() {}

View File

@ -0,0 +1,47 @@
error: unnecessary lifetime parameter `'b`
--> $DIR/transitively-redundant-lifetimes.rs:3:10
|
LL | fn a<'a, 'b>(x: &'a &'b &'a ()) {}
| ^^
|
= note: you can use the `'a` lifetime directly, in place of `'b`
note: the lint level is defined here
--> $DIR/transitively-redundant-lifetimes.rs:1:9
|
LL | #![deny(redundant_lifetimes)]
| ^^^^^^^^^^^^^^^^^^^
error: unnecessary lifetime parameter `'b`
--> $DIR/transitively-redundant-lifetimes.rs:5:14
|
LL | fn b<'a: 'b, 'b: 'a>() {}
| ^^
|
= note: you can use the `'a` lifetime directly, in place of `'b`
error: unnecessary lifetime parameter `'a`
--> $DIR/transitively-redundant-lifetimes.rs:8:6
|
LL | fn c<'a>(_: Foo<&'a ()>) {}
| ^^
|
= note: you can use the `'static` lifetime directly, in place of `'a`
error: unnecessary lifetime parameter `'a`
--> $DIR/transitively-redundant-lifetimes.rs:18:6
|
LL | impl<'a: 'static> Tr<'a> for () {}
| ^^
|
= note: you can use the `'static` lifetime directly, in place of `'a`
error: unnecessary lifetime parameter `'b`
--> $DIR/transitively-redundant-lifetimes.rs:12:10
|
LL | fn d<'b: 'a>(&'b self) {}
| ^^
|
= note: you can use the `'a` lifetime directly, in place of `'b`
error: aborting due to 5 previous errors