54
54
YieldFromExpr , NamedTupleExpr , NonlocalDecl , SymbolNode ,
55
55
SetComprehension , DictionaryComprehension , TypeAlias , TypeAliasExpr ,
56
56
YieldExpr , ExecStmt , BackquoteExpr , ImportBase , AwaitExpr ,
57
- IntExpr , FloatExpr , UnicodeExpr , TempNode , ImportedName ,
57
+ IntExpr , FloatExpr , UnicodeExpr , TempNode , ImportedName , OverloadPart ,
58
58
COVARIANT , CONTRAVARIANT , INVARIANT , UNBOUND_IMPORTED , LITERAL_YES , nongen_builtins ,
59
59
get_member_expr_fullname , REVEAL_TYPE , REVEAL_LOCALS
60
60
)
@@ -527,100 +527,142 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
527
527
# with a @property with a setter or a deleter, and for a classic
528
528
# @overload.
529
529
530
- # Decide whether to analyze this as a property or an overload. If an
531
- # overload, and we're outside a stub, find the impl and set it. Remove
532
- # the impl from the item list, it's special.
533
- types = [] # type: List[CallableType]
534
- non_overload_indexes = []
530
+ defn ._fullname = self .qualified_name (defn .name ())
535
531
536
- # See if the first item is a property (and not an overload)
537
532
first_item = defn .items [0 ]
538
533
first_item .is_overload = True
539
534
first_item .accept (self )
540
535
541
- defn ._fullname = self .qualified_name (defn .name ())
542
-
543
536
if isinstance (first_item , Decorator ) and first_item .func .is_property :
537
+ # This is a property.
544
538
first_item .func .is_overload = True
545
539
self .analyze_property_with_multi_part_definition (defn )
546
540
typ = function_type (first_item .func , self .builtin_type ('builtins.function' ))
547
541
assert isinstance (typ , CallableType )
548
542
types = [typ ]
549
543
else :
550
- for i , item in enumerate (defn .items ):
551
- if i != 0 :
552
- # The first item was already visited
553
- item .is_overload = True
554
- item .accept (self )
555
- # TODO: support decorated overloaded functions properly
556
- if isinstance (item , Decorator ):
557
- callable = function_type (item .func , self .builtin_type ('builtins.function' ))
558
- assert isinstance (callable , CallableType )
559
- if not any (refers_to_fullname (dec , 'typing.overload' )
560
- for dec in item .decorators ):
561
- if i == len (defn .items ) - 1 and not self .is_stub_file :
562
- # Last item outside a stub is impl
563
- defn .impl = item
564
- else :
565
- # Oops it wasn't an overload after all. A clear error
566
- # will vary based on where in the list it is, record
567
- # that.
568
- non_overload_indexes .append (i )
569
- else :
570
- item .func .is_overload = True
571
- types .append (callable )
572
- elif isinstance (item , FuncDef ):
573
- if i == len (defn .items ) - 1 and not self .is_stub_file :
574
- defn .impl = item
575
- else :
576
- non_overload_indexes .append (i )
544
+ # This is an a normal overload. Find the item signatures, the
545
+ # implementation (if outside a stub), and any missing @overload
546
+ # decorators.
547
+ types , impl , non_overload_indexes = self .find_overload_sigs_and_impl (defn )
548
+ defn .impl = impl
577
549
if non_overload_indexes :
578
- if types :
579
- # Some of them were overloads, but not all.
580
- for idx in non_overload_indexes :
581
- if self .is_stub_file :
582
- self .fail ("An implementation for an overloaded function "
583
- "is not allowed in a stub file" , defn .items [idx ])
584
- else :
585
- self .fail ("The implementation for an overloaded function "
586
- "must come last" , defn .items [idx ])
587
- else :
588
- for idx in non_overload_indexes [1 :]:
589
- self .name_already_defined (defn .name (), defn .items [idx ], first_item )
590
- if defn .impl :
591
- self .name_already_defined (defn .name (), defn .impl , first_item )
592
- # Remove the non-overloads
593
- for idx in reversed (non_overload_indexes ):
594
- del defn .items [idx ]
595
- # If we found an implementation, remove it from the overloads to
596
- # consider.
597
- if defn .impl is not None :
598
- assert defn .impl is defn .items [- 1 ]
550
+ self .handle_missing_overload_decorators (defn , non_overload_indexes ,
551
+ some_overload_decorators = len (types ) > 0 )
552
+ # If we found an implementation, remove it from the overload item list,
553
+ # as it's special.
554
+ if impl is not None :
555
+ assert impl is defn .items [- 1 ]
599
556
defn .items = defn .items [:- 1 ]
600
- elif not self .is_stub_file and not non_overload_indexes :
601
- if not (self .type and not self .is_func_scope () and self .type .is_protocol ):
602
- self .fail (
603
- "An overloaded function outside a stub file must have an implementation" ,
604
- defn )
605
- else :
606
- for item in defn .items :
607
- if isinstance (item , Decorator ):
608
- item .func .is_abstract = True
609
- else :
610
- item .is_abstract = True
557
+ elif not non_overload_indexes :
558
+ self .handle_missing_overload_implementation (defn )
611
559
612
560
if types :
613
561
defn .type = Overloaded (types )
614
562
defn .type .line = defn .line
615
563
616
564
if not defn .items :
617
- # It was not any kind of overload def after all. We've visited the
618
- # redefinitions already.
565
+ # It was not a real overload after all, but function redefinition . We've
566
+ # visited the redefinition(s) already.
619
567
return
620
568
621
- # Check final status, if the implementation is marked
622
- # as @final (or the first overload in stubs), then the whole overloaded
623
- # definition if @final.
569
+ # We know this is an overload def. Infer properties and perform some checks.
570
+ self .process_final_in_overload (defn )
571
+ self .process_static_or_class_method_in_overload (defn )
572
+
573
+ if self .type and not self .is_func_scope ():
574
+ self .type .names [defn .name ()] = SymbolTableNode (MDEF , defn )
575
+ defn .info = self .type
576
+ elif self .is_func_scope ():
577
+ self .add_local (defn , defn )
578
+
579
+ def find_overload_sigs_and_impl (
580
+ self ,
581
+ defn : OverloadedFuncDef ) -> Tuple [List [CallableType ],
582
+ Optional [OverloadPart ],
583
+ List [int ]]:
584
+ """Find overload signatures, the implementation, and items with missing @overload.
585
+
586
+ Assume that the first was already analyzed. As a side effect:
587
+ analyzes remaining items and updates 'is_overload' flags.
588
+ """
589
+ types = []
590
+ non_overload_indexes = []
591
+ impl = None # type: Optional[OverloadPart]
592
+ for i , item in enumerate (defn .items ):
593
+ if i != 0 :
594
+ # Assume that the first item was already visited
595
+ item .is_overload = True
596
+ item .accept (self )
597
+ # TODO: support decorated overloaded functions properly
598
+ if isinstance (item , Decorator ):
599
+ callable = function_type (item .func , self .builtin_type ('builtins.function' ))
600
+ assert isinstance (callable , CallableType )
601
+ if not any (refers_to_fullname (dec , 'typing.overload' )
602
+ for dec in item .decorators ):
603
+ if i == len (defn .items ) - 1 and not self .is_stub_file :
604
+ # Last item outside a stub is impl
605
+ impl = item
606
+ else :
607
+ # Oops it wasn't an overload after all. A clear error
608
+ # will vary based on where in the list it is, record
609
+ # that.
610
+ non_overload_indexes .append (i )
611
+ else :
612
+ item .func .is_overload = True
613
+ types .append (callable )
614
+ elif isinstance (item , FuncDef ):
615
+ if i == len (defn .items ) - 1 and not self .is_stub_file :
616
+ impl = item
617
+ else :
618
+ non_overload_indexes .append (i )
619
+ return types , impl , non_overload_indexes
620
+
621
+ def handle_missing_overload_decorators (self ,
622
+ defn : OverloadedFuncDef ,
623
+ non_overload_indexes : List [int ],
624
+ some_overload_decorators : bool ) -> None :
625
+ """Generate errors for overload items without @overload.
626
+
627
+ Side effect: remote non-overload items.
628
+ """
629
+ if some_overload_decorators :
630
+ # Some of them were overloads, but not all.
631
+ for idx in non_overload_indexes :
632
+ if self .is_stub_file :
633
+ self .fail ("An implementation for an overloaded function "
634
+ "is not allowed in a stub file" , defn .items [idx ])
635
+ else :
636
+ self .fail ("The implementation for an overloaded function "
637
+ "must come last" , defn .items [idx ])
638
+ else :
639
+ for idx in non_overload_indexes [1 :]:
640
+ self .name_already_defined (defn .name (), defn .items [idx ], defn .items [0 ])
641
+ if defn .impl :
642
+ self .name_already_defined (defn .name (), defn .impl , defn .items [0 ])
643
+ # Remove the non-overloads
644
+ for idx in reversed (non_overload_indexes ):
645
+ del defn .items [idx ]
646
+
647
+ def handle_missing_overload_implementation (self , defn : OverloadedFuncDef ) -> None :
648
+ """Generate error about missing overload implementation (only if needed)."""
649
+ if not self .is_stub_file :
650
+ if self .type and self .type .is_protocol and not self .is_func_scope ():
651
+ # An overloded protocol method doesn't need an implementation.
652
+ for item in defn .items :
653
+ if isinstance (item , Decorator ):
654
+ item .func .is_abstract = True
655
+ else :
656
+ item .is_abstract = True
657
+ else :
658
+ self .fail (
659
+ "An overloaded function outside a stub file must have an implementation" ,
660
+ defn )
661
+
662
+ def process_final_in_overload (self , defn : OverloadedFuncDef ) -> None :
663
+ """Detect the @final status of an overloaded function (and perform checks)."""
664
+ # If the implementation is marked as @final (or the first overload in
665
+ # stubs), then the whole overloaded definition if @final.
624
666
if any (item .is_final for item in defn .items ):
625
667
# We anyway mark it as final because it was probably the intention.
626
668
defn .is_final = True
@@ -636,7 +678,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
636
678
if defn .impl is not None and defn .impl .is_final :
637
679
defn .is_final = True
638
680
639
- # We know this is an overload def -- let's handle classmethod and staticmethod
681
+ def process_static_or_class_method_in_overload ( self , defn : OverloadedFuncDef ) -> None :
640
682
class_status = []
641
683
static_status = []
642
684
for item in defn .items :
@@ -667,12 +709,6 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
667
709
defn .is_class = class_status [0 ]
668
710
defn .is_static = static_status [0 ]
669
711
670
- if self .type and not self .is_func_scope ():
671
- self .type .names [defn .name ()] = SymbolTableNode (MDEF , defn )
672
- defn .info = self .type
673
- elif self .is_func_scope ():
674
- self .add_local (defn , defn )
675
-
676
712
def analyze_property_with_multi_part_definition (self , defn : OverloadedFuncDef ) -> None :
677
713
"""Analyze a property defined using multiple methods (e.g., using @x.setter).
678
714
0 commit comments