Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions mypy/mixedtraverser.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
TypeApplication,
TypedDictExpr,
TypeFormExpr,
TypeInfo,
TypeVarExpr,
Var,
WithStmt,
Expand All @@ -41,15 +42,29 @@ def visit_func(self, o: FuncItem, /) -> None:
self.visit_optional_type(o.type)

def visit_class_def(self, o: ClassDef, /) -> None:
# TODO: Should we visit generated methods/variables as well, either here or in
# TraverserVisitor?
super().visit_class_def(o)
info = o.info
if info:
for base in info.bases:
base.accept(self)
if info.special_alias:
info.special_alias.accept(self)
if o.info:
self.process_type_info(o.info)

def process_type_info(self, info: TypeInfo) -> None:
# TODO: Should we visit generated methods/variables as well?
# We should for methods generated by us (see below). But it is less clear for
# 3rd party plugin generated methods (since we don't want to emit errors there).
for base in info.bases:
base.accept(self)
if info.special_alias:
# We need to accept all types that are conceptually identical like special
# alias target and corresponding tuple_type or typeddict_type, since those
# may be copies, and not the same object.
info.special_alias.accept(self)
if info.tuple_type:
info.tuple_type.accept(self)
if info.typeddict_type:
info.typeddict_type.accept(self)
if info.is_named_tuple or info.is_newtype:
for sym in info.names.values():
if sym.plugin_generated and sym.node:
sym.node.accept(self)

def visit_type_alias_expr(self, o: TypeAliasExpr, /) -> None:
super().visit_type_alias_expr(o)
Expand All @@ -64,19 +79,20 @@ def visit_type_var_expr(self, o: TypeVarExpr, /) -> None:

def visit_typeddict_expr(self, o: TypedDictExpr, /) -> None:
super().visit_typeddict_expr(o)
self.visit_optional_type(o.info.typeddict_type)
self.process_type_info(o.info)

def visit_namedtuple_expr(self, o: NamedTupleExpr, /) -> None:
super().visit_namedtuple_expr(o)
assert o.info.tuple_type
o.info.tuple_type.accept(self)
self.process_type_info(o.info)

def visit__promote_expr(self, o: PromoteExpr, /) -> None:
super().visit__promote_expr(o)
o.type.accept(self)

def visit_newtype_expr(self, o: NewTypeExpr, /) -> None:
super().visit_newtype_expr(o)
if o.info:
self.process_type_info(o.info)
self.visit_optional_type(o.old_type)

# Statements
Expand Down
6 changes: 5 additions & 1 deletion mypy/typetraverser.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ def visit_instance(self, t: Instance, /) -> None:
self.traverse_type_tuple(t.args)

def visit_callable_type(self, t: CallableType, /) -> None:
# FIX generics
for tv in t.variables:
tv.upper_bound.accept(self)
if isinstance(tv, TypeVarType):
for v in tv.values:
v.accept(self)
self.traverse_type_list(t.arg_types)
t.ret_type.accept(self)
t.fallback.accept(self)
Expand Down
24 changes: 24 additions & 0 deletions test-data/unit/check-python312.test
Original file line number Diff line number Diff line change
Expand Up @@ -2293,3 +2293,27 @@ reveal_type(x) # N: Revealed type is "__main__.C[()]"
y: T[bool]
reveal_type(y) # N: Revealed type is "__main__.C[()]"
[builtins fixtures/tuple.pyi]

[case testTupleBaseTupleTypeUnpack]
class X(tuple[*tuple[int], *tuple[int]]):
pass
x = X((1, 2))
reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.X]"
[builtins fixtures/tuple.pyi]

[case testNewTypeTupleTypeUnpack]
from typing import NewType

T = NewType("T", tuple[*tuple[int], *tuple[int]])
t = T((1, 2))
reveal_type(t) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.T]"
[builtins fixtures/tuple.pyi]

[case testNamedTupleTupleTypeUnpack]
from typing import NamedTuple

class N(NamedTuple):
x: tuple[*tuple[int], *tuple[int]]
n = N((1, 2))
reveal_type(n) # N: Revealed type is "tuple[tuple[builtins.int, builtins.int], fallback=__main__.N]"
[builtins fixtures/tuple.pyi]
Loading