blob: 59ecb2bc9993025e435b83ceef15acc5becef859 [file] [log] [blame]
from __future__ import annotations
from mypy.expandtype import expand_type_by_instance
from mypy.nodes import TypeInfo
from mypy.types import AnyType, Instance, TupleType, TypeOfAny, has_type_vars
def map_instance_to_supertype(instance: Instance, superclass: TypeInfo) -> Instance:
"""Produce a supertype of `instance` that is an Instance
of `superclass`, mapping type arguments up the chain of bases.
If `superclass` is not a nominal superclass of `instance.type`,
then all type arguments are mapped to 'Any'.
"""
if instance.type == superclass:
# Fast path: `instance` already belongs to `superclass`.
return instance
if superclass.fullname == "builtins.tuple" and instance.type.tuple_type:
if has_type_vars(instance.type.tuple_type):
# We special case mapping generic tuple types to tuple base, because for
# such tuples fallback can't be calculated before applying type arguments.
alias = instance.type.special_alias
assert alias is not None
if not alias._is_recursive:
# Unfortunately we can't support this for generic recursive tuples.
# If we skip this special casing we will fall back to tuple[Any, ...].
tuple_type = expand_type_by_instance(instance.type.tuple_type, instance)
if isinstance(tuple_type, TupleType):
# Make the import here to avoid cyclic imports.
import mypy.typeops
return mypy.typeops.tuple_fallback(tuple_type)
elif isinstance(tuple_type, Instance):
# This can happen after normalizing variadic tuples.
return tuple_type
if not superclass.type_vars:
# Fast path: `superclass` has no type variables to map to.
return Instance(superclass, [])
return map_instance_to_supertypes(instance, superclass)[0]
def map_instance_to_supertypes(instance: Instance, supertype: TypeInfo) -> list[Instance]:
# FIX: Currently we should only have one supertype per interface, so no
# need to return an array
result: list[Instance] = []
for path in class_derivation_paths(instance.type, supertype):
types = [instance]
for sup in path:
a: list[Instance] = []
for t in types:
a.extend(map_instance_to_direct_supertypes(t, sup))
types = a
result.extend(types)
if result:
return result
else:
# Nothing. Presumably due to an error. Construct a dummy using Any.
any_type = AnyType(TypeOfAny.from_error)
return [Instance(supertype, [any_type] * len(supertype.type_vars))]
def class_derivation_paths(typ: TypeInfo, supertype: TypeInfo) -> list[list[TypeInfo]]:
"""Return an array of non-empty paths of direct base classes from
type to supertype. Return [] if no such path could be found.
InterfaceImplementationPaths(A, B) == [[B]] if A inherits B
InterfaceImplementationPaths(A, C) == [[B, C]] if A inherits B and
B inherits C
"""
# FIX: Currently we might only ever have a single path, so this could be
# simplified
result: list[list[TypeInfo]] = []
for base in typ.bases:
btype = base.type
if btype == supertype:
result.append([btype])
else:
# Try constructing a longer path via the base class.
for path in class_derivation_paths(btype, supertype):
result.append([btype] + path)
return result
def map_instance_to_direct_supertypes(instance: Instance, supertype: TypeInfo) -> list[Instance]:
# FIX: There should only be one supertypes, always.
typ = instance.type
result: list[Instance] = []
for b in typ.bases:
if b.type == supertype:
t = expand_type_by_instance(b, instance)
assert isinstance(t, Instance)
result.append(t)
if result:
return result
else:
# Relationship with the supertype not specified explicitly. Use dynamic
# type arguments implicitly.
any_type = AnyType(TypeOfAny.unannotated)
return [Instance(supertype, [any_type] * len(supertype.type_vars))]