@@ -636,13 +636,30 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
636636 self .visit_decorator (defn .items [0 ])
637637 for fdef in defn .items :
638638 assert isinstance (fdef , Decorator )
639- self .check_func_item (fdef .func , name = fdef .func .name , allow_empty = True )
639+ if defn .is_property :
640+ self .check_func_item (fdef .func , name = fdef .func .name , allow_empty = True )
641+ else :
642+ # Perform full check for real overloads to infer type of all decorated
643+ # overload variants.
644+ self .visit_decorator_inner (fdef , allow_empty = True )
640645 if fdef .func .abstract_status in (IS_ABSTRACT , IMPLICITLY_ABSTRACT ):
641646 num_abstract += 1
642647 if num_abstract not in (0 , len (defn .items )):
643648 self .fail (message_registry .INCONSISTENT_ABSTRACT_OVERLOAD , defn )
644649 if defn .impl :
645650 defn .impl .accept (self )
651+ if not defn .is_property :
652+ self .check_overlapping_overloads (defn )
653+ if defn .type is None :
654+ item_types = []
655+ for item in defn .items :
656+ assert isinstance (item , Decorator )
657+ item_type = self .extract_callable_type (item .var .type , item )
658+ if item_type is not None :
659+ item_types .append (item_type )
660+ if item_types :
661+ defn .type = Overloaded (item_types )
662+ # Check override validity after we analyzed current definition.
646663 if defn .info :
647664 found_method_base_classes = self .check_method_override (defn )
648665 if (
@@ -653,10 +670,35 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
653670 self .msg .no_overridable_method (defn .name , defn )
654671 self .check_explicit_override_decorator (defn , found_method_base_classes , defn .impl )
655672 self .check_inplace_operator_method (defn )
656- if not defn .is_property :
657- self .check_overlapping_overloads (defn )
658673 return None
659674
675+ def extract_callable_type (self , inner_type : Type | None , ctx : Context ) -> CallableType | None :
676+ """Get type as seen by an overload item caller."""
677+ inner_type = get_proper_type (inner_type )
678+ outer_type : CallableType | None = None
679+ if inner_type is not None and not isinstance (inner_type , AnyType ):
680+ if isinstance (inner_type , CallableType ):
681+ outer_type = inner_type
682+ elif isinstance (inner_type , Instance ):
683+ inner_call = get_proper_type (
684+ analyze_member_access (
685+ name = "__call__" ,
686+ typ = inner_type ,
687+ context = ctx ,
688+ is_lvalue = False ,
689+ is_super = False ,
690+ is_operator = True ,
691+ msg = self .msg ,
692+ original_type = inner_type ,
693+ chk = self ,
694+ )
695+ )
696+ if isinstance (inner_call , CallableType ):
697+ outer_type = inner_call
698+ if outer_type is None :
699+ self .msg .not_callable (inner_type , ctx )
700+ return outer_type
701+
660702 def check_overlapping_overloads (self , defn : OverloadedFuncDef ) -> None :
661703 # At this point we should have set the impl already, and all remaining
662704 # items are decorators
@@ -680,40 +722,20 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
680722
681723 # This can happen if we've got an overload with a different
682724 # decorator or if the implementation is untyped -- we gave up on the types.
683- inner_type = get_proper_type (inner_type )
684- if inner_type is not None and not isinstance (inner_type , AnyType ):
685- if isinstance (inner_type , CallableType ):
686- impl_type = inner_type
687- elif isinstance (inner_type , Instance ):
688- inner_call = get_proper_type (
689- analyze_member_access (
690- name = "__call__" ,
691- typ = inner_type ,
692- context = defn .impl ,
693- is_lvalue = False ,
694- is_super = False ,
695- is_operator = True ,
696- msg = self .msg ,
697- original_type = inner_type ,
698- chk = self ,
699- )
700- )
701- if isinstance (inner_call , CallableType ):
702- impl_type = inner_call
703- if impl_type is None :
704- self .msg .not_callable (inner_type , defn .impl )
725+ impl_type = self .extract_callable_type (inner_type , defn .impl )
705726
706727 is_descriptor_get = defn .info and defn .name == "__get__"
707728 for i , item in enumerate (defn .items ):
708- # TODO overloads involving decorators
709729 assert isinstance (item , Decorator )
710- sig1 = self .function_type (item .func )
711- assert isinstance (sig1 , CallableType )
730+ sig1 = self .extract_callable_type (item .var .type , item )
731+ if sig1 is None :
732+ continue
712733
713734 for j , item2 in enumerate (defn .items [i + 1 :]):
714735 assert isinstance (item2 , Decorator )
715- sig2 = self .function_type (item2 .func )
716- assert isinstance (sig2 , CallableType )
736+ sig2 = self .extract_callable_type (item2 .var .type , item2 )
737+ if sig2 is None :
738+ continue
717739
718740 if not are_argument_counts_overlapping (sig1 , sig2 ):
719741 continue
@@ -4751,17 +4773,20 @@ def visit_decorator(self, e: Decorator) -> None:
47514773 e .var .type = AnyType (TypeOfAny .special_form )
47524774 e .var .is_ready = True
47534775 return
4776+ self .visit_decorator_inner (e )
47544777
4778+ def visit_decorator_inner (self , e : Decorator , allow_empty : bool = False ) -> None :
47554779 if self .recurse_into_functions :
47564780 with self .tscope .function_scope (e .func ):
4757- self .check_func_item (e .func , name = e .func .name )
4781+ self .check_func_item (e .func , name = e .func .name , allow_empty = allow_empty )
47584782
47594783 # Process decorators from the inside out to determine decorated signature, which
47604784 # may be different from the declared signature.
47614785 sig : Type = self .function_type (e .func )
47624786 for d in reversed (e .decorators ):
47634787 if refers_to_fullname (d , OVERLOAD_NAMES ):
4764- self .fail (message_registry .MULTIPLE_OVERLOADS_REQUIRED , e )
4788+ if not allow_empty :
4789+ self .fail (message_registry .MULTIPLE_OVERLOADS_REQUIRED , e )
47654790 continue
47664791 dec = self .expr_checker .accept (d )
47674792 temp = self .temp_node (sig , context = e )
@@ -4788,6 +4813,8 @@ def visit_decorator(self, e: Decorator) -> None:
47884813 self .msg .fail ("Too many arguments for property" , e )
47894814 self .check_incompatible_property_override (e )
47904815 # For overloaded functions we already checked override for overload as a whole.
4816+ if allow_empty :
4817+ return
47914818 if e .func .info and not e .func .is_dynamic () and not e .is_overload :
47924819 found_method_base_classes = self .check_method_override (e )
47934820 if (
0 commit comments