From ccb9e9e84ade0df4d26f83ce020bca512ec9914f Mon Sep 17 00:00:00 2001 From: Qai Juang Date: Sun, 24 May 2026 03:37:29 -0400 Subject: [PATCH] account for lifetime bounds in single_use_lifetimes --- .../rustc_resolve/src/late/diagnostics.rs | 3 ++ tests/ui/single-use-lifetime/issue-104440.rs | 6 ++-- .../single-use-lifetime/issue-104440.stderr | 28 --------------- tests/ui/single-use-lifetime/issue-117965.rs | 3 +- .../single-use-lifetime/issue-117965.stderr | 16 --------- .../ui/single-use-lifetime/lifetime-bounds.rs | 34 +++++++++++++++++++ .../lifetime-bounds.stderr | 14 ++++++++ .../two-uses-in-trait-impl.rs | 14 -------- 8 files changed, 57 insertions(+), 61 deletions(-) delete mode 100644 tests/ui/single-use-lifetime/issue-104440.stderr delete mode 100644 tests/ui/single-use-lifetime/issue-117965.stderr create mode 100644 tests/ui/single-use-lifetime/lifetime-bounds.rs create mode 100644 tests/ui/single-use-lifetime/lifetime-bounds.stderr diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 873aad38a785..419a47b12698 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3630,6 +3630,9 @@ pub(crate) fn maybe_report_lifetime_uses( }; match use_set { Some(LifetimeUseSet::Many) => {} + // A lifetime bound is a real use of that lifetime parameter, even + // though visiting a bound like `'b: 'a` only records a use of `'a`. + Some(LifetimeUseSet::One { .. }) if !param.bounds.is_empty() => {} Some(LifetimeUseSet::One { use_span, use_ctxt }) => { let param_ident = param.ident; let deletion_span = diff --git a/tests/ui/single-use-lifetime/issue-104440.rs b/tests/ui/single-use-lifetime/issue-104440.rs index cecd17fb930b..6c4398f4372d 100644 --- a/tests/ui/single-use-lifetime/issue-104440.rs +++ b/tests/ui/single-use-lifetime/issue-104440.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(decl_macro, rustc_attrs)] #![deny(single_use_lifetimes)] @@ -35,7 +37,7 @@ fn k(t1: $T, t2: T) -> (T, $T) { mod lifetime_params { macro m($a:lifetime) { - fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { //~ ERROR lifetime parameter `'a` only used once + fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { (t1, t2) } } @@ -60,7 +62,7 @@ fn k<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { } } - m!('a); //~ ERROR lifetime parameter `'a` only used once + m!('a); n!('a); p!('a); } diff --git a/tests/ui/single-use-lifetime/issue-104440.stderr b/tests/ui/single-use-lifetime/issue-104440.stderr deleted file mode 100644 index 54ded31dcbe8..000000000000 --- a/tests/ui/single-use-lifetime/issue-104440.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: lifetime parameter `'a` only used once - --> $DIR/issue-104440.rs:63:8 - | -LL | m!('a); - | ^^ - | | - | this lifetime... - | ...is used only here - | -note: the lint level is defined here - --> $DIR/issue-104440.rs:2:9 - | -LL | #![deny(single_use_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: lifetime parameter `'a` only used once - --> $DIR/issue-104440.rs:38:30 - | -LL | fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { - | ^^ this lifetime... -- ...is used only here -... -LL | m!('a); - | ------ in this macro invocation - | - = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 2 previous errors - diff --git a/tests/ui/single-use-lifetime/issue-117965.rs b/tests/ui/single-use-lifetime/issue-117965.rs index 5eb2a03e13da..7c3b32651736 100644 --- a/tests/ui/single-use-lifetime/issue-117965.rs +++ b/tests/ui/single-use-lifetime/issue-117965.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![deny(single_use_lifetimes)] pub enum Data<'a> { @@ -7,7 +9,6 @@ pub enum Data<'a> { impl<'a> Data<'a> { pub fn get<'b: 'a>(&'b self) -> &'a str { - //~^ ERROR lifetime parameter `'b` only used once match &self { Self::Borrowed(val) => val, Self::Owned(val) => &val, diff --git a/tests/ui/single-use-lifetime/issue-117965.stderr b/tests/ui/single-use-lifetime/issue-117965.stderr deleted file mode 100644 index ed14ab92f4d1..000000000000 --- a/tests/ui/single-use-lifetime/issue-117965.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: lifetime parameter `'b` only used once - --> $DIR/issue-117965.rs:9:16 - | -LL | pub fn get<'b: 'a>(&'b self) -> &'a str { - | ^^ -- ...is used only here - | | - | this lifetime... - | -note: the lint level is defined here - --> $DIR/issue-117965.rs:1:9 - | -LL | #![deny(single_use_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/single-use-lifetime/lifetime-bounds.rs b/tests/ui/single-use-lifetime/lifetime-bounds.rs new file mode 100644 index 000000000000..a29d68a0b4a9 --- /dev/null +++ b/tests/ui/single-use-lifetime/lifetime-bounds.rs @@ -0,0 +1,34 @@ +// Regression test for https://github.com/rust-lang/rust/issues/153836. +#![deny(single_use_lifetimes)] +#![allow(dead_code)] +#![allow(unused_variables)] + +struct Foo<'a>(&'a i32); +struct Bar<'b>(&'b i32); + +fn function<'a, 'b: 'a>(_: Foo<'a>, _: Bar<'b>) {} + +type FnPtr = for<'a, 'b: 'a> fn(Foo<'a>, Bar<'b>); +//~^ ERROR bounds cannot be used in this context + +trait WhereBound<'a, 'b> {} + +fn where_bound() +where + T: for<'a, 'b: 'a> WhereBound<'a, 'b>, + //~^ ERROR bounds cannot be used in this context +{ +} + +trait ImplTrait<'a> { + fn foo(self, foo: Foo<'a>); +} + +impl<'a, 'b: 'a> ImplTrait<'a> for Bar<'b> { + fn foo(self, foo: Foo<'a>) { + let _: &'a i32 = self.0; + let _: Foo<'a> = foo; + } +} + +fn main() {} diff --git a/tests/ui/single-use-lifetime/lifetime-bounds.stderr b/tests/ui/single-use-lifetime/lifetime-bounds.stderr new file mode 100644 index 000000000000..3cc9e8ac90a6 --- /dev/null +++ b/tests/ui/single-use-lifetime/lifetime-bounds.stderr @@ -0,0 +1,14 @@ +error: bounds cannot be used in this context + --> $DIR/lifetime-bounds.rs:11:26 + | +LL | type FnPtr = for<'a, 'b: 'a> fn(Foo<'a>, Bar<'b>); + | ^^ + +error: bounds cannot be used in this context + --> $DIR/lifetime-bounds.rs:18:20 + | +LL | T: for<'a, 'b: 'a> WhereBound<'a, 'b>, + | ^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs b/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs index 983157614a24..1f0da77baee8 100644 --- a/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs +++ b/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs @@ -19,18 +19,4 @@ fn next(&mut self) -> Option { } } -trait BoundTrait<'a> { - fn foo(self, handler: &Handler<'a>); -} - -struct Handler<'a>(fn(&'a u32)); -struct Bar<'b>(&'b u32); - -// https://github.com/rust-lang/rust/issues/153836 -impl<'a, 'b: 'a> BoundTrait<'a> for Bar<'b> { - fn foo(self, handler: &Handler<'a>) { - (handler.0)(self.0); - } -} - fn main() {}