11use super :: models:: BuilderGenCtx ;
22use crate :: parsing:: ItemSigConfig ;
33use crate :: util:: prelude:: * ;
4+ use std:: collections:: BTreeSet ;
45use syn:: punctuated:: Punctuated ;
56use syn:: token:: Where ;
7+ use syn:: visit:: Visit ;
68
79pub ( super ) struct GenericSettersCtx < ' a > {
810 base : & ' a BuilderGenCtx ,
@@ -28,19 +30,24 @@ impl<'a> GenericSettersCtx<'a> {
2830 // Check for interdependent type parameters in generic bounds
2931 for param in generics {
3032 if let syn:: GenericParam :: Type ( type_param) = param {
31- let params_in_bounds =
32- find_type_params_in_bounds ( & type_param. bounds , & type_param_idents) ;
33- if params_in_bounds. len ( ) > 1
34- || ( params_in_bounds. len ( ) == 1
35- && params_in_bounds. first ( ) != Some ( & & type_param. ident ) )
36- {
37- let params_str = params_in_bounds
33+ let mut params = TypeParamFinder :: new ( & type_param_idents) ;
34+
35+ for bound in & type_param. bounds {
36+ params. visit_type_param_bound ( bound) ;
37+ }
38+
39+ // Self-referential type params are fine
40+ params. found . remove ( & type_param. ident ) ;
41+
42+ if let Some ( first_param) = params. found . iter ( ) . next ( ) {
43+ let params_str = params
44+ . found
3845 . iter ( )
3946 . map ( |p| format ! ( "`{p}`" ) )
4047 . collect :: < Vec < _ > > ( )
4148 . join ( ", " ) ;
4249 bail ! (
43- & type_param . bounds ,
50+ first_param ,
4451 "generic conversion methods cannot be generated for interdependent type parameters; \
4552 the bounds on generic parameter `{}` reference other type parameters: {}\n \
4653 \n \
@@ -55,10 +62,11 @@ impl<'a> GenericSettersCtx<'a> {
5562 // Check for interdependent type parameters in where clauses
5663 if let Some ( where_clause) = & self . base . generics . where_clause {
5764 for predicate in & where_clause. predicates {
58- let params_in_predicate =
59- find_type_params_in_predicate ( predicate, & type_param_idents) ;
60- if params_in_predicate. len ( ) > 1 {
61- let params_str = params_in_predicate
65+ let mut params = TypeParamFinder :: new ( & type_param_idents) ;
66+ params. visit_where_predicate ( predicate) ;
67+ if params. found . len ( ) > 1 {
68+ let params_str = params
69+ . found
6270 . iter ( )
6371 . map ( |p| format ! ( "`{p}`" ) )
6472 . collect :: < Vec < _ > > ( )
@@ -85,8 +93,9 @@ impl<'a> GenericSettersCtx<'a> {
8593 syn:: GenericParam :: Const ( const_param) => {
8694 bail ! (
8795 & const_param. ident,
88- "const generic parameters are not supported in `generics(setters(...))`; \
89- only type parameters can be converted"
96+ "const generic parameters are not yet supported with `generics(setters(...))`; \
97+ only type parameters can be overridden, feel free to open an issue if you need \
98+ this feature"
9099 ) ;
91100 }
92101 syn:: GenericParam :: Lifetime ( _) => {
@@ -122,8 +131,12 @@ impl<'a> GenericSettersCtx<'a> {
122131 let docs = self . method_docs ( param_ident) ;
123132
124133 // Build the generic arguments for the output type, where the current parameter
125- // is replaced with a new type variable
126- let new_type_var = self . base . namespace . unique_ident ( param_ident. to_string ( ) ) ;
134+ // is replaced with a new type variable. Even though the `GenericsNamespace`
135+ let new_type_var = self
136+ . base
137+ . namespace
138+ // Add `New` prefix to make the type variable more readable in the docs and IDE hints
139+ . unique_ident ( format ! ( "New{param_ident}" ) ) ;
127140
128141 // Copy the bounds from the original type parameter to the new one
129142 let bounds = & type_param. bounds ;
@@ -168,7 +181,10 @@ impl<'a> GenericSettersCtx<'a> {
168181
169182 // Add runtime assert that this field is None
170183 let field_ident = & member. name . orig ;
171- let message = format ! ( "BUG: field `{field_ident}` should be None when converting generic parameter `{param_ident}`" ) ;
184+ let message = format ! (
185+ "BUG: field `{field_ident}` should be None \
186+ when converting generic parameter `{param_ident}`"
187+ ) ;
172188 runtime_asserts. push ( quote ! {
173189 :: core:: assert!( named. #index. is_none( ) , #message) ;
174190 } ) ;
@@ -211,11 +227,7 @@ impl<'a> GenericSettersCtx<'a> {
211227 clause. predicates . push ( syn:: parse_quote!( #bound) ) ;
212228 }
213229
214- if clause. predicates . is_empty ( ) {
215- None
216- } else {
217- Some ( clause)
218- }
230+ ( !clause. predicates . is_empty ( ) ) . then ( || clause)
219231 } ;
220232
221233 quote ! {
@@ -280,82 +292,34 @@ impl<'a> GenericSettersCtx<'a> {
280292 }
281293}
282294
283- fn find_type_params_in_bounds < ' b > (
284- bounds : & Punctuated < syn:: TypeParamBound , syn:: token:: Plus > ,
285- type_params : & ' b [ & ' b syn:: Ident ] ,
286- ) -> Vec < & ' b syn:: Ident > {
287- use syn:: visit:: Visit ;
295+ struct TypeParamFinder < ' ty , ' ast > {
296+ type_params : & ' ty [ & ' ty syn:: Ident ] ,
288297
289- struct TypeParamFinder < ' a > {
290- type_params : & ' a [ & ' a syn:: Ident ] ,
291- found : std:: collections:: HashSet < & ' a syn:: Ident > ,
292- }
298+ // Use a `BTreeSet` for deterministic ordering
299+ found : BTreeSet < & ' ast syn:: Ident > ,
300+ }
293301
294- impl < ' ast > Visit < ' ast > for TypeParamFinder < ' _ > {
295- fn visit_path ( & mut self , path : & ' ast syn:: Path ) {
296- // Check if this path is one of our type parameters
297- for & param in self . type_params {
298- if path. is_ident ( param) {
299- self . found . insert ( param) ;
300- }
301- }
302- // Continue visiting nested paths
303- syn:: visit:: visit_path ( self , path) ;
302+ impl < ' ty > TypeParamFinder < ' ty , ' _ > {
303+ fn new ( type_params : & ' ty [ & ' ty syn:: Ident ] ) -> Self {
304+ Self {
305+ type_params,
306+ found : BTreeSet :: new ( ) ,
304307 }
305308 }
306-
307- let mut finder = TypeParamFinder {
308- type_params,
309- found : std:: collections:: HashSet :: new ( ) ,
310- } ;
311-
312- for bound in bounds {
313- finder. visit_type_param_bound ( bound) ;
314- }
315-
316- // Preserve the original order of type parameters for deterministic output
317- type_params
318- . iter ( )
319- . filter ( |param| finder. found . contains ( * param) )
320- . copied ( )
321- . collect ( )
322309}
323310
324- fn find_type_params_in_predicate < ' b > (
325- predicate : & syn:: WherePredicate ,
326- type_params : & ' b [ & ' b syn:: Ident ] ,
327- ) -> Vec < & ' b syn:: Ident > {
328- use syn:: visit:: Visit ;
329-
330- struct TypeParamFinder < ' a > {
331- type_params : & ' a [ & ' a syn:: Ident ] ,
332- found : std:: collections:: HashSet < & ' a syn:: Ident > ,
333- }
334-
335- impl < ' ast > Visit < ' ast > for TypeParamFinder < ' _ > {
336- fn visit_path ( & mut self , path : & ' ast syn:: Path ) {
337- // Check if this path is one of our type parameters
338- for & param in self . type_params {
339- if path. is_ident ( param) {
340- self . found . insert ( param) ;
341- }
311+ impl < ' ast > Visit < ' ast > for TypeParamFinder < ' _ , ' ast > {
312+ fn visit_path ( & mut self , path : & ' ast syn:: Path ) {
313+ // Check if this path is one of our type parameters
314+ if let Some ( param) = path. get_ident ( ) {
315+ if self . type_params . contains ( & param) {
316+ self . found . insert ( param) ;
342317 }
343- // Continue visiting nested paths
344- syn:: visit:: visit_path ( self , path) ;
345318 }
346- }
347319
348- let mut finder = TypeParamFinder {
349- type_params,
350- found : std:: collections:: HashSet :: new ( ) ,
351- } ;
352- finder. visit_where_predicate ( predicate) ;
353- // Preserve the original order of type parameters for deterministic output
354- type_params
355- . iter ( )
356- . filter ( |param| finder. found . contains ( * param) )
357- . copied ( )
358- . collect ( )
320+ // Continue visiting nested paths
321+ syn:: visit:: visit_path ( self , path) ;
322+ }
359323}
360324
361325fn replace_type_param_in_predicate (
@@ -406,8 +370,6 @@ fn member_uses_generic_param(member: &super::NamedMember, param_ident: &syn::Ide
406370
407371/// Recursively check if a type uses a specific generic parameter
408372fn type_uses_generic_param ( ty : & syn:: Type , param_ident : & syn:: Ident ) -> bool {
409- use syn:: visit:: Visit ;
410-
411373 struct GenericParamVisitor < ' a > {
412374 param_ident : & ' a syn:: Ident ,
413375 found : bool ,
0 commit comments