ITADN

regression: extra_unused_lifetimes warning about TypedBuilder that doesn't have an issue

#17255Openvlovich 创建于 2026-06-15
C-bugI-false-positive
V
vlovichcommented
### 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 条评论