@@ -10,9 +10,16 @@ use field::PropField;
1010use proc_macro2:: { Ident , Span } ;
1111use quote:: { format_ident, quote, ToTokens } ;
1212use syn:: parse:: { Parse , ParseStream , Result } ;
13- use syn:: { Attribute , DeriveInput , Generics , Visibility } ;
13+ use syn:: punctuated:: Pair ;
14+ use syn:: visit_mut:: VisitMut ;
15+ use syn:: {
16+ AngleBracketedGenericArguments , Attribute , ConstParam , DeriveInput , GenericArgument ,
17+ GenericParam , Generics , Path , PathArguments , PathSegment , Type , TypeParam , TypePath ,
18+ Visibility ,
19+ } ;
1420use wrapper:: PropsWrapper ;
1521
22+ use self :: field:: PropAttr ;
1623use self :: generics:: to_arguments;
1724
1825pub struct DerivePropsInput {
@@ -23,6 +30,76 @@ pub struct DerivePropsInput {
2330 preserved_attrs : Vec < Attribute > ,
2431}
2532
33+ /// AST visitor that replaces all occurences of the keyword `Self` with `new_self`
34+ struct Normaliser < ' ast > {
35+ new_self : & ' ast Ident ,
36+ generics : & ' ast Generics ,
37+ /// `Option` for one-time initialisation
38+ new_self_full : Option < PathSegment > ,
39+ }
40+
41+ impl < ' ast > Normaliser < ' ast > {
42+ pub fn new ( new_self : & ' ast Ident , generics : & ' ast Generics ) -> Self {
43+ Self {
44+ new_self,
45+ generics,
46+ new_self_full : None ,
47+ }
48+ }
49+
50+ fn get_new_self ( & mut self ) -> PathSegment {
51+ self . new_self_full
52+ . get_or_insert_with ( || {
53+ PathSegment {
54+ ident : self . new_self . clone ( ) ,
55+ arguments : if self . generics . lt_token . is_some ( ) {
56+ PathArguments :: AngleBracketed ( AngleBracketedGenericArguments {
57+ colon2_token : Some ( Default :: default ( ) ) ,
58+ lt_token : Default :: default ( ) ,
59+ args : self
60+ . generics
61+ . params
62+ . pairs ( )
63+ . map ( |pair| {
64+ let ( value, punct) = pair. cloned ( ) . into_tuple ( ) ;
65+ let value = match value {
66+ GenericParam :: Lifetime ( param) => {
67+ GenericArgument :: Lifetime ( param. lifetime )
68+ }
69+ GenericParam :: Type ( TypeParam { ident, .. } )
70+ | GenericParam :: Const ( ConstParam { ident, .. } ) => {
71+ GenericArgument :: Type ( Type :: Path ( TypePath {
72+ qself : None ,
73+ path : ident. into ( ) ,
74+ } ) )
75+ }
76+ } ;
77+ Pair :: new ( value, punct)
78+ } )
79+ . collect ( ) ,
80+ gt_token : Default :: default ( ) ,
81+ } )
82+ } else {
83+ // if no generics were defined for the struct
84+ PathArguments :: None
85+ } ,
86+ }
87+ } )
88+ . clone ( )
89+ }
90+ }
91+
92+ impl VisitMut for Normaliser < ' _ > {
93+ fn visit_path_mut ( & mut self , path : & mut Path ) {
94+ if let Some ( first) = path. segments . first_mut ( ) {
95+ if first. ident == "Self" {
96+ * first = self . get_new_self ( ) ;
97+ }
98+ syn:: visit_mut:: visit_path_mut ( self , path)
99+ }
100+ }
101+ }
102+
26103/// Some attributes on the original struct are to be preserved and added to the builder struct,
27104/// in order to avoid warnings (sometimes reported as errors) in the output.
28105fn should_preserve_attr ( attr : & Attribute ) -> bool {
@@ -74,22 +151,33 @@ impl Parse for DerivePropsInput {
74151 }
75152}
76153
154+ impl DerivePropsInput {
155+ /// Replaces all occurences of `Self` in the struct with the actual name of the struct.
156+ /// Must be called before tokenising the struct.
157+ pub fn normalise ( & mut self ) {
158+ let mut normaliser = Normaliser :: new ( & self . props_name , & self . generics ) ;
159+ for field in & mut self . prop_fields {
160+ normaliser. visit_type_mut ( & mut field. ty ) ;
161+ if let PropAttr :: PropOr ( expr) | PropAttr :: PropOrElse ( expr) = & mut field. attr {
162+ normaliser. visit_expr_mut ( expr)
163+ }
164+ }
165+ }
166+ }
167+
77168impl ToTokens for DerivePropsInput {
78169 fn to_tokens ( & self , tokens : & mut proc_macro2:: TokenStream ) {
79170 let Self {
80171 generics,
81172 props_name,
173+ prop_fields,
174+ preserved_attrs,
82175 ..
83176 } = self ;
84177
85178 // The wrapper is a new struct which wraps required props in `Option`
86179 let wrapper_name = format_ident ! ( "{}Wrapper" , props_name, span = Span :: mixed_site( ) ) ;
87- let wrapper = PropsWrapper :: new (
88- & wrapper_name,
89- generics,
90- & self . prop_fields ,
91- & self . preserved_attrs ,
92- ) ;
180+ let wrapper = PropsWrapper :: new ( & wrapper_name, generics, prop_fields, preserved_attrs) ;
93181 tokens. extend ( wrapper. into_token_stream ( ) ) ;
94182
95183 // The builder will only build if all required props have been set
@@ -101,7 +189,7 @@ impl ToTokens for DerivePropsInput {
101189 self ,
102190 & wrapper_name,
103191 & check_all_props_name,
104- & self . preserved_attrs ,
192+ preserved_attrs,
105193 ) ;
106194 let generic_args = to_arguments ( generics) ;
107195 tokens. extend ( builder. into_token_stream ( ) ) ;
0 commit comments