66from collections import defaultdict
77from collections .abc import Iterable , Iterator , Mapping , Sequence , Set as AbstractSet
88from contextlib import ExitStack , contextmanager
9- from typing import Callable , Final , Generic , NamedTuple , Optional , TypeVar , Union , cast , overload
9+ from typing import (
10+ Callable ,
11+ Final ,
12+ Generic ,
13+ Literal ,
14+ NamedTuple ,
15+ Optional ,
16+ TypeVar ,
17+ Union ,
18+ cast ,
19+ overload ,
20+ )
1021from typing_extensions import TypeAlias as _TypeAlias , TypeGuard
1122
1223import mypy .checkexpr
130141 WhileStmt ,
131142 WithStmt ,
132143 YieldExpr ,
144+ get_func_def ,
133145 is_final_node ,
134146)
135147from mypy .operators import flip_ops , int_op_to_method , neg_ops
@@ -276,6 +288,26 @@ class PartialTypeScope(NamedTuple):
276288 is_local : bool
277289
278290
291+ class LocalTypeMap :
292+ """Store inferred types into a temporary type map (returned).
293+
294+ This can be used to perform type checking "experiments" without
295+ affecting exported types (which are used by mypyc).
296+ """
297+
298+ def __init__ (self , chk : TypeChecker ) -> None :
299+ self .chk = chk
300+
301+ def __enter__ (self ) -> dict [Expression , Type ]:
302+ temp_type_map : dict [Expression , Type ] = {}
303+ self .chk ._type_maps .append (temp_type_map )
304+ return temp_type_map
305+
306+ def __exit__ (self , exc_type : object , exc_val : object , exc_tb : object ) -> Literal [False ]:
307+ self .chk ._type_maps .pop ()
308+ return False
309+
310+
279311class TypeChecker (NodeVisitor [None ], TypeCheckerSharedApi ):
280312 """Mypy type checker.
281313
@@ -401,6 +433,7 @@ def __init__(
401433 self .is_typeshed_stub = tree .is_typeshed_file (options )
402434 self .inferred_attribute_types = None
403435 self .allow_constructor_cache = True
436+ self .local_type_map = LocalTypeMap (self )
404437
405438 # If True, process function definitions. If False, don't. This is used
406439 # for processing module top levels in fine-grained incremental mode.
@@ -430,6 +463,13 @@ def __init__(
430463 self ._expr_checker = mypy .checkexpr .ExpressionChecker (
431464 self , self .msg , self .plugin , per_line_checking_time_ns
432465 )
466+
467+ self ._str_type : Instance | None = None
468+ self ._function_type : Instance | None = None
469+ self ._int_type : Instance | None = None
470+ self ._bool_type : Instance | None = None
471+ self ._object_type : Instance | None = None
472+
433473 self .pattern_checker = PatternChecker (self , self .msg , self .plugin , options )
434474 self ._unique_id = 0
435475
@@ -703,6 +743,12 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
703743 # TODO: keep precise type for callables with tricky but valid signatures.
704744 setter_type = fallback_setter_type
705745 defn .items [0 ].var .setter_type = setter_type
746+ if isinstance (defn .type , Overloaded ):
747+ # Update legacy property type for decorated properties.
748+ getter_type = self .extract_callable_type (defn .items [0 ].var .type , defn )
749+ if getter_type is not None :
750+ getter_type .definition = defn .items [0 ]
751+ defn .type .items [0 ] = getter_type
706752 for i , fdef in enumerate (defn .items ):
707753 assert isinstance (fdef , Decorator )
708754 if defn .is_property :
@@ -730,7 +776,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
730776 assert isinstance (item , Decorator )
731777 item_type = self .extract_callable_type (item .var .type , item )
732778 if item_type is not None :
733- item_type .definition = item . func
779+ item_type .definition = item
734780 item_types .append (item_type )
735781 if item_types :
736782 defn .type = Overloaded (item_types )
@@ -2501,8 +2547,9 @@ def check_override(
25012547
25022548 override_ids = override .type_var_ids ()
25032549 type_name = None
2504- if isinstance (override .definition , FuncDef ):
2505- type_name = override .definition .info .name
2550+ definition = get_func_def (override )
2551+ if isinstance (definition , FuncDef ):
2552+ type_name = definition .info .name
25062553
25072554 def erase_override (t : Type ) -> Type :
25082555 return erase_typevars (t , ids_to_erase = override_ids )
@@ -3509,6 +3556,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
35093556 continue
35103557
35113558 base_type , base_node = self .node_type_from_base (lvalue_node .name , base , lvalue )
3559+ # TODO: if the r.h.s. is a descriptor, we should check setter override as well.
35123560 custom_setter = is_custom_settable_property (base_node )
35133561 if isinstance (base_type , PartialType ):
35143562 base_type = None
@@ -4494,6 +4542,8 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
44944542 if isinstance (p_type , Overloaded ):
44954543 # TODO: in theory we can have a property with a deleter only.
44964544 var .is_settable_property = True
4545+ assert isinstance (definition , Decorator ), definition
4546+ var .setter_type = definition .var .setter_type
44974547
44984548 def set_inference_error_fallback_type (self , var : Var , lvalue : Lvalue , type : Type ) -> None :
44994549 """Store best known type for variable if type inference failed.
@@ -4613,7 +4663,7 @@ def check_simple_assignment(
46134663 # may cause some perf impact, plus we want to partially preserve
46144664 # the old behavior. This helps with various practical examples, see
46154665 # e.g. testOptionalTypeNarrowedByGenericCall.
4616- with self .msg .filter_errors () as local_errors , self .local_type_map () as type_map :
4666+ with self .msg .filter_errors () as local_errors , self .local_type_map as type_map :
46174667 alt_rvalue_type = self .expr_checker .accept (
46184668 rvalue , None , always_allow_any = always_allow_any
46194669 )
@@ -5356,6 +5406,8 @@ def visit_decorator_inner(
53565406 self .check_untyped_after_decorator (sig , e .func )
53575407 self .require_correct_self_argument (sig , e .func )
53585408 sig = set_callable_name (sig , e .func )
5409+ if isinstance (sig , CallableType ):
5410+ sig .definition = e
53595411 e .var .type = sig
53605412 e .var .is_ready = True
53615413 if e .func .is_property :
@@ -6218,21 +6270,26 @@ def find_isinstance_check_helper(
62186270 attr = try_getting_str_literals (node .args [1 ], self .lookup_type (node .args [1 ]))
62196271 if literal (expr ) == LITERAL_TYPE and attr and len (attr ) == 1 :
62206272 return self .hasattr_type_maps (expr , self .lookup_type (expr ), attr [0 ])
6221- elif isinstance (node .callee , RefExpr ):
6222- if node .callee .type_guard is not None or node .callee .type_is is not None :
6273+ else :
6274+ type_is , type_guard = None , None
6275+ called_type = self .lookup_type_or_none (node .callee )
6276+ if called_type is not None :
6277+ called_type = get_proper_type (called_type )
6278+ # TODO: there are some more cases in check_call() to handle.
6279+ # If the callee is an instance, try to extract TypeGuard/TypeIs from its __call__ method.
6280+ if isinstance (called_type , Instance ):
6281+ call = find_member ("__call__" , called_type , called_type , is_operator = True )
6282+ if call is not None :
6283+ called_type = get_proper_type (call )
6284+ if isinstance (called_type , CallableType ):
6285+ type_is , type_guard = called_type .type_is , called_type .type_guard
6286+
6287+ # If the callee is a RefExpr, extract TypeGuard/TypeIs directly.
6288+ if isinstance (node .callee , RefExpr ):
6289+ type_is , type_guard = node .callee .type_is , node .callee .type_guard
6290+ if type_guard is not None or type_is is not None :
62236291 # TODO: Follow *args, **kwargs
62246292 if node .arg_kinds [0 ] != nodes .ARG_POS :
6225- # the first argument might be used as a kwarg
6226- called_type = get_proper_type (self .lookup_type (node .callee ))
6227-
6228- # TODO: there are some more cases in check_call() to handle.
6229- if isinstance (called_type , Instance ):
6230- call = find_member (
6231- "__call__" , called_type , called_type , is_operator = True
6232- )
6233- if call is not None :
6234- called_type = get_proper_type (call )
6235-
62366293 # *assuming* the overloaded function is correct, there's a couple cases:
62376294 # 1) The first argument has different names, but is pos-only. We don't
62386295 # care about this case, the argument must be passed positionally.
@@ -6245,9 +6302,7 @@ def find_isinstance_check_helper(
62456302 # we want the idx-th variable to be narrowed
62466303 expr = collapse_walrus (node .args [idx ])
62476304 else :
6248- kind = (
6249- "guard" if node .callee .type_guard is not None else "narrower"
6250- )
6305+ kind = "guard" if type_guard is not None else "narrower"
62516306 self .fail (
62526307 message_registry .TYPE_GUARD_POS_ARG_REQUIRED .format (kind ), node
62536308 )
@@ -6258,15 +6313,15 @@ def find_isinstance_check_helper(
62586313 # considered "always right" (i.e. even if the types are not overlapping).
62596314 # Also note that a care must be taken to unwrap this back at read places
62606315 # where we use this to narrow down declared type.
6261- if node . callee . type_guard is not None :
6262- return {expr : TypeGuardedType (node . callee . type_guard )}, {}
6316+ if type_guard is not None :
6317+ return {expr : TypeGuardedType (type_guard )}, {}
62636318 else :
6264- assert node . callee . type_is is not None
6319+ assert type_is is not None
62656320 return conditional_types_to_typemaps (
62666321 expr ,
62676322 * self .conditional_types_with_intersection (
62686323 self .lookup_type (expr ),
6269- [TypeRange (node . callee . type_is , is_upper_bound = False )],
6324+ [TypeRange (type_is , is_upper_bound = False )],
62706325 expr ,
62716326 consider_runtime_isinstance = False ,
62726327 ),
@@ -7353,6 +7408,29 @@ def named_type(self, name: str) -> Instance:
73537408
73547409 For example, named_type('builtins.object') produces the 'object' type.
73557410 """
7411+ if name == "builtins.str" :
7412+ if self ._str_type is None :
7413+ self ._str_type = self ._named_type (name )
7414+ return self ._str_type
7415+ if name == "builtins.function" :
7416+ if self ._function_type is None :
7417+ self ._function_type = self ._named_type (name )
7418+ return self ._function_type
7419+ if name == "builtins.int" :
7420+ if self ._int_type is None :
7421+ self ._int_type = self ._named_type (name )
7422+ return self ._int_type
7423+ if name == "builtins.bool" :
7424+ if self ._bool_type is None :
7425+ self ._bool_type = self ._named_type (name )
7426+ return self ._bool_type
7427+ if name == "builtins.object" :
7428+ if self ._object_type is None :
7429+ self ._object_type = self ._named_type (name )
7430+ return self ._object_type
7431+ return self ._named_type (name )
7432+
7433+ def _named_type (self , name : str ) -> Instance :
73567434 # Assume that the name refers to a type.
73577435 sym = self .lookup_qualified (name )
73587436 node = sym .node
@@ -7412,18 +7490,6 @@ def lookup_type(self, node: Expression) -> Type:
74127490 def store_types (self , d : dict [Expression , Type ]) -> None :
74137491 self ._type_maps [- 1 ].update (d )
74147492
7415- @contextmanager
7416- def local_type_map (self ) -> Iterator [dict [Expression , Type ]]:
7417- """Store inferred types into a temporary type map (returned).
7418-
7419- This can be used to perform type checking "experiments" without
7420- affecting exported types (which are used by mypyc).
7421- """
7422- temp_type_map : dict [Expression , Type ] = {}
7423- self ._type_maps .append (temp_type_map )
7424- yield temp_type_map
7425- self ._type_maps .pop ()
7426-
74277493 def in_checked_function (self ) -> bool :
74287494 """Should we type-check the current function?
74297495
@@ -8651,17 +8717,21 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type:
86518717 return t .copy_modified (args = [a .accept (self ) for a in t .args ])
86528718
86538719
8654- def is_classmethod_node (node : Node | None ) -> bool | None :
8720+ def is_classmethod_node (node : SymbolNode | None ) -> bool | None :
86558721 """Find out if a node describes a classmethod."""
8722+ if isinstance (node , Decorator ):
8723+ node = node .func
86568724 if isinstance (node , FuncDef ):
86578725 return node .is_class
86588726 if isinstance (node , Var ):
86598727 return node .is_classmethod
86608728 return None
86618729
86628730
8663- def is_node_static (node : Node | None ) -> bool | None :
8731+ def is_node_static (node : SymbolNode | None ) -> bool | None :
86648732 """Find out if a node describes a static function method."""
8733+ if isinstance (node , Decorator ):
8734+ node = node .func
86658735 if isinstance (node , FuncDef ):
86668736 return node .is_static
86678737 if isinstance (node , Var ):
0 commit comments