6
6
from collections import defaultdict
7
7
from collections .abc import Iterable , Iterator , Mapping , Sequence , Set as AbstractSet
8
8
from 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
+ )
10
21
from typing_extensions import TypeAlias as _TypeAlias , TypeGuard
11
22
12
23
import mypy .checkexpr
130
141
WhileStmt ,
131
142
WithStmt ,
132
143
YieldExpr ,
144
+ get_func_def ,
133
145
is_final_node ,
134
146
)
135
147
from mypy .operators import flip_ops , int_op_to_method , neg_ops
@@ -276,6 +288,26 @@ class PartialTypeScope(NamedTuple):
276
288
is_local : bool
277
289
278
290
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
+
279
311
class TypeChecker (NodeVisitor [None ], TypeCheckerSharedApi ):
280
312
"""Mypy type checker.
281
313
@@ -401,6 +433,7 @@ def __init__(
401
433
self .is_typeshed_stub = tree .is_typeshed_file (options )
402
434
self .inferred_attribute_types = None
403
435
self .allow_constructor_cache = True
436
+ self .local_type_map = LocalTypeMap (self )
404
437
405
438
# If True, process function definitions. If False, don't. This is used
406
439
# for processing module top levels in fine-grained incremental mode.
@@ -430,6 +463,13 @@ def __init__(
430
463
self ._expr_checker = mypy .checkexpr .ExpressionChecker (
431
464
self , self .msg , self .plugin , per_line_checking_time_ns
432
465
)
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
+
433
473
self .pattern_checker = PatternChecker (self , self .msg , self .plugin , options )
434
474
self ._unique_id = 0
435
475
@@ -703,6 +743,12 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
703
743
# TODO: keep precise type for callables with tricky but valid signatures.
704
744
setter_type = fallback_setter_type
705
745
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
706
752
for i , fdef in enumerate (defn .items ):
707
753
assert isinstance (fdef , Decorator )
708
754
if defn .is_property :
@@ -730,7 +776,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
730
776
assert isinstance (item , Decorator )
731
777
item_type = self .extract_callable_type (item .var .type , item )
732
778
if item_type is not None :
733
- item_type .definition = item . func
779
+ item_type .definition = item
734
780
item_types .append (item_type )
735
781
if item_types :
736
782
defn .type = Overloaded (item_types )
@@ -2501,8 +2547,9 @@ def check_override(
2501
2547
2502
2548
override_ids = override .type_var_ids ()
2503
2549
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
2506
2553
2507
2554
def erase_override (t : Type ) -> Type :
2508
2555
return erase_typevars (t , ids_to_erase = override_ids )
@@ -3509,6 +3556,7 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, rvalue: Expression) ->
3509
3556
continue
3510
3557
3511
3558
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.
3512
3560
custom_setter = is_custom_settable_property (base_node )
3513
3561
if isinstance (base_type , PartialType ):
3514
3562
base_type = None
@@ -4494,6 +4542,8 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None:
4494
4542
if isinstance (p_type , Overloaded ):
4495
4543
# TODO: in theory we can have a property with a deleter only.
4496
4544
var .is_settable_property = True
4545
+ assert isinstance (definition , Decorator ), definition
4546
+ var .setter_type = definition .var .setter_type
4497
4547
4498
4548
def set_inference_error_fallback_type (self , var : Var , lvalue : Lvalue , type : Type ) -> None :
4499
4549
"""Store best known type for variable if type inference failed.
@@ -4613,7 +4663,7 @@ def check_simple_assignment(
4613
4663
# may cause some perf impact, plus we want to partially preserve
4614
4664
# the old behavior. This helps with various practical examples, see
4615
4665
# 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 :
4617
4667
alt_rvalue_type = self .expr_checker .accept (
4618
4668
rvalue , None , always_allow_any = always_allow_any
4619
4669
)
@@ -5356,6 +5406,8 @@ def visit_decorator_inner(
5356
5406
self .check_untyped_after_decorator (sig , e .func )
5357
5407
self .require_correct_self_argument (sig , e .func )
5358
5408
sig = set_callable_name (sig , e .func )
5409
+ if isinstance (sig , CallableType ):
5410
+ sig .definition = e
5359
5411
e .var .type = sig
5360
5412
e .var .is_ready = True
5361
5413
if e .func .is_property :
@@ -6218,21 +6270,26 @@ def find_isinstance_check_helper(
6218
6270
attr = try_getting_str_literals (node .args [1 ], self .lookup_type (node .args [1 ]))
6219
6271
if literal (expr ) == LITERAL_TYPE and attr and len (attr ) == 1 :
6220
6272
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 :
6223
6291
# TODO: Follow *args, **kwargs
6224
6292
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
-
6236
6293
# *assuming* the overloaded function is correct, there's a couple cases:
6237
6294
# 1) The first argument has different names, but is pos-only. We don't
6238
6295
# care about this case, the argument must be passed positionally.
@@ -6245,9 +6302,7 @@ def find_isinstance_check_helper(
6245
6302
# we want the idx-th variable to be narrowed
6246
6303
expr = collapse_walrus (node .args [idx ])
6247
6304
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"
6251
6306
self .fail (
6252
6307
message_registry .TYPE_GUARD_POS_ARG_REQUIRED .format (kind ), node
6253
6308
)
@@ -6258,15 +6313,15 @@ def find_isinstance_check_helper(
6258
6313
# considered "always right" (i.e. even if the types are not overlapping).
6259
6314
# Also note that a care must be taken to unwrap this back at read places
6260
6315
# 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 )}, {}
6263
6318
else :
6264
- assert node . callee . type_is is not None
6319
+ assert type_is is not None
6265
6320
return conditional_types_to_typemaps (
6266
6321
expr ,
6267
6322
* self .conditional_types_with_intersection (
6268
6323
self .lookup_type (expr ),
6269
- [TypeRange (node . callee . type_is , is_upper_bound = False )],
6324
+ [TypeRange (type_is , is_upper_bound = False )],
6270
6325
expr ,
6271
6326
consider_runtime_isinstance = False ,
6272
6327
),
@@ -7353,6 +7408,29 @@ def named_type(self, name: str) -> Instance:
7353
7408
7354
7409
For example, named_type('builtins.object') produces the 'object' type.
7355
7410
"""
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 :
7356
7434
# Assume that the name refers to a type.
7357
7435
sym = self .lookup_qualified (name )
7358
7436
node = sym .node
@@ -7412,18 +7490,6 @@ def lookup_type(self, node: Expression) -> Type:
7412
7490
def store_types (self , d : dict [Expression , Type ]) -> None :
7413
7491
self ._type_maps [- 1 ].update (d )
7414
7492
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
-
7427
7493
def in_checked_function (self ) -> bool :
7428
7494
"""Should we type-check the current function?
7429
7495
@@ -8651,17 +8717,21 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type:
8651
8717
return t .copy_modified (args = [a .accept (self ) for a in t .args ])
8652
8718
8653
8719
8654
- def is_classmethod_node (node : Node | None ) -> bool | None :
8720
+ def is_classmethod_node (node : SymbolNode | None ) -> bool | None :
8655
8721
"""Find out if a node describes a classmethod."""
8722
+ if isinstance (node , Decorator ):
8723
+ node = node .func
8656
8724
if isinstance (node , FuncDef ):
8657
8725
return node .is_class
8658
8726
if isinstance (node , Var ):
8659
8727
return node .is_classmethod
8660
8728
return None
8661
8729
8662
8730
8663
- def is_node_static (node : Node | None ) -> bool | None :
8731
+ def is_node_static (node : SymbolNode | None ) -> bool | None :
8664
8732
"""Find out if a node describes a static function method."""
8733
+ if isinstance (node , Decorator ):
8734
+ node = node .func
8665
8735
if isinstance (node , FuncDef ):
8666
8736
return node .is_static
8667
8737
if isinstance (node , Var ):
0 commit comments