@@ -9,7 +9,7 @@ use quote::ToTokens;
99use quote:: { quote, quote_spanned} ;
1010use syn:: ext:: IdentExt ;
1111use syn:: spanned:: Spanned ;
12- use syn:: Result ;
12+ use syn:: { Ident , Result } ;
1313
1414#[ derive( Clone , Debug ) ]
1515pub struct FnArg < ' a > {
@@ -69,24 +69,6 @@ fn handle_argument_error(pat: &syn::Pat) -> syn::Error {
6969 syn:: Error :: new ( span, msg)
7070}
7171
72- #[ derive( Clone , PartialEq , Debug , Copy , Eq ) ]
73- pub enum MethodTypeAttribute {
74- /// `#[new]`
75- New ,
76- /// `#[new]` && `#[classmethod]`
77- NewClassMethod ,
78- /// `#[classmethod]`
79- ClassMethod ,
80- /// `#[classattr]`
81- ClassAttribute ,
82- /// `#[staticmethod]`
83- StaticMethod ,
84- /// `#[getter]`
85- Getter ,
86- /// `#[setter]`
87- Setter ,
88- }
89-
9072#[ derive( Clone , Debug ) ]
9173pub enum FnType {
9274 Getter ( SelfType ) ,
@@ -278,13 +260,10 @@ impl<'a> FnSpec<'a> {
278260 ..
279261 } = options;
280262
281- let MethodAttributes {
282- ty : fn_type_attr,
283- mut python_name,
284- } = parse_method_attributes ( meth_attrs, name. map ( |name| name. value . 0 ) ) ?;
263+ let mut python_name = name. map ( |name| name. value . 0 ) ;
285264
286265 let ( fn_type, skip_first_arg, fixed_convention) =
287- Self :: parse_fn_type ( sig, fn_type_attr , & mut python_name) ?;
266+ Self :: parse_fn_type ( sig, meth_attrs , & mut python_name) ?;
288267 ensure_signatures_on_valid_method ( & fn_type, signature. as_ref ( ) , text_signature. as_ref ( ) ) ?;
289268
290269 let name = & sig. ident ;
@@ -331,9 +310,11 @@ impl<'a> FnSpec<'a> {
331310
332311 fn parse_fn_type (
333312 sig : & syn:: Signature ,
334- fn_type_attr : Option < MethodTypeAttribute > ,
313+ meth_attrs : & mut Vec < syn :: Attribute > ,
335314 python_name : & mut Option < syn:: Ident > ,
336315 ) -> Result < ( FnType , bool , Option < CallingConvention > ) > {
316+ let mut method_attributes = parse_method_attributes ( meth_attrs) ?;
317+
337318 let name = & sig. ident ;
338319 let parse_receiver = |msg : & ' static str | {
339320 let first_arg = sig
@@ -351,52 +332,70 @@ impl<'a> FnSpec<'a> {
351332 . map ( |stripped| syn:: Ident :: new ( stripped, name. span ( ) ) )
352333 } ;
353334
354- let ( fn_type, skip_first_arg, fixed_convention) = match fn_type_attr {
355- Some ( MethodTypeAttribute :: StaticMethod ) => ( FnType :: FnStatic , false , None ) ,
356- Some ( MethodTypeAttribute :: ClassAttribute ) => ( FnType :: ClassAttribute , false , None ) ,
357- Some ( MethodTypeAttribute :: New ) | Some ( MethodTypeAttribute :: NewClassMethod ) => {
335+ let ( fn_type, skip_first_arg, fixed_convention) = match method_attributes. as_mut_slice ( ) {
336+ [ ] => (
337+ FnType :: Fn ( parse_receiver (
338+ "static method needs #[staticmethod] attribute" ,
339+ ) ?) ,
340+ true ,
341+ None ,
342+ ) ,
343+ [ MethodTypeAttribute :: StaticMethod ( _) ] => ( FnType :: FnStatic , false , None ) ,
344+ [ MethodTypeAttribute :: ClassAttribute ( _) ] => ( FnType :: ClassAttribute , false , None ) ,
345+ [ MethodTypeAttribute :: New ( _) ]
346+ | [ MethodTypeAttribute :: New ( _) , MethodTypeAttribute :: ClassMethod ( _) ]
347+ | [ MethodTypeAttribute :: ClassMethod ( _) , MethodTypeAttribute :: New ( _) ] => {
358348 if let Some ( name) = & python_name {
359349 bail_spanned ! ( name. span( ) => "`name` not allowed with `#[new]`" ) ;
360350 }
361351 * python_name = Some ( syn:: Ident :: new ( "__new__" , Span :: call_site ( ) ) ) ;
362- if matches ! ( fn_type_attr , Some ( MethodTypeAttribute :: New ) ) {
352+ if matches ! ( method_attributes . as_slice ( ) , [ MethodTypeAttribute :: New ( _ ) ] ) {
363353 ( FnType :: FnNew , false , Some ( CallingConvention :: TpNew ) )
364354 } else {
365355 ( FnType :: FnNewClass , true , Some ( CallingConvention :: TpNew ) )
366356 }
367357 }
368- Some ( MethodTypeAttribute :: ClassMethod ) => ( FnType :: FnClass , true , None ) ,
369- Some ( MethodTypeAttribute :: Getter ) => {
370- // Strip off "get_" prefix if needed
371- if python_name. is_none ( ) {
358+ [ MethodTypeAttribute :: ClassMethod ( _) ] => ( FnType :: FnClass , true , None ) ,
359+ [ MethodTypeAttribute :: Getter ( _, name) ] => {
360+ if let Some ( name) = name. take ( ) {
361+ ensure_spanned ! (
362+ python_name. replace( name) . is_none( ) ,
363+ python_name. span( ) => "`name` may only be specified once"
364+ ) ;
365+ } else if python_name. is_none ( ) {
366+ // Strip off "get_" prefix if needed
372367 * python_name = strip_fn_name ( "get_" ) ;
373368 }
374369
375370 (
376- FnType :: Getter ( parse_receiver ( "expected receiver for #[getter]" ) ?) ,
371+ FnType :: Getter ( parse_receiver ( "expected receiver for ` #[getter]` " ) ?) ,
377372 true ,
378373 None ,
379374 )
380375 }
381- Some ( MethodTypeAttribute :: Setter ) => {
382- // Strip off "set_" prefix if needed
383- if python_name. is_none ( ) {
376+ [ MethodTypeAttribute :: Setter ( _, name) ] => {
377+ if let Some ( name) = name. take ( ) {
378+ ensure_spanned ! (
379+ python_name. replace( name) . is_none( ) ,
380+ python_name. span( ) => "`name` may only be specified once"
381+ ) ;
382+ } else if python_name. is_none ( ) {
383+ // Strip off "get_" prefix if needed
384384 * python_name = strip_fn_name ( "set_" ) ;
385385 }
386386
387387 (
388- FnType :: Setter ( parse_receiver ( "expected receiver for #[setter]" ) ?) ,
388+ FnType :: Setter ( parse_receiver ( "expected receiver for ` #[setter]` " ) ?) ,
389389 true ,
390390 None ,
391391 )
392392 }
393- None => (
394- FnType :: Fn ( parse_receiver (
395- "static method needs #[staticmethod] attribute" ,
396- ) ?) ,
397- true ,
398- None ,
399- ) ,
393+ [ first, second, ..] => {
394+ bail_spanned ! (
395+ second. span( ) =>
396+ format!( "`#[{}]` may not be combined with `#[{}]`" , first. ident( ) , second. ident( ) )
397+ )
398+ }
400399 } ;
401400 Ok ( ( fn_type, skip_first_arg, fixed_convention) )
402401 }
@@ -604,107 +603,123 @@ impl<'a> FnSpec<'a> {
604603 }
605604}
606605
607- #[ derive( Debug ) ]
608- struct MethodAttributes {
609- ty : Option < MethodTypeAttribute > ,
610- python_name : Option < syn:: Ident > ,
606+ enum MethodTypeAttribute {
607+ New ( Span ) ,
608+ ClassMethod ( Span ) ,
609+ StaticMethod ( Span ) ,
610+ Getter ( Span , Option < Ident > ) ,
611+ Setter ( Span , Option < Ident > ) ,
612+ ClassAttribute ( Span ) ,
611613}
612614
613- fn parse_method_attributes (
614- attrs : & mut Vec < syn:: Attribute > ,
615- mut python_name : Option < syn:: Ident > ,
616- ) -> Result < MethodAttributes > {
617- let mut new_attrs = Vec :: new ( ) ;
618- let mut ty: Option < MethodTypeAttribute > = None ;
619-
620- macro_rules! set_compound_ty {
621- ( $new_ty: expr, $ident: expr) => {
622- ty = match ( ty, $new_ty) {
623- ( None , new_ty) => Some ( new_ty) ,
624- ( Some ( MethodTypeAttribute :: ClassMethod ) , MethodTypeAttribute :: New ) => Some ( MethodTypeAttribute :: NewClassMethod ) ,
625- ( Some ( MethodTypeAttribute :: New ) , MethodTypeAttribute :: ClassMethod ) => Some ( MethodTypeAttribute :: NewClassMethod ) ,
626- ( Some ( _) , _) => bail_spanned!( $ident. span( ) => "can only combine `new` and `classmethod`" ) ,
627- } ;
628- } ;
615+ impl MethodTypeAttribute {
616+ fn span ( & self ) -> Span {
617+ match self {
618+ MethodTypeAttribute :: New ( span)
619+ | MethodTypeAttribute :: ClassMethod ( span)
620+ | MethodTypeAttribute :: StaticMethod ( span)
621+ | MethodTypeAttribute :: Getter ( span, _)
622+ | MethodTypeAttribute :: Setter ( span, _)
623+ | MethodTypeAttribute :: ClassAttribute ( span) => * span,
624+ }
629625 }
630626
631- macro_rules! set_ty {
632- ( $new_ty: expr, $ident: expr) => {
633- ensure_spanned!(
634- ty. replace( $new_ty) . is_none( ) ,
635- $ident. span( ) => "cannot combine these method types"
636- ) ;
637- } ;
627+ fn ident ( & self ) -> & ' static str {
628+ match self {
629+ MethodTypeAttribute :: New ( _) => "new" ,
630+ MethodTypeAttribute :: ClassMethod ( _) => "classmethod" ,
631+ MethodTypeAttribute :: StaticMethod ( _) => "staticmethod" ,
632+ MethodTypeAttribute :: Getter ( _, _) => "getter" ,
633+ MethodTypeAttribute :: Setter ( _, _) => "setter" ,
634+ MethodTypeAttribute :: ClassAttribute ( _) => "classattr" ,
635+ }
638636 }
639637
640- for attr in attrs. drain ( ..) {
641- match attr. meta {
642- syn:: Meta :: Path ( ref name) => {
643- if name. is_ident ( "new" ) || name. is_ident ( "__new__" ) {
644- set_compound_ty ! ( MethodTypeAttribute :: New , name) ;
645- } else if name. is_ident ( "classmethod" ) {
646- set_compound_ty ! ( MethodTypeAttribute :: ClassMethod , name) ;
647- } else if name. is_ident ( "staticmethod" ) {
648- set_ty ! ( MethodTypeAttribute :: StaticMethod , name) ;
649- } else if name. is_ident ( "classattr" ) {
650- set_ty ! ( MethodTypeAttribute :: ClassAttribute , name) ;
651- } else if name. is_ident ( "setter" ) || name. is_ident ( "getter" ) {
652- if let syn:: AttrStyle :: Inner ( _) = attr. style {
653- bail_spanned ! (
654- attr. span( ) => "inner attribute is not supported for setter and getter"
655- ) ;
656- }
657- if name. is_ident ( "setter" ) {
658- set_ty ! ( MethodTypeAttribute :: Setter , name) ;
659- } else {
660- set_ty ! ( MethodTypeAttribute :: Getter , name) ;
661- }
662- } else {
663- new_attrs. push ( attr)
638+ /// Attempts to parse a method type attribute.
639+ ///
640+ /// If the attribute does not match one of the attribute names, returns `Ok(None)`.
641+ ///
642+ /// Otherwise will either return a parse error or the attribute.
643+ fn parse_if_matching_attribute ( attr : & syn:: Attribute ) -> Result < Option < Self > > {
644+ fn ensure_no_arguments ( meta : & syn:: Meta , ident : & str ) -> syn:: Result < ( ) > {
645+ match meta {
646+ syn:: Meta :: Path ( _) => Ok ( ( ) ) ,
647+ syn:: Meta :: List ( l) => bail_spanned ! (
648+ l. span( ) => format!(
649+ "`#[{ident}]` does not take any arguments\n = help: did you mean `#[{ident}] #[pyo3({meta})]`?" ,
650+ ident = ident,
651+ meta = l. tokens,
652+ )
653+ ) ,
654+ syn:: Meta :: NameValue ( nv) => {
655+ bail_spanned ! ( nv. eq_token. span( ) => format!( "`#[{}]` does not take any arguments" , ident) )
664656 }
665657 }
666- syn:: Meta :: List ( ref ml @ syn:: MetaList { ref path, .. } ) => {
667- if path. is_ident ( "new" ) {
668- set_ty ! ( MethodTypeAttribute :: New , path) ;
669- } else if path. is_ident ( "setter" ) || path. is_ident ( "getter" ) {
670- if let syn:: AttrStyle :: Inner ( _) = attr. style {
671- bail_spanned ! (
672- attr. span( ) => "inner attribute is not supported for setter and getter"
673- ) ;
674- }
675-
676- if path. is_ident ( "setter" ) {
677- set_ty ! ( MethodTypeAttribute :: Setter , path) ;
678- } else {
679- set_ty ! ( MethodTypeAttribute :: Getter , path) ;
680- } ;
681-
682- ensure_spanned ! (
683- python_name. is_none( ) ,
684- python_name. span( ) => "`name` may only be specified once"
685- ) ;
658+ }
686659
687- if let Ok ( ident) = ml. parse_args :: < syn:: Ident > ( ) {
688- python_name = Some ( ident) ;
689- } else if let Ok ( syn:: Lit :: Str ( s) ) = ml. parse_args :: < syn:: Lit > ( ) {
690- python_name = Some ( s. parse ( ) ?) ;
660+ fn extract_name ( meta : & syn:: Meta , ident : & str ) -> Result < Option < Ident > > {
661+ match meta {
662+ syn:: Meta :: Path ( _) => Ok ( None ) ,
663+ syn:: Meta :: NameValue ( nv) => bail_spanned ! (
664+ nv. eq_token. span( ) => format!( "expected `#[{}(name)]` to set the name" , ident)
665+ ) ,
666+ syn:: Meta :: List ( ml) => {
667+ if let Ok ( name) = ml. parse_args :: < syn:: Ident > ( ) {
668+ Ok ( Some ( name) )
669+ } else if let Ok ( name) = ml. parse_args :: < syn:: LitStr > ( ) {
670+ name. parse ( ) . map ( Some )
691671 } else {
692- return Err ( syn:: Error :: new_spanned (
693- ml,
694- "expected ident or string literal for property name" ,
695- ) ) ;
672+ bail_spanned ! ( ml. tokens. span( ) => "expected ident or string literal for property name" ) ;
696673 }
697- } else {
698- new_attrs. push ( attr)
699674 }
700675 }
701- syn:: Meta :: NameValue ( _) => new_attrs. push ( attr) ,
676+ }
677+
678+ let meta = & attr. meta ;
679+ let path = meta. path ( ) ;
680+
681+ if path. is_ident ( "new" ) {
682+ ensure_no_arguments ( meta, "new" ) ?;
683+ Ok ( Some ( MethodTypeAttribute :: New ( path. span ( ) ) ) )
684+ } else if path. is_ident ( "__new__" ) {
685+ // TODO deprecate this form?
686+ ensure_no_arguments ( meta, "__new__" ) ?;
687+ Ok ( Some ( MethodTypeAttribute :: New ( path. span ( ) ) ) )
688+ } else if path. is_ident ( "classmethod" ) {
689+ ensure_no_arguments ( meta, "classmethod" ) ?;
690+ Ok ( Some ( MethodTypeAttribute :: ClassMethod ( path. span ( ) ) ) )
691+ } else if path. is_ident ( "staticmethod" ) {
692+ ensure_no_arguments ( meta, "staticmethod" ) ?;
693+ Ok ( Some ( MethodTypeAttribute :: StaticMethod ( path. span ( ) ) ) )
694+ } else if path. is_ident ( "classattr" ) {
695+ ensure_no_arguments ( meta, "classattr" ) ?;
696+ Ok ( Some ( MethodTypeAttribute :: ClassAttribute ( path. span ( ) ) ) )
697+ } else if path. is_ident ( "getter" ) {
698+ let name = extract_name ( meta, "getter" ) ?;
699+ Ok ( Some ( MethodTypeAttribute :: Getter ( path. span ( ) , name) ) )
700+ } else if path. is_ident ( "setter" ) {
701+ let name = extract_name ( meta, "setter" ) ?;
702+ Ok ( Some ( MethodTypeAttribute :: Setter ( path. span ( ) , name) ) )
703+ } else {
704+ Ok ( None )
705+ }
706+ }
707+ }
708+
709+ fn parse_method_attributes ( attrs : & mut Vec < syn:: Attribute > ) -> Result < Vec < MethodTypeAttribute > > {
710+ let mut new_attrs = Vec :: new ( ) ;
711+ let mut found_attrs = Vec :: new ( ) ;
712+
713+ for attr in attrs. drain ( ..) {
714+ match MethodTypeAttribute :: parse_if_matching_attribute ( & attr) ? {
715+ Some ( attr) => found_attrs. push ( attr) ,
716+ None => new_attrs. push ( attr) ,
702717 }
703718 }
704719
705720 * attrs = new_attrs;
706721
707- Ok ( MethodAttributes { ty , python_name } )
722+ Ok ( found_attrs )
708723}
709724
710725const IMPL_TRAIT_ERR : & str = "Python functions cannot have `impl Trait` arguments" ;
0 commit comments