Skip to content

Commit c9f8be2

Browse files
Merge branch 'master' into stararg-fastpath
2 parents f54394b + fb41108 commit c9f8be2

40 files changed

+1215
-272
lines changed

mypy/checker.py

Lines changed: 109 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,18 @@
66
from collections import defaultdict
77
from collections.abc import Iterable, Iterator, Mapping, Sequence, Set as AbstractSet
88
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+
)
1021
from typing_extensions import TypeAlias as _TypeAlias, TypeGuard
1122

1223
import mypy.checkexpr
@@ -130,6 +141,7 @@
130141
WhileStmt,
131142
WithStmt,
132143
YieldExpr,
144+
get_func_def,
133145
is_final_node,
134146
)
135147
from 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+
279311
class 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

Comments
 (0)