@@ -66,6 +66,7 @@ impl FromStr for AttrName {
66
66
struct ImplContext {
67
67
impl_extend_items : ItemNursery ,
68
68
getset_items : GetSetNursery ,
69
+ member_items : MemberNursery ,
69
70
extend_slots_items : ItemNursery ,
70
71
class_extensions : Vec < TokenStream > ,
71
72
errors : Vec < syn:: Error > ,
@@ -94,6 +95,7 @@ fn extract_items_into_context<'a, Item>(
94
95
context. errors . ok_or_push ( r) ;
95
96
}
96
97
context. errors . ok_or_push ( context. getset_items . validate ( ) ) ;
98
+ context. errors . ok_or_push ( context. member_items . validate ( ) ) ;
97
99
}
98
100
99
101
pub ( crate ) fn impl_pyimpl ( attr : AttributeArgs , item : Item ) -> Result < TokenStream > {
@@ -110,6 +112,7 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
110
112
} = extract_impl_attrs ( attr, & Ident :: new ( & quote ! ( ty) . to_string ( ) , ty. span ( ) ) ) ?;
111
113
112
114
let getset_impl = & context. getset_items ;
115
+ let member_impl = & context. member_items ;
113
116
let extend_impl = context. impl_extend_items . validate ( ) ?;
114
117
let slots_impl = context. extend_slots_items . validate ( ) ?;
115
118
let class_extensions = & context. class_extensions ;
@@ -123,6 +126,7 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
123
126
class: & ' static :: rustpython_vm:: Py <:: rustpython_vm:: builtins:: PyType >,
124
127
) {
125
128
#getset_impl
129
+ #member_impl
126
130
#extend_impl
127
131
#with_impl
128
132
#( #class_extensions) *
@@ -146,6 +150,7 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
146
150
} = extract_impl_attrs ( attr, & trai. ident ) ?;
147
151
148
152
let getset_impl = & context. getset_items ;
153
+ let member_impl = & context. member_items ;
149
154
let extend_impl = & context. impl_extend_items . validate ( ) ?;
150
155
let slots_impl = & context. extend_slots_items . validate ( ) ?;
151
156
let class_extensions = & context. class_extensions ;
@@ -156,6 +161,7 @@ pub(crate) fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream
156
161
class: & ' static :: rustpython_vm:: Py <:: rustpython_vm:: builtins:: PyType >,
157
162
) {
158
163
#getset_impl
164
+ #member_impl
159
165
#extend_impl
160
166
#with_impl
161
167
#( #class_extensions) *
@@ -689,23 +695,20 @@ where
689
695
let item_attr = args. attrs . remove ( self . index ( ) ) ;
690
696
let item_meta = MemberItemMeta :: from_attr ( ident. clone ( ) , & item_attr) ?;
691
697
692
- let py_name = item_meta. member_name ( ) ?;
693
- let tokens = {
694
- quote_spanned ! { ident. span( ) =>
695
- class. set_str_attr(
696
- #py_name,
697
- ctx. new_member( #py_name, Self :: #ident, None , class) ,
698
- ctx,
699
- ) ;
700
- }
698
+ let ( py_name, member_item_kind) = item_meta. member_name ( ) ?;
699
+ let member_kind = match item_meta. member_kind ( ) ? {
700
+ Some ( s) => match s. as_str ( ) {
701
+ "bool" => MemberKind :: Bool ,
702
+ _ => unreachable ! ( ) ,
703
+ } ,
704
+ _ => MemberKind :: ObjectEx ,
701
705
} ;
702
706
703
- args. context . impl_extend_items . add_item (
707
+ args. context . member_items . add_item (
708
+ py_name,
709
+ member_item_kind,
710
+ member_kind,
704
711
ident. clone ( ) ,
705
- vec ! [ py_name] ,
706
- args. cfgs . to_vec ( ) ,
707
- tokens,
708
- 5 ,
709
712
) ?;
710
713
Ok ( ( ) )
711
714
}
@@ -805,6 +808,95 @@ impl ToTokens for GetSetNursery {
805
808
}
806
809
}
807
810
811
+ #[ derive( Default ) ]
812
+ #[ allow( clippy:: type_complexity) ]
813
+ struct MemberNursery {
814
+ map : HashMap < ( String , MemberKind ) , ( Option < Ident > , Option < Ident > ) > ,
815
+ validated : bool ,
816
+ }
817
+
818
+ enum MemberItemKind {
819
+ Get ,
820
+ Set ,
821
+ }
822
+
823
+ #[ derive( Eq , PartialEq , Hash ) ]
824
+ enum MemberKind {
825
+ Bool ,
826
+ ObjectEx ,
827
+ }
828
+
829
+ impl MemberNursery {
830
+ fn add_item (
831
+ & mut self ,
832
+ name : String ,
833
+ kind : MemberItemKind ,
834
+ member_kind : MemberKind ,
835
+ item_ident : Ident ,
836
+ ) -> Result < ( ) > {
837
+ assert ! ( !self . validated, "new item is not allowed after validation" ) ;
838
+ let entry = self . map . entry ( ( name. clone ( ) , member_kind) ) . or_default ( ) ;
839
+ let func = match kind {
840
+ MemberItemKind :: Get => & mut entry. 0 ,
841
+ MemberItemKind :: Set => & mut entry. 1 ,
842
+ } ;
843
+ if func. is_some ( ) {
844
+ return Err ( syn:: Error :: new_spanned (
845
+ item_ident,
846
+ format ! ( "Multiple member accessors with name '{}'" , name) ,
847
+ ) ) ;
848
+ }
849
+ * func = Some ( item_ident) ;
850
+ Ok ( ( ) )
851
+ }
852
+
853
+ fn validate ( & mut self ) -> Result < ( ) > {
854
+ let mut errors = Vec :: new ( ) ;
855
+ for ( ( name, _) , ( getter, setter) ) in & self . map {
856
+ if getter. is_none ( ) {
857
+ errors. push ( syn:: Error :: new_spanned (
858
+ setter. as_ref ( ) . unwrap ( ) ,
859
+ format ! ( "Member '{}' is missing a getter" , name) ,
860
+ ) ) ;
861
+ } ;
862
+ }
863
+ errors. into_result ( ) ?;
864
+ self . validated = true ;
865
+ Ok ( ( ) )
866
+ }
867
+ }
868
+
869
+ impl ToTokens for MemberNursery {
870
+ fn to_tokens ( & self , tokens : & mut TokenStream ) {
871
+ assert ! ( self . validated, "Call `validate()` before token generation" ) ;
872
+ let properties = self
873
+ . map
874
+ . iter ( )
875
+ . map ( |( ( name, member_kind) , ( getter, setter) ) | {
876
+ let setter = match setter {
877
+ Some ( setter) => quote_spanned ! { setter. span( ) => Some ( Self :: #setter) } ,
878
+ None => quote ! { None } ,
879
+ } ;
880
+ let member_kind = match member_kind {
881
+ MemberKind :: Bool => {
882
+ quote ! ( :: rustpython_vm:: builtins:: descriptor:: MemberKind :: Bool )
883
+ }
884
+ MemberKind :: ObjectEx => {
885
+ quote ! ( :: rustpython_vm:: builtins:: descriptor:: MemberKind :: ObjectEx )
886
+ }
887
+ } ;
888
+ quote_spanned ! { getter. span( ) =>
889
+ class. set_str_attr(
890
+ #name,
891
+ ctx. new_member( #name, #member_kind, Self :: #getter, #setter, class) ,
892
+ ctx,
893
+ ) ;
894
+ }
895
+ } ) ;
896
+ tokens. extend ( properties) ;
897
+ }
898
+ }
899
+
808
900
struct MethodItemMeta ( ItemMetaInner ) ;
809
901
810
902
impl ItemMeta for MethodItemMeta {
@@ -983,7 +1075,7 @@ impl SlotItemMeta {
983
1075
struct MemberItemMeta ( ItemMetaInner ) ;
984
1076
985
1077
impl ItemMeta for MemberItemMeta {
986
- const ALLOWED_NAMES : & ' static [ & ' static str ] = & [ "magic" ] ;
1078
+ const ALLOWED_NAMES : & ' static [ & ' static str ] = & [ "magic" , "type" , "setter" ] ;
987
1079
988
1080
fn from_inner ( inner : ItemMetaInner ) -> Self {
989
1081
Self ( inner)
@@ -994,11 +1086,52 @@ impl ItemMeta for MemberItemMeta {
994
1086
}
995
1087
996
1088
impl MemberItemMeta {
997
- fn member_name ( & self ) -> Result < String > {
1089
+ fn member_name ( & self ) -> Result < ( String , MemberItemKind ) > {
998
1090
let inner = self . inner ( ) ;
1091
+ let sig_name = inner. item_name ( ) ;
1092
+ let extract_prefix_name = |prefix, item_typ| {
1093
+ if let Some ( name) = sig_name. strip_prefix ( prefix) {
1094
+ if name. is_empty ( ) {
1095
+ Err ( syn:: Error :: new_spanned (
1096
+ & inner. meta_ident ,
1097
+ format ! (
1098
+ "A #[{}({typ})] fn with a {prefix}* name must \
1099
+ have something after \" {prefix}\" ",
1100
+ inner. meta_name( ) ,
1101
+ typ = item_typ,
1102
+ prefix = prefix
1103
+ ) ,
1104
+ ) )
1105
+ } else {
1106
+ Ok ( name. to_owned ( ) )
1107
+ }
1108
+ } else {
1109
+ Err ( syn:: Error :: new_spanned (
1110
+ & inner. meta_ident ,
1111
+ format ! (
1112
+ "A #[{}(setter)] fn must either have a `name` \
1113
+ parameter or a fn name along the lines of \" set_*\" ",
1114
+ inner. meta_name( )
1115
+ ) ,
1116
+ ) )
1117
+ }
1118
+ } ;
999
1119
let magic = inner. _bool ( "magic" ) ?;
1000
- let name = inner. item_name ( ) ;
1001
- Ok ( if magic { format ! ( "__{}__" , name) } else { name } )
1120
+ let kind = if inner. _bool ( "setter" ) ? {
1121
+ MemberItemKind :: Set
1122
+ } else {
1123
+ MemberItemKind :: Get
1124
+ } ;
1125
+ let name = match kind {
1126
+ MemberItemKind :: Get => sig_name,
1127
+ MemberItemKind :: Set => extract_prefix_name ( "set_" , "setter" ) ?,
1128
+ } ;
1129
+ Ok ( ( if magic { format ! ( "__{}__" , name) } else { name } , kind) )
1130
+ }
1131
+
1132
+ fn member_kind ( & self ) -> Result < Option < String > > {
1133
+ let inner = self . inner ( ) ;
1134
+ inner. _optional_str ( "type" )
1002
1135
}
1003
1136
}
1004
1137
0 commit comments