diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a067534b18938..2baaad0c642f6 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1621,6 +1621,40 @@ declare_lint! { "detects lifetime parameters that are never used" } +declare_lint! { + /// The `named_static_lifetimes` lint detects lifetime parameters that are + /// defined as outliving the static lifetime. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[deny(named_static_lifetimes)] + /// + /// pub fn foo<'a>(_s: &'a str) where 'a: 'static {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// By definition, the static lifetime (`'static`) outlives every other + /// lifetime. If another lifetime `'a` lives at least as long as `'static`, + /// then it is identical to `'static`. In that case, there is no need for + /// the name `'a`, and it likely only makes the code harder to read. + /// + /// To fix this, consider using `'static` instead of the named lifetime. + /// Here is the result of doing that in the example above: + /// + /// ```rust + /// #[deny(named_static_lifetimes)] + /// + /// pub fn foo(_s: &'static str) {} + /// ``` + pub NAMED_STATIC_LIFETIMES, + Warn, + "detects lifetime parameters that are identical to `'static`" +} + declare_lint! { /// The `tyvar_behind_raw_pointer` lint detects raw pointer to an /// inference variable. @@ -3174,6 +3208,7 @@ declare_lint_pass! { UNCONDITIONAL_RECURSION, SINGLE_USE_LIFETIMES, UNUSED_LIFETIMES, + NAMED_STATIC_LIFETIMES, UNUSED_LABELS, TYVAR_BEHIND_RAW_POINTER, ELIDED_LIFETIMES_IN_PATHS, diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 447f4174c10d5..87898ca4ee897 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -21,6 +21,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; +use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -1255,20 +1256,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { continue; } this.insert_lifetime(lt, Region::Static); - this.tcx - .sess - .struct_span_warn( - lifetime.span, - &format!( + this.tcx.struct_span_lint_hir( + lint::builtin::NAMED_STATIC_LIFETIMES, + lifetime.hir_id, + lifetime.span, + |lint| { + let msg = &format!( "unnecessary lifetime parameter `{}`", lifetime.name.ident(), - ), - ) - .help(&format!( - "you can use the `'static` lifetime directly, in place of `{}`", - lifetime.name.ident(), - )) - .emit(); + ); + let help = &format!( + "you can use the `'static` lifetime directly, in place of `{}`", + lifetime.name.ident(), + ); + lint.build(msg) + .help(help) + .emit(); + }, + ); } } } diff --git a/src/test/ui/generic-associated-types/unsatified-item-lifetime-bound.stderr b/src/test/ui/generic-associated-types/unsatified-item-lifetime-bound.stderr index ae52010cc50a2..1b600913d6445 100644 --- a/src/test/ui/generic-associated-types/unsatified-item-lifetime-bound.stderr +++ b/src/test/ui/generic-associated-types/unsatified-item-lifetime-bound.stderr @@ -4,6 +4,7 @@ warning: unnecessary lifetime parameter `'a` LL | type Y<'a: 'static>; | ^^ | + = note: `#[warn(named_static_lifetimes)]` on by default = help: you can use the `'static` lifetime directly, in place of `'a` error[E0478]: lifetime bound not satisfied diff --git a/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr b/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr index 3e48aef553b16..2a3c9f270888f 100644 --- a/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr +++ b/src/test/ui/impl-trait/equal-hidden-lifetimes.stderr @@ -4,6 +4,7 @@ warning: unnecessary lifetime parameter `'a` LL | fn equal_regions_static<'a: 'static>(x: &'a i32) -> impl Sized { | ^^ | + = note: `#[warn(named_static_lifetimes)]` on by default = help: you can use the `'static` lifetime directly, in place of `'a` warning: 1 warning emitted diff --git a/src/test/ui/issues/issue-30438-c.stderr b/src/test/ui/issues/issue-30438-c.stderr index a7a5c0500fd23..d06d740ccc709 100644 --- a/src/test/ui/issues/issue-30438-c.stderr +++ b/src/test/ui/issues/issue-30438-c.stderr @@ -4,6 +4,7 @@ warning: unnecessary lifetime parameter `'z` LL | fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y as Trait>::Out where 'z: 'static { | ^^ | + = note: `#[warn(named_static_lifetimes)]` on by default = help: you can use the `'static` lifetime directly, in place of `'z` error[E0515]: cannot return reference to local variable `x` diff --git a/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr b/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr index 70ed418d5cbbb..94d85913c56a9 100644 --- a/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr +++ b/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr @@ -4,6 +4,7 @@ warning: unnecessary lifetime parameter `'a` LL | where 'a: 'static | ^^ | + = note: `#[warn(named_static_lifetimes)]` on by default = help: you can use the `'static` lifetime directly, in place of `'a` warning: 1 warning emitted diff --git a/src/test/ui/regions/regions-static-bound-rpass.stderr b/src/test/ui/regions/regions-static-bound-rpass.stderr index 9355a409d5099..298c4ce8b8fbe 100644 --- a/src/test/ui/regions/regions-static-bound-rpass.stderr +++ b/src/test/ui/regions/regions-static-bound-rpass.stderr @@ -4,6 +4,7 @@ warning: unnecessary lifetime parameter `'a` LL | where 'a: 'static { t } | ^^ | + = note: `#[warn(named_static_lifetimes)]` on by default = help: you can use the `'static` lifetime directly, in place of `'a` warning: unnecessary lifetime parameter `'a` diff --git a/src/test/ui/regions/regions-static-bound.stderr b/src/test/ui/regions/regions-static-bound.stderr index 2886ec3ead51f..678f55e08fadc 100644 --- a/src/test/ui/regions/regions-static-bound.stderr +++ b/src/test/ui/regions/regions-static-bound.stderr @@ -4,6 +4,7 @@ warning: unnecessary lifetime parameter `'a` LL | where 'a: 'static { t } | ^^ | + = note: `#[warn(named_static_lifetimes)]` on by default = help: you can use the `'static` lifetime directly, in place of `'a` warning: unnecessary lifetime parameter `'b` diff --git a/src/test/ui/static/static-lifetime-bound.stderr b/src/test/ui/static/static-lifetime-bound.stderr index ef07a89315f40..cc7d9b14b4f2f 100644 --- a/src/test/ui/static/static-lifetime-bound.stderr +++ b/src/test/ui/static/static-lifetime-bound.stderr @@ -4,6 +4,7 @@ warning: unnecessary lifetime parameter `'a` LL | fn f<'a: 'static>(_: &'a i32) {} | ^^ | + = note: `#[warn(named_static_lifetimes)]` on by default = help: you can use the `'static` lifetime directly, in place of `'a` error[E0597]: `x` does not live long enough diff --git a/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr b/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr index 920eef11da4b9..79ad389c03b75 100644 --- a/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr +++ b/src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr @@ -4,6 +4,7 @@ warning: unnecessary lifetime parameter `'a` LL | fn f<'a: 'static>(t: &'a str) -> X<'a> { | ^^ | + = note: `#[warn(named_static_lifetimes)]` on by default = help: you can use the `'static` lifetime directly, in place of `'a` error: non-defining opaque type use in defining scope