Skip to content

Commit f67f358

Browse files
Merge branch 'master' into for-loop-len-cache
2 parents 5642a6d + c53367f commit f67f358

19 files changed

+553
-134
lines changed

mypy/binder.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ def __init__(self, options: Options) -> None:
138138
# flexible inference of variable types (--allow-redefinition-new).
139139
self.bind_all = options.allow_redefinition_new
140140

141+
# This tracks any externally visible changes in binder to invalidate
142+
# expression caches when needed.
143+
self.version = 0
144+
141145
def _get_id(self) -> int:
142146
self.next_id += 1
143147
return self.next_id
@@ -158,6 +162,7 @@ def push_frame(self, conditional_frame: bool = False) -> Frame:
158162
return f
159163

160164
def _put(self, key: Key, type: Type, from_assignment: bool, index: int = -1) -> None:
165+
self.version += 1
161166
self.frames[index].types[key] = CurrentType(type, from_assignment)
162167

163168
def _get(self, key: Key, index: int = -1) -> CurrentType | None:
@@ -185,6 +190,7 @@ def put(self, expr: Expression, typ: Type, *, from_assignment: bool = True) -> N
185190
self._put(key, typ, from_assignment)
186191

187192
def unreachable(self) -> None:
193+
self.version += 1
188194
self.frames[-1].unreachable = True
189195

190196
def suppress_unreachable_warnings(self) -> None:

mypy/build.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def default_flush_errors(
194194
result.errors = messages
195195
return result
196196
except CompileError as e:
197-
# CompileErrors raised from an errors object carry all of the
197+
# CompileErrors raised from an errors object carry all the
198198
# messages that have not been reported out by error streaming.
199199
# Patch it up to contain either none or all none of the messages,
200200
# depending on whether we are flushing errors.
@@ -802,11 +802,11 @@ def correct_rel_imp(imp: ImportFrom | ImportAll) -> str:
802802
res.append((pri, sub_id, imp.line))
803803
else:
804804
all_are_submodules = False
805-
# Add cur_id as a dependency, even if all of the
805+
# Add cur_id as a dependency, even if all the
806806
# imports are submodules. Processing import from will try
807807
# to look through cur_id, so we should depend on it.
808-
# As a workaround for for some bugs in cycle handling (#4498),
809-
# if all of the imports are submodules, do the import at a lower
808+
# As a workaround for some bugs in cycle handling (#4498),
809+
# if all the imports are submodules, do the import at a lower
810810
# priority.
811811
pri = import_priority(imp, PRI_HIGH if not all_are_submodules else PRI_LOW)
812812
res.append((pri, cur_id, imp.line))
@@ -929,7 +929,7 @@ def write_deps_cache(
929929
) -> None:
930930
"""Write cache files for fine-grained dependencies.
931931
932-
Serialize fine-grained dependencies map for fine grained mode.
932+
Serialize fine-grained dependencies map for fine-grained mode.
933933
934934
Dependencies on some module 'm' is stored in the dependency cache
935935
file m.deps.json. This entails some spooky action at a distance:
@@ -943,7 +943,7 @@ def write_deps_cache(
943943
fine-grained dependencies in a global cache file:
944944
* We take a snapshot of current sources to later check consistency
945945
between the fine-grained dependency cache and module cache metadata
946-
* We store the mtime of all of the dependency files to verify they
946+
* We store the mtime of all the dependency files to verify they
947947
haven't changed
948948
"""
949949
metastore = manager.metastore
@@ -1111,7 +1111,7 @@ def read_deps_cache(manager: BuildManager, graph: Graph) -> dict[str, FgDepMeta]
11111111
if deps_meta is None:
11121112
return None
11131113
meta_snapshot = deps_meta["snapshot"]
1114-
# Take a snapshot of the source hashes from all of the metas we found.
1114+
# Take a snapshot of the source hashes from all the metas we found.
11151115
# (Including the ones we rejected because they were out of date.)
11161116
# We use this to verify that they match up with the proto_deps.
11171117
current_meta_snapshot = {

mypy/checker.py

Lines changed: 74 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,6 @@ def reset(self) -> None:
449449
self.binder = ConditionalTypeBinder(self.options)
450450
self._type_maps[1:] = []
451451
self._type_maps[0].clear()
452-
self.temp_type_map = None
453452
self.expr_checker.reset()
454453
self.deferred_nodes = []
455454
self.partial_types = []
@@ -1374,49 +1373,19 @@ def check_func_def(
13741373
)
13751374

13761375
# Store argument types.
1376+
found_self = False
1377+
if isinstance(defn, FuncDef) and not defn.is_decorated:
1378+
found_self = self.require_correct_self_argument(typ, defn)
13771379
for i in range(len(typ.arg_types)):
13781380
arg_type = typ.arg_types[i]
1379-
if (
1380-
isinstance(defn, FuncDef)
1381-
and ref_type is not None
1382-
and i == 0
1383-
and defn.has_self_or_cls_argument
1384-
and typ.arg_kinds[0] not in [nodes.ARG_STAR, nodes.ARG_STAR2]
1385-
):
1386-
if defn.is_class or defn.name == "__new__":
1387-
ref_type = mypy.types.TypeType.make_normalized(ref_type)
1388-
if not is_same_type(arg_type, ref_type):
1389-
# This level of erasure matches the one in checkmember.check_self_arg(),
1390-
# better keep these two checks consistent.
1391-
erased = get_proper_type(erase_typevars(erase_to_bound(arg_type)))
1392-
if not is_subtype(ref_type, erased, ignore_type_params=True):
1393-
if (
1394-
isinstance(erased, Instance)
1395-
and erased.type.is_protocol
1396-
or isinstance(erased, TypeType)
1397-
and isinstance(erased.item, Instance)
1398-
and erased.item.type.is_protocol
1399-
):
1400-
# We allow the explicit self-type to be not a supertype of
1401-
# the current class if it is a protocol. For such cases
1402-
# the consistency check will be performed at call sites.
1403-
msg = None
1404-
elif typ.arg_names[i] in {"self", "cls"}:
1405-
msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format(
1406-
erased.str_with_options(self.options),
1407-
ref_type.str_with_options(self.options),
1408-
)
1409-
else:
1410-
msg = message_registry.MISSING_OR_INVALID_SELF_TYPE
1411-
if msg:
1412-
self.fail(msg, defn)
1413-
elif isinstance(arg_type, TypeVarType):
1381+
if isinstance(arg_type, TypeVarType):
14141382
# Refuse covariant parameter type variables
14151383
# TODO: check recursively for inner type variables
14161384
if (
14171385
arg_type.variance == COVARIANT
14181386
and defn.name not in ("__init__", "__new__", "__post_init__")
14191387
and not is_private(defn.name) # private methods are not inherited
1388+
and (i != 0 or not found_self)
14201389
):
14211390
ctx: Context = arg_type
14221391
if ctx.line < 0:
@@ -1566,6 +1535,69 @@ def check_func_def(
15661535

15671536
self.binder = old_binder
15681537

1538+
def require_correct_self_argument(self, func: Type, defn: FuncDef) -> bool:
1539+
func = get_proper_type(func)
1540+
if not isinstance(func, CallableType):
1541+
return False
1542+
1543+
# Do not report errors for untyped methods in classes nested in untyped funcs.
1544+
if not (
1545+
self.options.check_untyped_defs
1546+
or len(self.dynamic_funcs) < 2
1547+
or not self.dynamic_funcs[-2]
1548+
or not defn.is_dynamic()
1549+
):
1550+
return bool(func.arg_types)
1551+
1552+
with self.scope.push_function(defn):
1553+
# We temporary push the definition to get the self type as
1554+
# visible from *inside* of this function/method.
1555+
ref_type: Type | None = self.scope.active_self_type()
1556+
if ref_type is None:
1557+
return False
1558+
1559+
if not defn.has_self_or_cls_argument or (
1560+
func.arg_kinds and func.arg_kinds[0] in [nodes.ARG_STAR, nodes.ARG_STAR2]
1561+
):
1562+
return False
1563+
1564+
if not func.arg_types:
1565+
self.fail(
1566+
'Method must have at least one argument. Did you forget the "self" argument?', defn
1567+
)
1568+
return False
1569+
1570+
arg_type = func.arg_types[0]
1571+
if defn.is_class or defn.name == "__new__":
1572+
ref_type = mypy.types.TypeType.make_normalized(ref_type)
1573+
if is_same_type(arg_type, ref_type):
1574+
return True
1575+
1576+
# This level of erasure matches the one in checkmember.check_self_arg(),
1577+
# better keep these two checks consistent.
1578+
erased = get_proper_type(erase_typevars(erase_to_bound(arg_type)))
1579+
if not is_subtype(ref_type, erased, ignore_type_params=True):
1580+
if (
1581+
isinstance(erased, Instance)
1582+
and erased.type.is_protocol
1583+
or isinstance(erased, TypeType)
1584+
and isinstance(erased.item, Instance)
1585+
and erased.item.type.is_protocol
1586+
):
1587+
# We allow the explicit self-type to be not a supertype of
1588+
# the current class if it is a protocol. For such cases
1589+
# the consistency check will be performed at call sites.
1590+
msg = None
1591+
elif func.arg_names[0] in {"self", "cls"}:
1592+
msg = message_registry.ERASED_SELF_TYPE_NOT_SUPERTYPE.format(
1593+
erased.str_with_options(self.options), ref_type.str_with_options(self.options)
1594+
)
1595+
else:
1596+
msg = message_registry.MISSING_OR_INVALID_SELF_TYPE
1597+
if msg:
1598+
self.fail(msg, defn)
1599+
return True
1600+
15691601
def is_var_redefined_in_outer_context(self, v: Var, after_line: int) -> bool:
15701602
"""Can the variable be assigned to at module top level or outer function?
15711603
@@ -3024,6 +3056,8 @@ def visit_block(self, b: Block) -> None:
30243056
break
30253057
else:
30263058
self.accept(s)
3059+
# Clear expression cache after each statement to avoid unlimited growth.
3060+
self.expr_checker.expr_cache.clear()
30273061

30283062
def should_report_unreachable_issues(self) -> bool:
30293063
return (
@@ -4005,7 +4039,7 @@ def check_multi_assignment_from_union(
40054039
for t, lv in zip(transposed, self.flatten_lvalues(lvalues)):
40064040
# We can access _type_maps directly since temporary type maps are
40074041
# only created within expressions.
4008-
t.append(self._type_maps[0].pop(lv, AnyType(TypeOfAny.special_form)))
4042+
t.append(self._type_maps[-1].pop(lv, AnyType(TypeOfAny.special_form)))
40094043
union_types = tuple(make_simplified_union(col) for col in transposed)
40104044
for expr, items in assignments.items():
40114045
# Bind a union of types collected in 'assignments' to every expression.
@@ -4664,6 +4698,8 @@ def replace_partial_type(
46644698
) -> None:
46654699
"""Replace the partial type of var with a non-partial type."""
46664700
var.type = new_type
4701+
# Updating a partial type should invalidate expression caches.
4702+
self.binder.version += 1
46674703
del partial_types[var]
46684704
if self.options.allow_redefinition_new:
46694705
# When using --allow-redefinition-new, binder tracks all types of
@@ -5303,6 +5339,7 @@ def visit_decorator_inner(
53035339
)
53045340
if non_trivial_decorator:
53055341
self.check_untyped_after_decorator(sig, e.func)
5342+
self.require_correct_self_argument(sig, e.func)
53065343
sig = set_callable_name(sig, e.func)
53075344
e.var.type = sig
53085345
e.var.is_ready = True

0 commit comments

Comments
 (0)