regression: extra_unused_lifetimes warning about TypedBuilder that doesn't have an issue
C-bugI-false-positive
### Summary
1.94-1.96 has no problems with this (`cargo +1.96 clippy`):
```
cat Cargo.toml
[package]
name = "tmp"
version = "0.1.0"
edition = "2024"
[dependencies]
typed-builder = "0.23.2"
[lints]
workspace = true
```
But nightly claims there's an extra unused lifetime.
### Lint Name
extra_unused_lifetimes
### Reproducer
I tried this code:
```rust
use std::time::Duration;
use typed_builder::TypedBuilder;
#[expect(dead_code)]
#[derive(TypedBuilder)]
struct Options {
#[builder(default = Duration::from_secs(10))]
update_lru_every: Duration,
#[builder(default = Duration::from_mins(1))]
evict_entries_older_than: Duration,
}
fn main() {
println!("Hello, world!");
}
```
I saw this happen:
```
cargo +nightly clippy
warning: this lifetime isn't used in the type
--> tmp/src/main.rs:5:10
|
5 | #[derive(TypedBuilder)]
| ^^^^^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
= note: `#[warn(clippy::extra_unused_lifetimes)]` on by default
= note: this warning originates in the derive macro `TypedBuilder` (in Nightly builds, run with -Z macro-backtrace for more info)
```
I expected to see this happen:
No lints because a) this in a derive / macro that's not defined in this crate b) there's not even any lifetimes when you expand.
Here's the fully expanded as per cargo expand:
```
#![feature(prelude_import)]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
use std::time::Duration;
use typed_builder::TypedBuilder;
#[expect(dead_code)]
struct Options {
#[builder(default = Duration::from_secs(10))]
update_lru_every: Duration,
#[builder(default = Duration::from_mins(1))]
evict_entries_older_than: Duration,
}
#[automatically_derived]
impl Options {
/**
Create a builder for building `Options`.
On the builder, call `.update_lru_every(...)`(optional), `.evict_entries_older_than(...)`(optional) to set the values of the fields.
Finally, call `.build()` to create the instance of `Options`.
*/
#[allow(dead_code, clippy::default_trait_access)]
fn builder() -> OptionsBuilder<((), ())> {
OptionsBuilder {
fields: ((), ()),
phantom: ::core::default::Default::default(),
}
}
}
#[must_use]
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
struct OptionsBuilder<TypedBuilderFields = ((), ())> {
fields: TypedBuilderFields,
phantom: ::core::marker::PhantomData<()>,
}
#[automatically_derived]
impl<TypedBuilderFields> Clone for OptionsBuilder<TypedBuilderFields>
where
TypedBuilderFields: Clone,
{
#[allow(clippy::default_trait_access)]
fn clone(&self) -> Self {
Self {
fields: self.fields.clone(),
phantom: ::core::default::Default::default(),
}
}
}
#[allow(dead_code, non_camel_case_types, missing_docs)]
#[automatically_derived]
impl<__evict_entries_older_than> OptionsBuilder<((), __evict_entries_older_than)> {
#[allow(clippy::used_underscore_binding, clippy::no_effect_underscore_binding)]
pub fn update_lru_every(
self,
update_lru_every: Duration,
) -> OptionsBuilder<((Duration,), __evict_entries_older_than)> {
let update_lru_every = (update_lru_every,);
let ((), evict_entries_older_than) = self.fields;
OptionsBuilder {
fields: (update_lru_every, evict_entries_older_than),
phantom: self.phantom,
}
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
#[allow(clippy::exhaustive_enums)]
pub enum OptionsBuilder_Error_Repeated_field_update_lru_every {}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, missing_docs)]
#[automatically_derived]
impl<
__evict_entries_older_than,
> OptionsBuilder<((Duration,), __evict_entries_older_than)> {
#[deprecated(note = "Repeated field update_lru_every")]
pub fn update_lru_every(
self,
_: OptionsBuilder_Error_Repeated_field_update_lru_every,
) -> OptionsBuilder<((Duration,), __evict_entries_older_than)> {
self
}
}
#[allow(dead_code, non_camel_case_types, missing_docs)]
#[automatically_derived]
impl<__update_lru_every> OptionsBuilder<(__update_lru_every, ())> {
#[allow(clippy::used_underscore_binding, clippy::no_effect_underscore_binding)]
pub fn evict_entries_older_than(
self,
evict_entries_older_than: Duration,
) -> OptionsBuilder<(__update_lru_every, (Duration,))> {
let evict_entries_older_than = (evict_entries_older_than,);
let (update_lru_every, ()) = self.fields;
OptionsBuilder {
fields: (update_lru_every, evict_entries_older_than),
phantom: self.phantom,
}
}
}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, non_snake_case)]
#[allow(clippy::exhaustive_enums)]
pub enum OptionsBuilder_Error_Repeated_field_evict_entries_older_than {}
#[doc(hidden)]
#[allow(dead_code, non_camel_case_types, missing_docs)]
#[automatically_derived]
impl<__update_lru_every> OptionsBuilder<(__update_lru_every, (Duration,))> {
#[deprecated(note = "Repeated field evict_entries_older_than")]
pub fn evict_entries_older_than(
self,
_: OptionsBuilder_Error_Repeated_field_evict_entries_older_than,
) -> OptionsBuilder<(__update_lru_every, (Duration,))> {
self
}
}
#[allow(clippy::ref_option_ref)]
#[automatically_derived]
impl ::typed_builder::NextFieldDefault<((Duration,),)> for Options {
type Output = Duration;
fn resolve((.., (input,)): ((Duration,),)) -> Self::Output {
input
}
}
#[allow(clippy::ref_option_ref)]
#[automatically_derived]
impl ::typed_builder::NextFieldDefault<((),)> for Options {
type Output = Duration;
fn resolve(((),): ((),)) -> Self::Output {
Duration::from_secs(10)
}
}
#[allow(clippy::ref_option_ref)]
#[automatically_derived]
impl ::typed_builder::NextFieldDefault<(&Duration, (Duration,))> for Options {
type Output = Duration;
fn resolve((.., (input,)): (&Duration, (Duration,))) -> Self::Output {
input
}
}
#[allow(clippy::ref_option_ref)]
#[automatically_derived]
impl ::typed_builder::NextFieldDefault<(&Duration, ())> for Options {
type Output = Duration;
fn resolve((update_lru_every, ()): (&Duration, ())) -> Self::Output {
Duration::from_mins(1)
}
}
#[allow(dead_code, non_camel_case_types, missing_docs, clippy::ref_option_ref)]
#[automatically_derived]
impl<
__update_lru_every,
__evict_entries_older_than,
> OptionsBuilder<(__update_lru_every, __evict_entries_older_than)>
where
Options: for<'__typed_builder_lifetime_for_default> ::typed_builder::NextFieldDefault<
(__update_lru_every,),
Output = Duration,
>,
Options: for<'__typed_builder_lifetime_for_default> ::typed_builder::NextFieldDefault<
(&'__typed_builder_lifetime_for_default Duration, __evict_entries_older_than),
Output = Duration,
>,
{
#[allow(
clippy::default_trait_access,
clippy::used_underscore_binding,
clippy::no_effect_underscore_binding
)]
pub fn build(self) -> Options {
let (update_lru_every, evict_entries_older_than) = self.fields;
let update_lru_every = <Options as ::typed_builder::NextFieldDefault<
(__update_lru_every,),
>>::resolve((update_lru_every,));
let evict_entries_older_than = <Options as ::typed_builder::NextFieldDefault<
(&Duration, __evict_entries_older_than),
>>::resolve((&update_lru_every, evict_entries_older_than));
#[allow(deprecated)]
Options {
update_lru_every,
evict_entries_older_than,
}
.into()
}
}
fn main() {
{
::std::io::_print(format_args!("Hello, world!\n"));
};
}
```
### Version
```text
rustc -Vv
rustc 1.95.0 (59807616e 2026-04-14)
binary: rustc
commit-hash: 59807616e1fa2540724bfbac14d7976d7e4a3860
commit-date: 2026-04-14
host: aarch64-apple-darwin
release: 1.95.0
LLVM version: 22.1.2
rustc +1.96 -Vv
rustc 1.96.0 (ac68faa20 2026-05-25)
binary: rustc
commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96
commit-date: 2026-05-25
host: aarch64-apple-darwin
release: 1.96.0
LLVM version: 22.1.2
rustc +nightly -Vv
rustc 1.98.0-nightly (3daae5e42 2026-06-14)
binary: rustc
commit-hash: 3daae5e42ec9ba435212987331af1b7b8634fa90
commit-date: 2026-06-14
host: aarch64-apple-darwin
release: 1.98.0-nightly
LLVM version: 22.1.6
```
### Additional Labels
_No response_
0 条评论