@@ -8,6 +8,7 @@ use quote::ToTokens;
8
8
use quote:: { quote, quote_spanned} ;
9
9
use std:: ops:: Deref ;
10
10
use syn:: ext:: IdentExt ;
11
+ use syn:: punctuated:: Punctuated ;
11
12
use syn:: spanned:: Spanned ;
12
13
13
14
#[ derive( Clone , PartialEq , Debug ) ]
@@ -106,7 +107,7 @@ pub fn get_return_info(output: &syn::ReturnType) -> syn::Type {
106
107
}
107
108
}
108
109
109
- pub fn parse_method_receiver ( arg : & mut syn:: FnArg ) -> syn:: Result < SelfType > {
110
+ pub fn parse_method_receiver ( arg : & syn:: FnArg ) -> syn:: Result < SelfType > {
110
111
match arg {
111
112
syn:: FnArg :: Receiver ( recv) => Ok ( SelfType :: Receiver {
112
113
mutable : recv. mutability . is_some ( ) ,
@@ -117,34 +118,127 @@ pub fn parse_method_receiver(arg: &mut syn::FnArg) -> syn::Result<SelfType> {
117
118
118
119
impl < ' a > FnSpec < ' a > {
119
120
/// Parser function signature and function attributes
120
- #[ allow( clippy:: manual_strip) ] // for strip_prefix replacement supporting rust < 1.45
121
121
pub fn parse (
122
122
sig : & ' a mut syn:: Signature ,
123
123
meth_attrs : & mut Vec < syn:: Attribute > ,
124
124
allow_custom_name : bool ,
125
125
) -> syn:: Result < FnSpec < ' a > > {
126
- let name = & sig. ident ;
127
126
let MethodAttributes {
128
127
ty : fn_type_attr,
129
128
args : fn_attrs,
130
129
mut python_name,
131
130
} = parse_method_attributes ( meth_attrs, allow_custom_name) ?;
132
131
133
- let mut arguments = Vec :: new ( ) ;
132
+ let ( fn_type, skip_args) = Self :: parse_fn_type ( sig, fn_type_attr, & mut python_name) ?;
133
+
134
+ let name = & sig. ident ;
135
+ let ty = get_return_info ( & sig. output ) ;
136
+ let python_name = python_name. as_ref ( ) . unwrap_or ( name) . unraw ( ) ;
137
+
138
+ let text_signature = Self :: parse_text_signature ( meth_attrs, & fn_type, & python_name) ?;
139
+ let doc = utils:: get_doc ( & meth_attrs, text_signature, true ) ?;
140
+
141
+ let arguments = Self :: parse_arguments ( & mut sig. inputs , skip_args) ?;
142
+
143
+ Ok ( FnSpec {
144
+ tp : fn_type,
145
+ name,
146
+ python_name,
147
+ attrs : fn_attrs,
148
+ args : arguments,
149
+ output : ty,
150
+ doc,
151
+ } )
152
+ }
153
+
154
+ fn parse_text_signature (
155
+ meth_attrs : & mut Vec < syn:: Attribute > ,
156
+ fn_type : & FnType ,
157
+ python_name : & syn:: Ident ,
158
+ ) -> syn:: Result < Option < syn:: LitStr > > {
159
+ let mut parse_erroneous_text_signature = |error_msg : & str | {
160
+ // try to parse anyway to give better error messages
161
+ if let Some ( text_signature) =
162
+ utils:: parse_text_signature_attrs ( meth_attrs, & python_name) ?
163
+ {
164
+ bail_spanned ! ( text_signature. span( ) => error_msg)
165
+ } else {
166
+ Ok ( None )
167
+ }
168
+ } ;
169
+
170
+ let text_signature = match & fn_type {
171
+ FnType :: Fn ( _) | FnType :: FnClass | FnType :: FnStatic => {
172
+ utils:: parse_text_signature_attrs ( & mut * meth_attrs, & python_name) ?
173
+ }
174
+ FnType :: FnNew => parse_erroneous_text_signature (
175
+ "text_signature not allowed on __new__; if you want to add a signature on \
176
+ __new__, put it on the struct definition instead",
177
+ ) ?,
178
+ FnType :: FnCall ( _) | FnType :: Getter ( _) | FnType :: Setter ( _) | FnType :: ClassAttribute => {
179
+ parse_erroneous_text_signature ( "text_signature not allowed with this method type" ) ?
180
+ }
181
+ } ;
182
+
183
+ Ok ( text_signature)
184
+ }
185
+
186
+ fn parse_arguments (
187
+ // inputs: &'a mut [syn::FnArg],
188
+ inputs : & ' a mut Punctuated < syn:: FnArg , syn:: Token ![ , ] > ,
189
+ skip_args : usize ,
190
+ ) -> syn:: Result < Vec < FnArg < ' a > > > {
191
+ let mut arguments = vec ! [ ] ;
192
+ for input in inputs. iter_mut ( ) . skip ( skip_args) {
193
+ match input {
194
+ syn:: FnArg :: Receiver ( recv) => {
195
+ bail_spanned ! ( recv. span( ) => "unexpected receiver for method" )
196
+ } // checked in parse_fn_type
197
+ syn:: FnArg :: Typed ( cap) => {
198
+ let arg_attrs = PyFunctionArgAttrs :: from_attrs ( & mut cap. attrs ) ?;
199
+ let ( ident, by_ref, mutability) = match * cap. pat {
200
+ syn:: Pat :: Ident ( syn:: PatIdent {
201
+ ref ident,
202
+ ref by_ref,
203
+ ref mutability,
204
+ ..
205
+ } ) => ( ident, by_ref, mutability) ,
206
+ _ => bail_spanned ! ( cap. pat. span( ) => "unsupported argument" ) ,
207
+ } ;
208
+
209
+ arguments. push ( FnArg {
210
+ name : ident,
211
+ by_ref,
212
+ mutability,
213
+ ty : cap. ty . deref ( ) ,
214
+ optional : utils:: option_type_argument ( cap. ty . deref ( ) ) ,
215
+ py : utils:: is_python ( cap. ty . deref ( ) ) ,
216
+ attrs : arg_attrs,
217
+ } ) ;
218
+ }
219
+ }
220
+ }
134
221
135
- // TODO: maybe there's a cleaner solution
136
- let inputs_empty = sig. inputs . is_empty ( ) ;
137
- let sig_span = sig. span ( ) ;
138
- let inputs_span = sig. inputs . span ( ) ;
222
+ Ok ( arguments)
223
+ }
139
224
140
- let mut inputs_iter = sig. inputs . iter_mut ( ) ;
225
+ fn parse_fn_type (
226
+ sig : & syn:: Signature ,
227
+ fn_type_attr : Option < MethodTypeAttribute > ,
228
+ python_name : & mut Option < syn:: Ident > ,
229
+ ) -> syn:: Result < ( FnType , usize ) > {
230
+ let name = & sig. ident ;
231
+ let mut inputs_iter = sig. inputs . iter ( ) . enumerate ( ) ;
232
+ let inputs_len = sig. inputs . len ( ) ;
141
233
let mut parse_receiver = |msg : & ' static str | {
142
234
inputs_iter
143
235
. next ( )
144
- . ok_or_else ( || err_spanned ! ( sig_span => msg) )
236
+ . ok_or_else ( || err_spanned ! ( sig. span( ) => msg) )
237
+ . map ( |( _, arg) | arg)
145
238
. and_then ( parse_method_receiver)
146
239
} ;
147
240
241
+ #[ allow( clippy:: manual_strip) ] // for strip_prefix replacement supporting rust < 1.45
148
242
// strip get_ or set_
149
243
let strip_fn_name = |prefix : & ' static str | {
150
244
let ident = name. unraw ( ) . to_string ( ) ;
@@ -155,13 +249,12 @@ impl<'a> FnSpec<'a> {
155
249
}
156
250
} ;
157
251
158
- // Parse receiver & function type for various method types
159
252
let fn_type = match fn_type_attr {
160
253
Some ( MethodTypeAttribute :: StaticMethod ) => FnType :: FnStatic ,
161
254
Some ( MethodTypeAttribute :: ClassAttribute ) => {
162
255
ensure_spanned ! (
163
- inputs_empty ,
164
- inputs_span => "class attribute methods cannot take arguments"
256
+ sig . inputs . is_empty ( ) ,
257
+ sig . inputs . span ( ) => "class attribute methods cannot take arguments"
165
258
) ;
166
259
FnType :: ClassAttribute
167
260
}
@@ -177,15 +270,15 @@ impl<'a> FnSpec<'a> {
177
270
Some ( MethodTypeAttribute :: Getter ) => {
178
271
// Strip off "get_" prefix if needed
179
272
if python_name. is_none ( ) {
180
- python_name = strip_fn_name ( "get_" ) ;
273
+ * python_name = strip_fn_name ( "get_" ) ;
181
274
}
182
275
183
276
FnType :: Getter ( parse_receiver ( "expected receiver for #[getter]" ) ?)
184
277
}
185
278
Some ( MethodTypeAttribute :: Setter ) => {
186
279
// Strip off "set_" prefix if needed
187
280
if python_name. is_none ( ) {
188
- python_name = strip_fn_name ( "set_" ) ;
281
+ * python_name = strip_fn_name ( "set_" ) ;
189
282
}
190
283
191
284
FnType :: Setter ( parse_receiver ( "expected receiver for #[setter]" ) ?)
@@ -194,76 +287,10 @@ impl<'a> FnSpec<'a> {
194
287
"static method needs #[staticmethod] attribute" ,
195
288
) ?) ,
196
289
} ;
197
-
198
- // parse rest of arguments
199
- for input in inputs_iter {
200
- match input {
201
- syn:: FnArg :: Receiver ( recv) => {
202
- bail_spanned ! ( recv. span( ) => "unexpected receiver for method" )
203
- }
204
- syn:: FnArg :: Typed ( cap) => {
205
- let arg_attrs = PyFunctionArgAttrs :: from_attrs ( & mut cap. attrs ) ?;
206
- let ( ident, by_ref, mutability) = match * cap. pat {
207
- syn:: Pat :: Ident ( syn:: PatIdent {
208
- ref ident,
209
- ref by_ref,
210
- ref mutability,
211
- ..
212
- } ) => ( ident, by_ref, mutability) ,
213
- _ => bail_spanned ! ( cap. pat. span( ) => "unsupported argument" ) ,
214
- } ;
215
-
216
- arguments. push ( FnArg {
217
- name : ident,
218
- by_ref,
219
- mutability,
220
- ty : cap. ty . deref ( ) ,
221
- optional : utils:: option_type_argument ( cap. ty . deref ( ) ) ,
222
- py : utils:: is_python ( cap. ty . deref ( ) ) ,
223
- attrs : arg_attrs,
224
- } ) ;
225
- }
226
- }
227
- }
228
-
229
- let ty = get_return_info ( & sig. output ) ;
230
- let python_name = python_name. as_ref ( ) . unwrap_or ( name) . unraw ( ) ;
231
-
232
- let mut parse_erroneous_text_signature = |error_msg : & str | {
233
- // try to parse anyway to give better error messages
234
- if let Some ( text_signature) =
235
- utils:: parse_text_signature_attrs ( meth_attrs, & python_name) ?
236
- {
237
- bail_spanned ! ( text_signature. span( ) => error_msg)
238
- } else {
239
- Ok ( None )
240
- }
241
- } ;
242
-
243
- let text_signature = match & fn_type {
244
- FnType :: Fn ( _) | FnType :: FnClass | FnType :: FnStatic => {
245
- utils:: parse_text_signature_attrs ( & mut * meth_attrs, & python_name) ?
246
- }
247
- FnType :: FnNew => parse_erroneous_text_signature (
248
- "text_signature not allowed on __new__; if you want to add a signature on \
249
- __new__, put it on the struct definition instead",
250
- ) ?,
251
- FnType :: FnCall ( _) | FnType :: Getter ( _) | FnType :: Setter ( _) | FnType :: ClassAttribute => {
252
- parse_erroneous_text_signature ( "text_signature not allowed with this method type" ) ?
253
- }
254
- } ;
255
-
256
- let doc = utils:: get_doc ( & meth_attrs, text_signature, true ) ?;
257
-
258
- Ok ( FnSpec {
259
- tp : fn_type,
260
- name,
261
- python_name,
262
- attrs : fn_attrs,
263
- args : arguments,
264
- output : ty,
265
- doc,
266
- } )
290
+ Ok ( (
291
+ fn_type,
292
+ inputs_iter. next ( ) . map_or ( inputs_len, |( count, _) | count) ,
293
+ ) )
267
294
}
268
295
269
296
pub fn is_args ( & self , name : & syn:: Ident ) -> bool {
0 commit comments