@@ -651,6 +651,14 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
651651 self .visit_func_def (defn .items [1 ].func )
652652 setter_type = self .function_type (defn .items [1 ].func )
653653 assert isinstance (setter_type , CallableType )
654+ if len (setter_type .arg_types ) != 2 :
655+ self .fail ("Invalid property setter signature" , defn .items [1 ].func )
656+ any_type = AnyType (TypeOfAny .from_error )
657+ setter_type = setter_type .copy_modified (
658+ arg_types = [any_type , any_type ],
659+ arg_kinds = [ARG_POS , ARG_POS ],
660+ arg_names = [None , None ],
661+ )
654662 defn .items [0 ].var .setter_type = setter_type
655663 for fdef in defn .items :
656664 assert isinstance (fdef , Decorator )
@@ -2058,22 +2066,29 @@ def check_setter_type_override(
20582066 """
20592067 base_node = base_attr .node
20602068 assert isinstance (base_node , (OverloadedFuncDef , Var ))
2061- original_type = get_raw_setter_type (base_node )
2069+ original_type , is_original_setter = get_raw_setter_type (base_node )
20622070 if isinstance (base_node , Var ):
20632071 expanded_type = map_type_from_supertype (original_type , defn .info , base )
20642072 original_type = get_proper_type (
20652073 expand_self_type (base_node , expanded_type , fill_typevars (defn .info ))
20662074 )
20672075 else :
2076+ assert isinstance (original_type , ProperType )
20682077 assert isinstance (original_type , CallableType )
20692078 original_type = self .bind_and_map_method (base_attr , original_type , defn .info , base )
20702079 assert isinstance (original_type , CallableType )
2071- original_type = get_proper_type (original_type .arg_types [0 ])
2080+ if is_original_setter :
2081+ original_type = original_type .arg_types [0 ]
2082+ else :
2083+ original_type = original_type .ret_type
20722084
2073- typ = get_raw_setter_type (defn )
2074- assert isinstance (typ , CallableType )
2085+ typ , is_setter = get_raw_setter_type (defn )
2086+ assert isinstance (typ , ProperType ) and isinstance ( typ , CallableType )
20752087 typ = bind_self (typ , self .scope .active_self_type ())
2076- typ = get_proper_type (typ .arg_types [0 ])
2088+ if is_setter :
2089+ typ = typ .arg_types [0 ]
2090+ else :
2091+ typ = typ .ret_type
20772092
20782093 if not is_subtype (original_type , typ ):
20792094 self .msg .incompatible_setter_override (defn .items [1 ], typ , original_type , base )
@@ -3422,7 +3437,8 @@ def check_compatibility_all_supers(
34223437 base_type , _ = self .lvalue_type_from_base (
34233438 lvalue_node , base , setter_type = True
34243439 )
3425- # Setter type must be ready if the getter type is ready.
3440+ # Setter type for a custom property must be ready if
3441+ # the getter type is ready.
34263442 assert base_type is not None
34273443 if not is_subtype (base_type , lvalue_type ):
34283444 self .msg .incompatible_setter_override (
@@ -3519,8 +3535,14 @@ def check_compatibility_super(
35193535 def lvalue_type_from_base (
35203536 self , expr_node : Var , base : TypeInfo , setter_type : bool = False
35213537 ) -> tuple [Type | None , SymbolNode | None ]:
3522- """For a NameExpr that is part of a class, walk all base classes and try
3523- to find the first class that defines a Type for the same name."""
3538+ """Find a type for a variable name in base class.
3539+
3540+ Return the type found and the corresponding node defining the name or None
3541+ for both if the name is not defined in base or the node type is not known (yet).
3542+ The type returned is already properly mapped/bound to the subclass.
3543+ If setter_type is True, return setter types for settable properties (otherwise the
3544+ getter type is returned).
3545+ """
35243546 expr_name = expr_node .name
35253547 base_var = base .names .get (expr_name )
35263548
@@ -3558,7 +3580,8 @@ def lvalue_type_from_base(
35583580 if setter_type :
35593581 assert isinstance (base_node .items [0 ], Decorator )
35603582 base_type = base_node .items [0 ].var .setter_type
3561- assert isinstance (base_type , CallableType )
3583+ # This flag is True only for custom properties, so it is safe to assert.
3584+ assert base_type is not None
35623585 base_type = self .bind_and_map_method (base_var , base_type , expr_node .info , base )
35633586 assert isinstance (base_type , CallableType )
35643587 base_type = get_proper_type (base_type .arg_types [0 ])
@@ -8778,6 +8801,11 @@ def is_settable_property(defn: SymbolNode | None) -> TypeGuard[OverloadedFuncDef
87788801
87798802
87808803def is_custom_settable_property (defn : SymbolNode | None ) -> bool :
8804+ """Check if a node is a settable property with a non-trivial setter type.
8805+
8806+ By non-trivial here we mean that it is known (i.e. definition was already type
8807+ checked), it is not Any, and it is different from the property getter type.
8808+ """
87818809 if defn is None :
87828810 return False
87838811 if not is_settable_property (defn ):
@@ -8796,17 +8824,27 @@ def is_custom_settable_property(defn: SymbolNode | None) -> bool:
87968824 return not is_same_type (get_property_type (get_proper_type (var .type )), setter_type )
87978825
87988826
8799- def get_raw_setter_type (defn : OverloadedFuncDef | Var ) -> ProperType :
8827+ def get_raw_setter_type (defn : OverloadedFuncDef | Var ) -> tuple [Type , bool ]:
8828+ """Get an effective original setter type for a node.
8829+
8830+ For a variable it is simply its type. For a property it is the type
8831+ of the setter method (if not None), or the getter method (used as fallback
8832+ for the plugin generated properties).
8833+ Return the type and a flag indicating that we didn't fall back to getter.
8834+ """
88008835 if isinstance (defn , Var ):
88018836 # This function should not be called if the var is not ready.
88028837 assert defn .type is not None
8803- return get_proper_type ( defn .type )
8838+ return defn .type , True
88048839 first_item = defn .items [0 ]
88058840 assert isinstance (first_item , Decorator )
88068841 var = first_item .var
8807- # TODO: handle synthetic properties here and elsewhere.
8808- assert var .setter_type is not None
8809- return var .setter_type
8842+ # This function may be called on non-custom properties, so we need
8843+ # to handle the situation when it is synthetic (plugin generated).
8844+ if var .setter_type is not None :
8845+ return var .setter_type , True
8846+ assert var .type is not None
8847+ return var .type , False
88108848
88118849
88128850def get_property_type (t : ProperType ) -> ProperType :
0 commit comments