From 594df2e63e69dad2db69b8d69f705fd96cc35995 Mon Sep 17 00:00:00 2001 From: eisverygoodletter Date: Thu, 2 Oct 2025 19:16:39 +1000 Subject: [PATCH 1/8] Preserve fn and {} for start_fn so that rustdoc generates correct links --- .../src/builder/builder_gen/input_fn/mod.rs | 3 ++ .../src/builder/builder_gen/input_struct.rs | 2 + bon-macros/src/builder/builder_gen/models.rs | 15 +++++++ .../src/builder/builder_gen/start_fn.rs | 39 ++++++++++++------- bon/src/lib.rs | 2 +- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/bon-macros/src/builder/builder_gen/input_fn/mod.rs b/bon-macros/src/builder/builder_gen/input_fn/mod.rs index 9547cf48..79b204c3 100644 --- a/bon-macros/src/builder/builder_gen/input_fn/mod.rs +++ b/bon-macros/src/builder/builder_gen/input_fn/mod.rs @@ -97,6 +97,9 @@ impl<'a> FnInputCtx<'a> { .collect(), params.fn_item.norm.sig.generics.where_clause.clone(), )), + + orig_fn_token: Some(params.fn_item.orig.sig.fn_token), + orig_brace_tokens: Some(params.fn_item.orig.block.brace_token), }; let self_ty_prefix = params.impl_ctx.as_deref().and_then(|impl_ctx| { diff --git a/bon-macros/src/builder/builder_gen/input_struct.rs b/bon-macros/src/builder/builder_gen/input_struct.rs index caeb3037..830490a5 100644 --- a/bon-macros/src/builder/builder_gen/input_struct.rs +++ b/bon-macros/src/builder/builder_gen/input_struct.rs @@ -187,6 +187,8 @@ impl StructInputCtx { vis: start_fn_vis.map(SpannedKey::into_value), docs: start_fn_docs, generics: None, + orig_brace_tokens: None, + orig_fn_token: None, }; let assoc_method_ctx = Some(AssocMethodCtxParams { diff --git a/bon-macros/src/builder/builder_gen/models.rs b/bon-macros/src/builder/builder_gen/models.rs index 4462b3d2..95adf9ba 100644 --- a/bon-macros/src/builder/builder_gen/models.rs +++ b/bon-macros/src/builder/builder_gen/models.rs @@ -85,6 +85,13 @@ pub(super) struct StartFn { /// Overrides the default generics pub(super) generics: Option, + + /// Preserve the original fn token span. + /// This determines the generated fn's span start + pub(super) orig_fn_token: syn::token::Fn, + /// Preserve the original brace token. + /// This determines the generated fn's span end + pub(super) orig_brace_tokens: syn::token::Brace, } pub(super) struct StartFnParams { @@ -97,6 +104,12 @@ pub(super) struct StartFnParams { /// Overrides the default generics pub(super) generics: Option, + /// Preserve original fn token for its span if possible. Defaults to + /// macro callsite if not supplied. + pub(super) orig_fn_token: Option, + /// Preserve original brace tokens `{}` for their spans if possible. + /// Defaults to macro callsite if not supplied. + pub(super) orig_brace_tokens: Option, } pub(super) struct BuilderType { @@ -309,6 +322,8 @@ impl BuilderGenCtx { vis: start_fn.vis.unwrap_or_else(|| builder_type.vis.clone()), docs: start_fn.docs, generics: start_fn.generics, + orig_fn_token: start_fn.orig_fn_token.unwrap_or_default(), + orig_brace_tokens: start_fn.orig_brace_tokens.unwrap_or_default(), }; let finish_fn = FinishFn { diff --git a/bon-macros/src/builder/builder_gen/start_fn.rs b/bon-macros/src/builder/builder_gen/start_fn.rs index e61e59e7..bdfb9061 100644 --- a/bon-macros/src/builder/builder_gen/start_fn.rs +++ b/bon-macros/src/builder/builder_gen/start_fn.rs @@ -96,6 +96,29 @@ impl super::BuilderGenCtx { }; let const_ = &self.const_; + let fn_kw = self.start_fn.orig_fn_token; + + // construct function body separately so that we can reuse the original + // function's braces and span. Reusing the span makes rustdoc link + // our generated function to the original function rather than the + // macro invocation site. + let body_contents: Vec = syn::parse_quote! { + #ide_hints + #( #start_fn_vars )* + #( #custom_fields_vars )* + + #builder_ident { + __unsafe_private_phantom: ::core::marker::PhantomData, + #( #custom_fields_idents, )* + #receiver_field_init + #( #start_fn_args_fields_idents, )* + __unsafe_private_named: #named_members_field_init, + } + }; + let body_block = syn::Block { + brace_token: self.start_fn.orig_brace_tokens, + stmts: body_contents, + }; syn::parse_quote! { #(#docs)* @@ -109,24 +132,12 @@ impl super::BuilderGenCtx { // const operations. clippy::missing_const_for_fn, )] - #vis #const_ fn #start_fn_ident< #(#generics_decl),* >( + #vis #const_ #fn_kw #start_fn_ident< #(#generics_decl),* >( #receiver #(#start_fn_params,)* ) -> #builder_ident< #(#generic_args,)* > #where_clause - { - #ide_hints - #( #start_fn_vars )* - #( #custom_fields_vars )* - - #builder_ident { - __unsafe_private_phantom: ::core::marker::PhantomData, - #( #custom_fields_idents, )* - #receiver_field_init - #( #start_fn_args_fields_idents, )* - __unsafe_private_named: #named_members_field_init, - } - } + #body_block } } } diff --git a/bon/src/lib.rs b/bon/src/lib.rs index 398a3402..a8cd04a8 100644 --- a/bon/src/lib.rs +++ b/bon/src/lib.rs @@ -14,7 +14,7 @@ // Rexport all macros from the proc-macro crate. pub use bon_macros::{bon, builder, map, set, Builder}; -/// Small utility declarative macros for creating colletions with [`Into`] conversions. +/// Small utility declarative macros for creating collections with [`Into`] conversions. mod collections; #[doc(hidden)] From 373cb719632ba8a99f3d04413a2d284053550252 Mon Sep 17 00:00:00 2001 From: eisverygoodletter Date: Thu, 2 Oct 2025 22:06:47 +1000 Subject: [PATCH 2/8] rename and simplify body construction based on suggested changes --- bon-macros/src/builder/builder_gen/models.rs | 8 ++--- .../src/builder/builder_gen/start_fn.rs | 31 +++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/bon-macros/src/builder/builder_gen/models.rs b/bon-macros/src/builder/builder_gen/models.rs index 95adf9ba..bc87e187 100644 --- a/bon-macros/src/builder/builder_gen/models.rs +++ b/bon-macros/src/builder/builder_gen/models.rs @@ -88,10 +88,10 @@ pub(super) struct StartFn { /// Preserve the original fn token span. /// This determines the generated fn's span start - pub(super) orig_fn_token: syn::token::Fn, + pub(super) fn_token: syn::token::Fn, /// Preserve the original brace token. /// This determines the generated fn's span end - pub(super) orig_brace_tokens: syn::token::Brace, + pub(super) brace_tokens: syn::token::Brace, } pub(super) struct StartFnParams { @@ -322,8 +322,8 @@ impl BuilderGenCtx { vis: start_fn.vis.unwrap_or_else(|| builder_type.vis.clone()), docs: start_fn.docs, generics: start_fn.generics, - orig_fn_token: start_fn.orig_fn_token.unwrap_or_default(), - orig_brace_tokens: start_fn.orig_brace_tokens.unwrap_or_default(), + fn_token: start_fn.orig_fn_token.unwrap_or_default(), + brace_tokens: start_fn.orig_brace_tokens.unwrap_or_default(), }; let finish_fn = FinishFn { diff --git a/bon-macros/src/builder/builder_gen/start_fn.rs b/bon-macros/src/builder/builder_gen/start_fn.rs index bdfb9061..606ec311 100644 --- a/bon-macros/src/builder/builder_gen/start_fn.rs +++ b/bon-macros/src/builder/builder_gen/start_fn.rs @@ -96,29 +96,28 @@ impl super::BuilderGenCtx { }; let const_ = &self.const_; - let fn_kw = self.start_fn.orig_fn_token; + let fn_kw = self.start_fn.fn_token; // construct function body separately so that we can reuse the original // function's braces and span. Reusing the span makes rustdoc link // our generated function to the original function rather than the // macro invocation site. - let body_contents: Vec = syn::parse_quote! { - #ide_hints - #( #start_fn_vars )* - #( #custom_fields_vars )* - - #builder_ident { - __unsafe_private_phantom: ::core::marker::PhantomData, - #( #custom_fields_idents, )* - #receiver_field_init - #( #start_fn_args_fields_idents, )* - __unsafe_private_named: #named_members_field_init, + let body_span = self.start_fn.brace_tokens.span; + let body_block = quote_spanned! {body_span=> + { + #ide_hints + #( #start_fn_vars )* + #( #custom_fields_vars )* + + #builder_ident { + __unsafe_private_phantom: ::core::marker::PhantomData, + #( #custom_fields_idents, )* + #receiver_field_init + #( #start_fn_args_fields_idents, )* + __unsafe_private_named: #named_members_field_init, + } } }; - let body_block = syn::Block { - brace_token: self.start_fn.orig_brace_tokens, - stmts: body_contents, - }; syn::parse_quote! { #(#docs)* From f9c13adf2ac0ef235748795ec2286a87b68e4713 Mon Sep 17 00:00:00 2001 From: eisverygoodletter Date: Thu, 2 Oct 2025 22:17:34 +1000 Subject: [PATCH 3/8] Add allow(..) annotations for some tests --- bon/tests/integration/builder/attr_bon.rs | 1 + bon/tests/integration/builder/attr_const.rs | 2 ++ bon/tests/integration/builder/attr_top_level_start_fn.rs | 3 +++ 3 files changed, 6 insertions(+) diff --git a/bon/tests/integration/builder/attr_bon.rs b/bon/tests/integration/builder/attr_bon.rs index 18045e5d..936be0ab 100644 --- a/bon/tests/integration/builder/attr_bon.rs +++ b/bon/tests/integration/builder/attr_bon.rs @@ -102,6 +102,7 @@ fn receiver_variations() { } #[builder] + #[allow(dead_code)] fn mut_self_as_pin_mut_self(mut self: Pin<&mut Self>) { #[allow(clippy::self_assignment, unused_assignments)] { diff --git a/bon/tests/integration/builder/attr_const.rs b/bon/tests/integration/builder/attr_const.rs index 260624dc..495eb33b 100644 --- a/bon/tests/integration/builder/attr_const.rs +++ b/bon/tests/integration/builder/attr_const.rs @@ -205,6 +205,7 @@ mod msrv_1_61 { #[test] const fn test_function() { #[builder(const)] + #[allow(unreachable_pub)] pub const fn sut(#[builder(getter)] _x: u32) {} sut().x(1).call(); @@ -217,6 +218,7 @@ mod msrv_1_61 { #[bon] impl Sut { #[builder(const)] + #[allow(unreachable_pub)] pub const fn sut(#[builder(getter)] _x: u32) {} } diff --git a/bon/tests/integration/builder/attr_top_level_start_fn.rs b/bon/tests/integration/builder/attr_top_level_start_fn.rs index 6782f8b8..28fd9d10 100644 --- a/bon/tests/integration/builder/attr_top_level_start_fn.rs +++ b/bon/tests/integration/builder/attr_top_level_start_fn.rs @@ -52,6 +52,7 @@ fn test_method() { fn test_function() { { #[builder(start_fn(name = sut_builder))] + #[allow(dead_code)] fn sut(arg1: bool, arg2: u32) -> impl fmt::Debug { (arg1, arg2) } @@ -61,6 +62,7 @@ fn test_function() { { #[builder(start_fn = sut_builder)] + #[allow(dead_code)] fn sut(arg1: u32) -> u32 { arg1 } @@ -70,6 +72,7 @@ fn test_function() { { #[builder(start_fn(name = sut_builder, vis = ""))] + #[allow(dead_code)] fn sut(arg1: u32) -> u32 { arg1 } From 4d66911145eaf9086838e20ac9cd9caa71521c8f Mon Sep 17 00:00:00 2001 From: eisverygoodletter Date: Thu, 2 Oct 2025 22:54:16 +1000 Subject: [PATCH 4/8] Rewrite using quote_macro_spanned and only preserve span --- .../src/builder/builder_gen/input_fn/mod.rs | 9 ++-- .../src/builder/builder_gen/input_struct.rs | 3 +- bon-macros/src/builder/builder_gen/models.rs | 18 +++----- .../src/builder/builder_gen/start_fn.rs | 43 ++++++++----------- 4 files changed, 30 insertions(+), 43 deletions(-) diff --git a/bon-macros/src/builder/builder_gen/input_fn/mod.rs b/bon-macros/src/builder/builder_gen/input_fn/mod.rs index 79b204c3..0df8292f 100644 --- a/bon-macros/src/builder/builder_gen/input_fn/mod.rs +++ b/bon-macros/src/builder/builder_gen/input_fn/mod.rs @@ -12,6 +12,7 @@ use crate::util::prelude::*; use std::borrow::Cow; use std::rc::Rc; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; use syn::visit_mut::VisitMut; pub(crate) struct FnInputCtx<'a> { @@ -97,9 +98,11 @@ impl<'a> FnInputCtx<'a> { .collect(), params.fn_item.norm.sig.generics.where_clause.clone(), )), - - orig_fn_token: Some(params.fn_item.orig.sig.fn_token), - orig_brace_tokens: Some(params.fn_item.orig.block.brace_token), + // We don't use params.fn_item.orig.span() here because that span + // only contains the annotations before the function and the + // function name. Block contains function name + implementation + // which is what rustdoc normally links to. + span: Some(params.fn_item.orig.block.span()), }; let self_ty_prefix = params.impl_ctx.as_deref().and_then(|impl_ctx| { diff --git a/bon-macros/src/builder/builder_gen/input_struct.rs b/bon-macros/src/builder/builder_gen/input_struct.rs index 830490a5..be8343fa 100644 --- a/bon-macros/src/builder/builder_gen/input_struct.rs +++ b/bon-macros/src/builder/builder_gen/input_struct.rs @@ -187,8 +187,7 @@ impl StructInputCtx { vis: start_fn_vis.map(SpannedKey::into_value), docs: start_fn_docs, generics: None, - orig_brace_tokens: None, - orig_fn_token: None, + span: None, }; let assoc_method_ctx = Some(AssocMethodCtxParams { diff --git a/bon-macros/src/builder/builder_gen/models.rs b/bon-macros/src/builder/builder_gen/models.rs index bc87e187..0741043c 100644 --- a/bon-macros/src/builder/builder_gen/models.rs +++ b/bon-macros/src/builder/builder_gen/models.rs @@ -86,12 +86,8 @@ pub(super) struct StartFn { /// Overrides the default generics pub(super) generics: Option, - /// Preserve the original fn token span. - /// This determines the generated fn's span start - pub(super) fn_token: syn::token::Fn, - /// Preserve the original brace token. - /// This determines the generated fn's span end - pub(super) brace_tokens: syn::token::Brace, + /// Preserve a span the documentation for `start_fn` should link to. + pub(super) span: Span, } pub(super) struct StartFnParams { @@ -104,12 +100,9 @@ pub(super) struct StartFnParams { /// Overrides the default generics pub(super) generics: Option, - /// Preserve original fn token for its span if possible. Defaults to - /// macro callsite if not supplied. - pub(super) orig_fn_token: Option, - /// Preserve original brace tokens `{}` for their spans if possible. + /// Preserve a span the documentation for `start_fn` should link to. /// Defaults to macro callsite if not supplied. - pub(super) orig_brace_tokens: Option, + pub(super) span: Option, } pub(super) struct BuilderType { @@ -322,8 +315,7 @@ impl BuilderGenCtx { vis: start_fn.vis.unwrap_or_else(|| builder_type.vis.clone()), docs: start_fn.docs, generics: start_fn.generics, - fn_token: start_fn.orig_fn_token.unwrap_or_default(), - brace_tokens: start_fn.orig_brace_tokens.unwrap_or_default(), + span: start_fn.span.unwrap_or_else(Span::call_site), }; let finish_fn = FinishFn { diff --git a/bon-macros/src/builder/builder_gen/start_fn.rs b/bon-macros/src/builder/builder_gen/start_fn.rs index 606ec311..0cb6d590 100644 --- a/bon-macros/src/builder/builder_gen/start_fn.rs +++ b/bon-macros/src/builder/builder_gen/start_fn.rs @@ -96,30 +96,11 @@ impl super::BuilderGenCtx { }; let const_ = &self.const_; - let fn_kw = self.start_fn.fn_token; - - // construct function body separately so that we can reuse the original - // function's braces and span. Reusing the span makes rustdoc link - // our generated function to the original function rather than the - // macro invocation site. - let body_span = self.start_fn.brace_tokens.span; - let body_block = quote_spanned! {body_span=> - { - #ide_hints - #( #start_fn_vars )* - #( #custom_fields_vars )* - - #builder_ident { - __unsafe_private_phantom: ::core::marker::PhantomData, - #( #custom_fields_idents, )* - #receiver_field_init - #( #start_fn_args_fields_idents, )* - __unsafe_private_named: #named_members_field_init, - } - } - }; - syn::parse_quote! { + // Construct using a span which links to our original implementation. + // This ensures rustdoc doesn't just link every method to the macro + // callsite. + syn::parse_quote_spanned! {self.start_fn.span=> #(#docs)* #[inline(always)] #[allow( @@ -131,12 +112,24 @@ impl super::BuilderGenCtx { // const operations. clippy::missing_const_for_fn, )] - #vis #const_ #fn_kw #start_fn_ident< #(#generics_decl),* >( + #vis #const_ fn #start_fn_ident< #(#generics_decl),* >( #receiver #(#start_fn_params,)* ) -> #builder_ident< #(#generic_args,)* > #where_clause - #body_block + { + #ide_hints + #( #start_fn_vars )* + #( #custom_fields_vars )* + + #builder_ident { + __unsafe_private_phantom: ::core::marker::PhantomData, + #( #custom_fields_idents, )* + #receiver_field_init + #( #start_fn_args_fields_idents, )* + __unsafe_private_named: #named_members_field_init, + } + } } } } From 6806bdcc167527818f0afa9e3355de5585828a10 Mon Sep 17 00:00:00 2001 From: eisverygoodletter Date: Fri, 3 Oct 2025 00:25:05 +1000 Subject: [PATCH 5/8] Generate allow(clippy::elidable_lifetime_names) for all start_fns --- bon-macros/src/builder/builder_gen/start_fn.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bon-macros/src/builder/builder_gen/start_fn.rs b/bon-macros/src/builder/builder_gen/start_fn.rs index 0cb6d590..2543615b 100644 --- a/bon-macros/src/builder/builder_gen/start_fn.rs +++ b/bon-macros/src/builder/builder_gen/start_fn.rs @@ -111,6 +111,11 @@ impl super::BuilderGenCtx { // Let's keep it as non-const for now to avoid restricting ourselfves to only // const operations. clippy::missing_const_for_fn, + // Hide generics with elidable lifetimes. Clippy may suggest + // eliding lifetimes even when it is wrong. See + // https://github.com/elastio/bon/pull/341#discussion_r2398893516 + // for an explanation. + clippy::elidable_lifetime_names, )] #vis #const_ fn #start_fn_ident< #(#generics_decl),* >( #receiver From 10f036f1ffcbfbad66cd6813fe8eff994594f709 Mon Sep 17 00:00:00 2001 From: eisverygoodletter Date: Fri, 3 Oct 2025 00:54:30 +1000 Subject: [PATCH 6/8] Generate allow(clippy::needless_lifetimes) to support older MSRV versions as well --- bon-macros/src/builder/builder_gen/start_fn.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bon-macros/src/builder/builder_gen/start_fn.rs b/bon-macros/src/builder/builder_gen/start_fn.rs index 2543615b..ff16779c 100644 --- a/bon-macros/src/builder/builder_gen/start_fn.rs +++ b/bon-macros/src/builder/builder_gen/start_fn.rs @@ -116,6 +116,7 @@ impl super::BuilderGenCtx { // https://github.com/elastio/bon/pull/341#discussion_r2398893516 // for an explanation. clippy::elidable_lifetime_names, + clippy::needless_lifetimes, )] #vis #const_ fn #start_fn_ident< #(#generics_decl),* >( #receiver From ed2c6961eb2698722d2f4432684d7b25f317d255 Mon Sep 17 00:00:00 2001 From: eisverygoodletter Date: Fri, 3 Oct 2025 01:10:42 +1000 Subject: [PATCH 7/8] Add conditional compilation on rust version so that we allow the correct clippy lint --- .../src/builder/builder_gen/start_fn.rs | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/bon-macros/src/builder/builder_gen/start_fn.rs b/bon-macros/src/builder/builder_gen/start_fn.rs index ff16779c..6a49880d 100644 --- a/bon-macros/src/builder/builder_gen/start_fn.rs +++ b/bon-macros/src/builder/builder_gen/start_fn.rs @@ -96,6 +96,26 @@ impl super::BuilderGenCtx { }; let const_ = &self.const_; + // add the `clippy::needless_lifetimes` lint if before rust version 1.87 + // Rust version 1.87 includes a clippy change where `needless_lifetimes` + // was split with the more complex part of the lint going to + // `elidable_lifetime_names`. For versions since 1.87 we want to block + // `elidable_lifetime_names` (See + // https://github.com/elastio/bon/pull/341#discussion_r2398893516 for + // an explanation). + #[rustversion::before(1.87)] + fn get_needless_lifetime_lint_annotation() -> TokenStream { + quote! { + #[allow(clippy::needless_lifetimes)] + } + } + #[rustversion::since(1.87)] + fn get_needless_lifetime_lint_annotation() -> TokenStream { + quote! { + #[allow(clippy::elidable_lifetime_names)] + } + } + let needless_lifetime_lint = get_needless_lifetime_lint_annotation(); // Construct using a span which links to our original implementation. // This ensures rustdoc doesn't just link every method to the macro @@ -111,13 +131,8 @@ impl super::BuilderGenCtx { // Let's keep it as non-const for now to avoid restricting ourselfves to only // const operations. clippy::missing_const_for_fn, - // Hide generics with elidable lifetimes. Clippy may suggest - // eliding lifetimes even when it is wrong. See - // https://github.com/elastio/bon/pull/341#discussion_r2398893516 - // for an explanation. - clippy::elidable_lifetime_names, - clippy::needless_lifetimes, )] + #needless_lifetime_lint #vis #const_ fn #start_fn_ident< #(#generics_decl),* >( #receiver #(#start_fn_params,)* From fe82c6d790495ad56b2a2862c960e4c91e58eac6 Mon Sep 17 00:00:00 2001 From: eisverygoodletter Date: Fri, 3 Oct 2025 14:45:31 +1000 Subject: [PATCH 8/8] Simplify conditional logic for allow annotation generation --- .../src/builder/builder_gen/start_fn.rs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/bon-macros/src/builder/builder_gen/start_fn.rs b/bon-macros/src/builder/builder_gen/start_fn.rs index 6a49880d..1a168ee8 100644 --- a/bon-macros/src/builder/builder_gen/start_fn.rs +++ b/bon-macros/src/builder/builder_gen/start_fn.rs @@ -103,19 +103,11 @@ impl super::BuilderGenCtx { // `elidable_lifetime_names` (See // https://github.com/elastio/bon/pull/341#discussion_r2398893516 for // an explanation). - #[rustversion::before(1.87)] - fn get_needless_lifetime_lint_annotation() -> TokenStream { - quote! { - #[allow(clippy::needless_lifetimes)] - } - } - #[rustversion::since(1.87)] - fn get_needless_lifetime_lint_annotation() -> TokenStream { - quote! { - #[allow(clippy::elidable_lifetime_names)] - } - } - let needless_lifetime_lint = get_needless_lifetime_lint_annotation(); + let needless_lifetime_lint = if rustversion::cfg!(before(1.87)) { + format_ident!("needless_lifetimes") + } else { + format_ident!("elidable_lifetime_names") + }; // Construct using a span which links to our original implementation. // This ensures rustdoc doesn't just link every method to the macro @@ -131,8 +123,8 @@ impl super::BuilderGenCtx { // Let's keep it as non-const for now to avoid restricting ourselfves to only // const operations. clippy::missing_const_for_fn, + clippy::#needless_lifetime_lint )] - #needless_lifetime_lint #vis #const_ fn #start_fn_ident< #(#generics_decl),* >( #receiver #(#start_fn_params,)*