blob: cc3158552d7825e590c8b386f69fe4a49d0f7958 [file] [log] [blame]
"""Expression type checker. This file is conceptually part of TypeChecker."""
from typing import cast, Dict, Set, List, Iterable, Tuple, Callable, Union, Optional
from mypy.types import (
Type, AnyType, CallableType, Overloaded, NoneTyp, Void, TypeVarDef,
TupleType, Instance, TypeVarId, TypeVarType, ErasedType, UnionType,
PartialType, DeletedType, UnboundType, UninhabitedType, TypeType,
true_only, false_only, is_named_instance
)
from mypy.nodes import (
NameExpr, RefExpr, Var, FuncDef, OverloadedFuncDef, TypeInfo, CallExpr,
Node, MemberExpr, IntExpr, StrExpr, BytesExpr, UnicodeExpr, FloatExpr,
OpExpr, UnaryExpr, IndexExpr, CastExpr, RevealTypeExpr, TypeApplication, ListExpr,
TupleExpr, DictExpr, FuncExpr, SuperExpr, SliceExpr, Context, Expression,
ListComprehension, GeneratorExpr, SetExpr, MypyFile, Decorator,
ConditionalExpr, ComparisonExpr, TempNode, SetComprehension,
DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr,
TypeAliasExpr, BackquoteExpr, ARG_POS, ARG_NAMED, ARG_STAR2, MODULE_REF,
)
from mypy.nodes import function_type
from mypy import nodes
import mypy.checker
from mypy import types
from mypy.sametypes import is_same_type
from mypy.erasetype import replace_meta_vars
from mypy.messages import MessageBuilder
from mypy import messages
from mypy.infer import infer_type_arguments, infer_function_type_arguments
from mypy import join
from mypy.subtypes import is_subtype, is_equivalent
from mypy import applytype
from mypy import erasetype
from mypy.checkmember import analyze_member_access, type_object_type
from mypy.semanal import self_type
from mypy.constraints import get_actual_type
from mypy.checkstrformat import StringFormatterChecker
from mypy.expandtype import expand_type
from mypy.util import split_module_names
from mypy import experiments
# Type of callback user for checking individual function arguments. See
# check_args() below for details.
ArgChecker = Callable[[Type, Type, int, Type, int, int, CallableType, Context, MessageBuilder],
None]
def extract_refexpr_names(expr: RefExpr) -> Set[str]:
"""Recursively extracts all module references from a reference expression.
Note that currently, the only two subclasses of RefExpr are NameExpr and
MemberExpr."""
output = set() # type: Set[str]
while expr.kind == MODULE_REF or expr.fullname is not None:
if expr.kind == MODULE_REF and expr.fullname is not None:
# If it's None, something's wrong (perhaps due to an
# import cycle or a supressed error). For now we just
# skip it.
output.add(expr.fullname)
if isinstance(expr, NameExpr):
is_suppressed_import = isinstance(expr.node, Var) and expr.node.is_suppressed_import
if isinstance(expr.node, TypeInfo):
# Reference to a class or a nested class
output.update(split_module_names(expr.node.module_name))
elif expr.fullname is not None and '.' in expr.fullname and not is_suppressed_import:
# Everything else (that is not a silenced import within a class)
output.add(expr.fullname.rsplit('.', 1)[0])
break
elif isinstance(expr, MemberExpr):
if isinstance(expr.expr, RefExpr):
expr = expr.expr
else:
break
else:
raise AssertionError("Unknown RefExpr subclass: {}".format(type(expr)))
return output
class Finished(Exception):
"""Raised if we can terminate overload argument check early (no match)."""
class ExpressionChecker:
"""Expression type checker.
This class works closely together with checker.TypeChecker.
"""
# Some services are provided by a TypeChecker instance.
chk = None # type: mypy.checker.TypeChecker
# This is shared with TypeChecker, but stored also here for convenience.
msg = None # type: MessageBuilder
strfrm_checker = None # type: StringFormatterChecker
def __init__(self,
chk: 'mypy.checker.TypeChecker',
msg: MessageBuilder) -> None:
"""Construct an expression type checker."""
self.chk = chk
self.msg = msg
self.strfrm_checker = StringFormatterChecker(self, self.chk, self.msg)
def visit_name_expr(self, e: NameExpr) -> Type:
"""Type check a name expression.
It can be of any kind: local, member or global.
"""
self.chk.module_refs.update(extract_refexpr_names(e))
result = self.analyze_ref_expr(e)
return self.chk.narrow_type_from_binder(e, result)
def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
result = None # type: Type
node = e.node
if isinstance(node, Var):
# Variable reference.
result = self.analyze_var_ref(node, e)
if isinstance(result, PartialType):
if result.type is None:
# 'None' partial type. It has a well-defined type. In an lvalue context
# we want to preserve the knowledge of it being a partial type.
if not lvalue:
result = NoneTyp()
else:
partial_types = self.chk.find_partial_types(node)
if partial_types is not None and not self.chk.current_node_deferred:
context = partial_types[node]
self.msg.fail(messages.NEED_ANNOTATION_FOR_VAR, context)
result = AnyType()
elif isinstance(node, FuncDef):
# Reference to a global function.
result = function_type(node, self.named_type('builtins.function'))
elif isinstance(node, OverloadedFuncDef):
result = node.type
elif isinstance(node, TypeInfo):
# Reference to a type object.
result = type_object_type(node, self.named_type)
elif isinstance(node, MypyFile):
# Reference to a module object.
result = self.named_type('builtins.module')
elif isinstance(node, Decorator):
result = self.analyze_var_ref(node.var, e)
else:
# Unknown reference; use any type implicitly to avoid
# generating extra type errors.
result = AnyType()
return result
def analyze_var_ref(self, var: Var, context: Context) -> Type:
if not var.type:
if not var.is_ready and self.chk.in_checked_function():
self.chk.handle_cannot_determine_type(var.name(), context)
# Implicit 'Any' type.
return AnyType()
else:
# Look up local type of variable with type (inferred or explicit).
val = self.chk.binder.get(var)
if val is None:
return var.type
else:
return val
def visit_call_expr(self, e: CallExpr) -> Type:
"""Type check a call expression."""
if e.analyzed:
# It's really a special form that only looks like a call.
return self.accept(e.analyzed, self.chk.type_context[-1])
self.try_infer_partial_type(e)
callee_type = self.accept(e.callee)
if (self.chk.options.disallow_untyped_calls and
self.chk.in_checked_function() and
isinstance(callee_type, CallableType)
and callee_type.implicit):
return self.msg.untyped_function_call(callee_type, e)
return self.check_call_expr_with_callee_type(callee_type, e)
# Types and methods that can be used to infer partial types.
item_args = {'builtins.list': ['append'],
'builtins.set': ['add', 'discard'],
}
container_args = {'builtins.list': {'extend': ['builtins.list']},
'builtins.dict': {'update': ['builtins.dict']},
'builtins.set': {'update': ['builtins.set', 'builtins.list']},
}
def try_infer_partial_type(self, e: CallExpr) -> None:
if isinstance(e.callee, MemberExpr) and isinstance(e.callee.expr, RefExpr):
var = cast(Var, e.callee.expr.node)
partial_types = self.chk.find_partial_types(var)
if partial_types is not None and not self.chk.current_node_deferred:
partial_type = var.type
if (partial_type is None or
not isinstance(partial_type, PartialType) or
partial_type.type is None):
# A partial None type -> can't infer anything.
return
typename = partial_type.type.fullname()
methodname = e.callee.name
# Sometimes we can infer a full type for a partial List, Dict or Set type.
# TODO: Don't infer argument expression twice.
if (typename in self.item_args and methodname in self.item_args[typename]
and e.arg_kinds == [ARG_POS]):
item_type = self.accept(e.args[0])
full_item_type = UnionType.make_simplified_union(
[item_type, partial_type.inner_types[0]])
if mypy.checker.is_valid_inferred_type(full_item_type):
var.type = self.chk.named_generic_type(typename, [full_item_type])
del partial_types[var]
elif (typename in self.container_args
and methodname in self.container_args[typename]
and e.arg_kinds == [ARG_POS]):
arg_type = self.accept(e.args[0])
if isinstance(arg_type, Instance):
arg_typename = arg_type.type.fullname()
if arg_typename in self.container_args[typename][methodname]:
full_item_types = [
UnionType.make_simplified_union([item_type, prev_type])
for item_type, prev_type
in zip(arg_type.args, partial_type.inner_types)
]
if all(mypy.checker.is_valid_inferred_type(item_type)
for item_type in full_item_types):
var.type = self.chk.named_generic_type(typename,
list(full_item_types))
del partial_types[var]
def check_call_expr_with_callee_type(self, callee_type: Type,
e: CallExpr) -> Type:
"""Type check call expression.
The given callee type overrides the type of the callee
expression.
"""
return self.check_call(callee_type, e.args, e.arg_kinds, e,
e.arg_names, callable_node=e.callee)[0]
def check_call(self, callee: Type, args: List[Expression],
arg_kinds: List[int], context: Context,
arg_names: List[str] = None,
callable_node: Expression = None,
arg_messages: MessageBuilder = None) -> Tuple[Type, Type]:
"""Type check a call.
Also infer type arguments if the callee is a generic function.
Return (result type, inferred callee type).
Arguments:
callee: type of the called value
args: actual argument expressions
arg_kinds: contains nodes.ARG_* constant for each argument in args
describing whether the argument is positional, *arg, etc.
arg_names: names of arguments (optional)
callable_node: associate the inferred callable type to this node,
if specified
arg_messages: TODO
"""
arg_messages = arg_messages or self.msg
if isinstance(callee, CallableType):
if callee.is_concrete_type_obj() and callee.type_object().is_abstract:
type = callee.type_object()
self.msg.cannot_instantiate_abstract_class(
callee.type_object().name(), type.abstract_attributes,
context)
formal_to_actual = map_actuals_to_formals(
arg_kinds, arg_names,
callee.arg_kinds, callee.arg_names,
lambda i: self.accept(args[i]))
if callee.is_generic():
callee = freshen_generic_callable(callee)
callee = self.infer_function_type_arguments_using_context(
callee, context)
callee = self.infer_function_type_arguments(
callee, args, arg_kinds, formal_to_actual, context)
arg_types = self.infer_arg_types_in_context2(
callee, args, arg_kinds, formal_to_actual)
self.check_argument_count(callee, arg_types, arg_kinds,
arg_names, formal_to_actual, context, self.msg)
self.check_argument_types(arg_types, arg_kinds, callee,
formal_to_actual, context,
messages=arg_messages)
if (callee.is_type_obj() and (len(arg_types) == 1)
and is_equivalent(callee.ret_type, self.named_type('builtins.type'))):
callee = callee.copy_modified(ret_type=TypeType(arg_types[0]))
if callable_node:
# Store the inferred callable type.
self.chk.store_type(callable_node, callee)
return callee.ret_type, callee
elif isinstance(callee, Overloaded):
# Type check arguments in empty context. They will be checked again
# later in a context derived from the signature; these types are
# only used to pick a signature variant.
self.msg.disable_errors()
arg_types = self.infer_arg_types_in_context(None, args)
self.msg.enable_errors()
target = self.overload_call_target(arg_types, arg_kinds, arg_names,
callee, context,
messages=arg_messages)
return self.check_call(target, args, arg_kinds, context, arg_names,
arg_messages=arg_messages)
elif isinstance(callee, AnyType) or not self.chk.in_checked_function():
self.infer_arg_types_in_context(None, args)
return AnyType(), AnyType()
elif isinstance(callee, UnionType):
self.msg.disable_type_names += 1
results = [self.check_call(subtype, args, arg_kinds, context, arg_names,
arg_messages=arg_messages)
for subtype in callee.items]
self.msg.disable_type_names -= 1
return (UnionType.make_simplified_union([res[0] for res in results]),
callee)
elif isinstance(callee, Instance):
call_function = analyze_member_access('__call__', callee, context,
False, False, False, self.named_type,
self.not_ready_callback, self.msg, chk=self.chk)
return self.check_call(call_function, args, arg_kinds, context, arg_names,
callable_node, arg_messages)
elif isinstance(callee, TypeVarType):
return self.check_call(callee.upper_bound, args, arg_kinds, context, arg_names,
callable_node, arg_messages)
elif isinstance(callee, TypeType):
# Pass the original Type[] as context since that's where errors should go.
item = self.analyze_type_type_callee(callee.item, callee)
return self.check_call(item, args, arg_kinds, context, arg_names,
callable_node, arg_messages)
else:
return self.msg.not_callable(callee, context), AnyType()
def analyze_type_type_callee(self, item: Type, context: Context) -> Type:
"""Analyze the callee X in X(...) where X is Type[item].
Return a Y that we can pass to check_call(Y, ...).
"""
if isinstance(item, AnyType):
return AnyType()
if isinstance(item, Instance):
return type_object_type(item.type, self.named_type)
if isinstance(item, UnionType):
return UnionType([self.analyze_type_type_callee(item, context)
for item in item.items], item.line)
if isinstance(item, TypeVarType):
# Pretend we're calling the typevar's upper bound,
# i.e. its constructor (a poor approximation for reality,
# but better than AnyType...), but replace the return type
# with typevar.
callee = self.analyze_type_type_callee(item.upper_bound, context)
if isinstance(callee, CallableType):
if callee.is_generic():
callee = None
else:
callee = callee.copy_modified(ret_type=item)
elif isinstance(callee, Overloaded):
if callee.items()[0].is_generic():
callee = None
else:
callee = Overloaded([c.copy_modified(ret_type=item)
for c in callee.items()])
if callee:
return callee
self.msg.unsupported_type_type(item, context)
return AnyType()
def infer_arg_types_in_context(self, callee: Optional[CallableType],
args: List[Expression]) -> List[Type]:
"""Infer argument expression types using a callable type as context.
For example, if callee argument 2 has type List[int], infer the
argument expression with List[int] type context.
"""
# TODO Always called with callee as None, i.e. empty context.
res = [] # type: List[Type]
fixed = len(args)
if callee:
fixed = min(fixed, callee.max_fixed_args())
arg_type = None # type: Type
ctx = None # type: Type
for i, arg in enumerate(args):
if i < fixed:
if callee and i < len(callee.arg_types):
ctx = callee.arg_types[i]
arg_type = self.accept(arg, ctx)
else:
if callee and callee.is_var_arg:
arg_type = self.accept(arg, callee.arg_types[-1])
else:
arg_type = self.accept(arg)
if has_erased_component(arg_type):
res.append(NoneTyp())
else:
res.append(arg_type)
return res
def infer_arg_types_in_context2(
self, callee: CallableType, args: List[Expression], arg_kinds: List[int],
formal_to_actual: List[List[int]]) -> List[Type]:
"""Infer argument expression types using a callable type as context.
For example, if callee argument 2 has type List[int], infer the
argument expression with List[int] type context.
Returns the inferred types of *actual arguments*.
"""
res = [None] * len(args) # type: List[Type]
for i, actuals in enumerate(formal_to_actual):
for ai in actuals:
if arg_kinds[ai] not in (nodes.ARG_STAR, nodes.ARG_STAR2):
res[ai] = self.accept(args[ai], callee.arg_types[i])
# Fill in the rest of the argument types.
for i, t in enumerate(res):
if not t:
res[i] = self.accept(args[i])
return res
def infer_function_type_arguments_using_context(
self, callable: CallableType, error_context: Context) -> CallableType:
"""Unify callable return type to type context to infer type vars.
For example, if the return type is set[t] where 't' is a type variable
of callable, and if the context is set[int], return callable modified
by substituting 't' with 'int'.
"""
ctx = self.chk.type_context[-1]
if not ctx:
return callable
# The return type may have references to type metavariables that
# we are inferring right now. We must consider them as indeterminate
# and they are not potential results; thus we replace them with the
# special ErasedType type. On the other hand, class type variables are
# valid results.
erased_ctx = replace_meta_vars(ctx, ErasedType())
ret_type = callable.ret_type
if isinstance(ret_type, TypeVarType):
if ret_type.values or (not isinstance(ctx, Instance) or
not ctx.args):
# The return type is a type variable. If it has values, we can't easily restrict
# type inference to conform to the valid values. If it's unrestricted, we could
# infer a too general type for the type variable if we use context, and this could
# result in confusing and spurious type errors elsewhere.
#
# Give up and just use function arguments for type inference. As an exception,
# if the context is a generic instance type, actually use it as context, as
# this *seems* to usually be the reasonable thing to do.
#
# See also github issues #462 and #360.
ret_type = NoneTyp()
args = infer_type_arguments(callable.type_var_ids(), ret_type, erased_ctx)
# Only substitute non-None and non-erased types.
new_args = [] # type: List[Type]
for arg in args:
if isinstance(arg, (NoneTyp, UninhabitedType)) or has_erased_component(arg):
new_args.append(None)
else:
new_args.append(arg)
return cast(CallableType, self.apply_generic_arguments(callable, new_args,
error_context))
def infer_function_type_arguments(self, callee_type: CallableType,
args: List[Expression],
arg_kinds: List[int],
formal_to_actual: List[List[int]],
context: Context) -> CallableType:
"""Infer the type arguments for a generic callee type.
Infer based on the types of arguments.
Return a derived callable type that has the arguments applied.
"""
if self.chk.in_checked_function():
# Disable type errors during type inference. There may be errors
# due to partial available context information at this time, but
# these errors can be safely ignored as the arguments will be
# inferred again later.
self.msg.disable_errors()
arg_types = self.infer_arg_types_in_context2(
callee_type, args, arg_kinds, formal_to_actual)
self.msg.enable_errors()
arg_pass_nums = self.get_arg_infer_passes(
callee_type.arg_types, formal_to_actual, len(args))
pass1_args = [] # type: List[Optional[Type]]
for i, arg in enumerate(arg_types):
if arg_pass_nums[i] > 1:
pass1_args.append(None)
else:
pass1_args.append(arg)
inferred_args = infer_function_type_arguments(
callee_type, pass1_args, arg_kinds, formal_to_actual,
strict=self.chk.in_checked_function()) # type: List[Type]
if 2 in arg_pass_nums:
# Second pass of type inference.
(callee_type,
inferred_args) = self.infer_function_type_arguments_pass2(
callee_type, args, arg_kinds, formal_to_actual,
inferred_args, context)
if callee_type.special_sig == 'dict' and len(inferred_args) == 2 and (
ARG_NAMED in arg_kinds or ARG_STAR2 in arg_kinds):
# HACK: Infer str key type for dict(...) with keyword args. The type system
# can't represent this so we special case it, as this is a pretty common
# thing. This doesn't quite work with all possible subclasses of dict
# if they shuffle type variables around, as we assume that there is a 1-1
# correspondence with dict type variables. This is a marginal issue and
# a little tricky to fix so it's left unfixed for now.
if isinstance(inferred_args[0], (NoneTyp, UninhabitedType)):
inferred_args[0] = self.named_type('builtins.str')
elif not is_subtype(self.named_type('builtins.str'), inferred_args[0]):
self.msg.fail(messages.KEYWORD_ARGUMENT_REQUIRES_STR_KEY_TYPE,
context)
else:
# In dynamically typed functions use implicit 'Any' types for
# type variables.
inferred_args = [AnyType()] * len(callee_type.variables)
return self.apply_inferred_arguments(callee_type, inferred_args,
context)
def infer_function_type_arguments_pass2(
self, callee_type: CallableType,
args: List[Expression],
arg_kinds: List[int],
formal_to_actual: List[List[int]],
inferred_args: List[Type],
context: Context) -> Tuple[CallableType, List[Type]]:
"""Perform second pass of generic function type argument inference.
The second pass is needed for arguments with types such as Callable[[T], S],
where both T and S are type variables, when the actual argument is a
lambda with inferred types. The idea is to infer the type variable T
in the first pass (based on the types of other arguments). This lets
us infer the argument and return type of the lambda expression and
thus also the type variable S in this second pass.
Return (the callee with type vars applied, inferred actual arg types).
"""
# None or erased types in inferred types mean that there was not enough
# information to infer the argument. Replace them with None values so
# that they are not applied yet below.
for i, arg in enumerate(inferred_args):
if isinstance(arg, (NoneTyp, UninhabitedType)) or has_erased_component(arg):
inferred_args[i] = None
callee_type = cast(CallableType, self.apply_generic_arguments(
callee_type, inferred_args, context))
arg_types = self.infer_arg_types_in_context2(
callee_type, args, arg_kinds, formal_to_actual)
inferred_args = infer_function_type_arguments(
callee_type, arg_types, arg_kinds, formal_to_actual)
return callee_type, inferred_args
def get_arg_infer_passes(self, arg_types: List[Type],
formal_to_actual: List[List[int]],
num_actuals: int) -> List[int]:
"""Return pass numbers for args for two-pass argument type inference.
For each actual, the pass number is either 1 (first pass) or 2 (second
pass).
Two-pass argument type inference primarily lets us infer types of
lambdas more effectively.
"""
res = [1] * num_actuals
for i, arg in enumerate(arg_types):
if arg.accept(ArgInferSecondPassQuery()):
for j in formal_to_actual[i]:
res[j] = 2
return res
def apply_inferred_arguments(self, callee_type: CallableType,
inferred_args: List[Type],
context: Context) -> CallableType:
"""Apply inferred values of type arguments to a generic function.
Inferred_args contains the values of function type arguments.
"""
# Report error if some of the variables could not be solved. In that
# case assume that all variables have type Any to avoid extra
# bogus error messages.
for i, inferred_type in enumerate(inferred_args):
if not inferred_type or has_erased_component(inferred_type):
# Could not infer a non-trivial type for a type variable.
self.msg.could_not_infer_type_arguments(
callee_type, i + 1, context)
inferred_args = [AnyType()] * len(inferred_args)
# Apply the inferred types to the function type. In this case the
# return type must be CallableType, since we give the right number of type
# arguments.
return cast(CallableType, self.apply_generic_arguments(callee_type,
inferred_args, context))
def check_argument_count(self, callee: CallableType, actual_types: List[Type],
actual_kinds: List[int], actual_names: List[str],
formal_to_actual: List[List[int]],
context: Context,
messages: Optional[MessageBuilder]) -> bool:
"""Check that there is a value for all required arguments to a function.
Also check that there are no duplicate values for arguments. Report found errors
using 'messages' if it's not None.
Return False if there were any errors. Otherwise return True
"""
# TODO(jukka): We could return as soon as we find an error if messages is None.
formal_kinds = callee.arg_kinds
# Collect list of all actual arguments matched to formal arguments.
all_actuals = [] # type: List[int]
for actuals in formal_to_actual:
all_actuals.extend(actuals)
is_unexpected_arg_error = False # Keep track of errors to avoid duplicate errors.
ok = True # False if we've found any error.
for i, kind in enumerate(actual_kinds):
if i not in all_actuals and (
kind != nodes.ARG_STAR or
not is_empty_tuple(actual_types[i])):
# Extra actual: not matched by a formal argument.
ok = False
if kind != nodes.ARG_NAMED:
if messages:
messages.too_many_arguments(callee, context)
else:
if messages:
messages.unexpected_keyword_argument(
callee, actual_names[i], context)
is_unexpected_arg_error = True
elif kind == nodes.ARG_STAR and (
nodes.ARG_STAR not in formal_kinds):
actual_type = actual_types[i]
if isinstance(actual_type, TupleType):
if all_actuals.count(i) < len(actual_type.items):
# Too many tuple items as some did not match.
if messages:
messages.too_many_arguments(callee, context)
ok = False
# *args can be applied even if the function takes a fixed
# number of positional arguments. This may succeed at runtime.
for i, kind in enumerate(formal_kinds):
if kind == nodes.ARG_POS and (not formal_to_actual[i] and
not is_unexpected_arg_error):
# No actual for a mandatory positional formal.
if messages:
messages.too_few_arguments(callee, context, actual_names)
ok = False
elif kind in [nodes.ARG_POS, nodes.ARG_OPT,
nodes.ARG_NAMED] and is_duplicate_mapping(
formal_to_actual[i], actual_kinds):
if (self.chk.in_checked_function() or
isinstance(actual_types[formal_to_actual[i][0]], TupleType)):
if messages:
messages.duplicate_argument_value(callee, i, context)
ok = False
elif (kind == nodes.ARG_NAMED and formal_to_actual[i] and
actual_kinds[formal_to_actual[i][0]] not in [nodes.ARG_NAMED, nodes.ARG_STAR2]):
# Positional argument when expecting a keyword argument.
if messages:
messages.too_many_positional_arguments(callee, context)
ok = False
return ok
def check_argument_types(self, arg_types: List[Type], arg_kinds: List[int],
callee: CallableType,
formal_to_actual: List[List[int]],
context: Context,
messages: MessageBuilder = None,
check_arg: ArgChecker = None) -> None:
"""Check argument types against a callable type.
Report errors if the argument types are not compatible.
"""
messages = messages or self.msg
check_arg = check_arg or self.check_arg
# Keep track of consumed tuple *arg items.
tuple_counter = [0]
for i, actuals in enumerate(formal_to_actual):
for actual in actuals:
arg_type = arg_types[actual]
if arg_type is None:
continue # Some kind of error was already reported.
# Check that a *arg is valid as varargs.
if (arg_kinds[actual] == nodes.ARG_STAR and
not self.is_valid_var_arg(arg_type)):
messages.invalid_var_arg(arg_type, context)
if (arg_kinds[actual] == nodes.ARG_STAR2 and
not self.is_valid_keyword_var_arg(arg_type)):
messages.invalid_keyword_var_arg(arg_type, context)
# Get the type of an individual actual argument (for *args
# and **args this is the item type, not the collection type).
if (isinstance(arg_type, TupleType)
and tuple_counter[0] >= len(arg_type.items)
and arg_kinds[actual] == nodes.ARG_STAR):
# The tuple is exhausted. Continue with further arguments.
continue
actual_type = get_actual_type(arg_type, arg_kinds[actual],
tuple_counter)
check_arg(actual_type, arg_type, arg_kinds[actual],
callee.arg_types[i],
actual + 1, i + 1, callee, context, messages)
# There may be some remaining tuple varargs items that haven't
# been checked yet. Handle them.
if (callee.arg_kinds[i] == nodes.ARG_STAR and
arg_kinds[actual] == nodes.ARG_STAR and
isinstance(arg_types[actual], TupleType)):
tuplet = cast(TupleType, arg_types[actual])
while tuple_counter[0] < len(tuplet.items):
actual_type = get_actual_type(arg_type,
arg_kinds[actual],
tuple_counter)
check_arg(actual_type, arg_type, arg_kinds[actual],
callee.arg_types[i],
actual + 1, i + 1, callee, context, messages)
def check_arg(self, caller_type: Type, original_caller_type: Type,
caller_kind: int,
callee_type: Type, n: int, m: int, callee: CallableType,
context: Context, messages: MessageBuilder) -> None:
"""Check the type of a single argument in a call."""
if self.chk.is_unusable_type(caller_type):
messages.does_not_return_value(caller_type, context)
elif isinstance(caller_type, DeletedType):
messages.deleted_as_rvalue(caller_type, context)
elif not is_subtype(caller_type, callee_type):
if self.chk.should_suppress_optional_error([caller_type, callee_type]):
return
messages.incompatible_argument(n, m, callee, original_caller_type,
caller_kind, context)
def overload_call_target(self, arg_types: List[Type], arg_kinds: List[int],
arg_names: List[str],
overload: Overloaded, context: Context,
messages: MessageBuilder = None) -> Type:
"""Infer the correct overload item to call with given argument types.
The return value may be CallableType or AnyType (if an unique item
could not be determined).
"""
messages = messages or self.msg
# TODO: For overlapping signatures we should try to get a more precise
# result than 'Any'.
match = [] # type: List[CallableType]
best_match = 0
for typ in overload.items():
similarity = self.erased_signature_similarity(arg_types, arg_kinds, arg_names,
typ, context=context)
if similarity > 0 and similarity >= best_match:
if (match and not is_same_type(match[-1].ret_type,
typ.ret_type) and
not mypy.checker.is_more_precise_signature(
match[-1], typ)):
# Ambiguous return type. Either the function overload is
# overlapping (which we don't handle very well here) or the
# caller has provided some Any argument types; in either
# case we'll fall back to Any. It's okay to use Any types
# in calls.
#
# Overlapping overload items are generally fine if the
# overlapping is only possible when there is multiple
# inheritance, as this is rare. See docstring of
# mypy.meet.is_overlapping_types for more about this.
#
# Note that there is no ambiguity if the items are
# covariant in both argument types and return types with
# respect to type precision. We'll pick the best/closest
# match.
#
# TODO: Consider returning a union type instead if the
# overlapping is NOT due to Any types?
return AnyType()
else:
match.append(typ)
best_match = max(best_match, similarity)
if not match:
if not self.chk.should_suppress_optional_error(arg_types):
messages.no_variant_matches_arguments(overload, arg_types, context)
return AnyType()
else:
if len(match) == 1:
return match[0]
else:
# More than one signature matches. Pick the first *non-erased*
# matching signature, or default to the first one if none
# match.
for m in match:
if self.match_signature_types(arg_types, arg_kinds, arg_names, m,
context=context):
return m
return match[0]
def erased_signature_similarity(self, arg_types: List[Type], arg_kinds: List[int],
arg_names: List[str], callee: CallableType,
context: Context) -> int:
"""Determine whether arguments could match the signature at runtime.
Return similarity level (0 = no match, 1 = can match, 2 = non-promotion match). See
overload_arg_similarity for a discussion of similarity levels.
"""
formal_to_actual = map_actuals_to_formals(arg_kinds,
arg_names,
callee.arg_kinds,
callee.arg_names,
lambda i: arg_types[i])
if not self.check_argument_count(callee, arg_types, arg_kinds, arg_names,
formal_to_actual, None, None):
# Too few or many arguments -> no match.
return 0
similarity = 2
def check_arg(caller_type: Type, original_caller_type: Type, caller_kind: int,
callee_type: Type, n: int, m: int, callee: CallableType,
context: Context, messages: MessageBuilder) -> None:
nonlocal similarity
similarity = min(similarity,
overload_arg_similarity(caller_type, callee_type))
if similarity == 0:
# No match -- exit early since none of the remaining work can change
# the result.
raise Finished
try:
self.check_argument_types(arg_types, arg_kinds, callee, formal_to_actual,
context=context, check_arg=check_arg)
except Finished:
pass
return similarity
def match_signature_types(self, arg_types: List[Type], arg_kinds: List[int],
arg_names: List[str], callee: CallableType,
context: Context) -> bool:
"""Determine whether arguments types match the signature.
Assume that argument counts are compatible.
Return True if arguments match.
"""
formal_to_actual = map_actuals_to_formals(arg_kinds,
arg_names,
callee.arg_kinds,
callee.arg_names,
lambda i: arg_types[i])
ok = True
def check_arg(caller_type: Type, original_caller_type: Type, caller_kind: int,
callee_type: Type, n: int, m: int, callee: CallableType,
context: Context, messages: MessageBuilder) -> None:
nonlocal ok
if not is_subtype(caller_type, callee_type):
ok = False
self.check_argument_types(arg_types, arg_kinds, callee, formal_to_actual,
context=context, check_arg=check_arg)
return ok
def apply_generic_arguments(self, callable: CallableType, types: List[Type],
context: Context) -> Type:
"""Simple wrapper around mypy.applytype.apply_generic_arguments."""
return applytype.apply_generic_arguments(callable, types, self.msg, context)
def apply_generic_arguments2(self, overload: Overloaded, types: List[Type],
context: Context) -> Type:
items = [] # type: List[CallableType]
for item in overload.items():
applied = self.apply_generic_arguments(item, types, context)
if isinstance(applied, CallableType):
items.append(applied)
else:
# There was an error.
return AnyType()
return Overloaded(items)
def visit_member_expr(self, e: MemberExpr) -> Type:
"""Visit member expression (of form e.id)."""
self.chk.module_refs.update(extract_refexpr_names(e))
result = self.analyze_ordinary_member_access(e, False)
return self.chk.narrow_type_from_binder(e, result)
def analyze_ordinary_member_access(self, e: MemberExpr,
is_lvalue: bool) -> Type:
"""Analyse member expression or member lvalue."""
if e.kind is not None:
# This is a reference to a module attribute.
return self.analyze_ref_expr(e)
else:
# This is a reference to a non-module attribute.
return analyze_member_access(e.name, self.accept(e.expr), e,
is_lvalue, False, False,
self.named_type, self.not_ready_callback, self.msg,
chk=self.chk)
def analyze_external_member_access(self, member: str, base_type: Type,
context: Context) -> Type:
"""Analyse member access that is external, i.e. it cannot
refer to private definitions. Return the result type.
"""
# TODO remove; no private definitions in mypy
return analyze_member_access(member, base_type, context, False, False, False,
self.named_type, self.not_ready_callback, self.msg,
chk=self.chk)
def visit_int_expr(self, e: IntExpr) -> Type:
"""Type check an integer literal (trivial)."""
return self.named_type('builtins.int')
def visit_str_expr(self, e: StrExpr) -> Type:
"""Type check a string literal (trivial)."""
return self.named_type('builtins.str')
def visit_bytes_expr(self, e: BytesExpr) -> Type:
"""Type check a bytes literal (trivial)."""
return self.named_type('builtins.bytes')
def visit_unicode_expr(self, e: UnicodeExpr) -> Type:
"""Type check a unicode literal (trivial)."""
return self.named_type('builtins.unicode')
def visit_float_expr(self, e: FloatExpr) -> Type:
"""Type check a float literal (trivial)."""
return self.named_type('builtins.float')
def visit_complex_expr(self, e: ComplexExpr) -> Type:
"""Type check a complex literal."""
return self.named_type('builtins.complex')
def visit_ellipsis(self, e: EllipsisExpr) -> Type:
"""Type check '...'."""
if self.chk.options.python_version[0] >= 3:
return self.named_type('builtins.ellipsis')
else:
# '...' is not valid in normal Python 2 code, but it can
# be used in stubs. The parser makes sure that we only
# get this far if we are in a stub, and we can safely
# return 'object' as ellipsis is special cased elsewhere.
# The builtins.ellipsis type does not exist in Python 2.
return self.named_type('builtins.object')
def visit_op_expr(self, e: OpExpr) -> Type:
"""Type check a binary operator expression."""
if e.op == 'and' or e.op == 'or':
return self.check_boolean_op(e, e)
if e.op == '*' and isinstance(e.left, ListExpr):
# Expressions of form [...] * e get special type inference.
return self.check_list_multiply(e)
if e.op == '%':
pyversion = self.chk.options.python_version
if pyversion[0] == 3:
if isinstance(e.left, BytesExpr) and pyversion[1] >= 5:
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
if isinstance(e.left, StrExpr):
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
elif pyversion[0] <= 2:
if isinstance(e.left, (StrExpr, BytesExpr, UnicodeExpr)):
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
left_type = self.accept(e.left)
if e.op in nodes.op_methods:
method = self.get_operator_method(e.op)
result, method_type = self.check_op(method, left_type, e.right, e,
allow_reverse=True)
e.method_type = method_type
return result
else:
raise RuntimeError('Unknown operator {}'.format(e.op))
def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
"""Type check a comparison expression.
Comparison expressions are type checked consecutive-pair-wise
That is, 'a < b > c == d' is check as 'a < b and b > c and c == d'
"""
result = None # type: mypy.types.Type
# Check each consecutive operand pair and their operator
for left, right, operator in zip(e.operands, e.operands[1:], e.operators):
left_type = self.accept(left)
method_type = None # type: mypy.types.Type
if operator == 'in' or operator == 'not in':
right_type = self.accept(right) # TODO only evaluate if needed
# Keep track of whether we get type check errors (these won't be reported, they
# are just to verify whether something is valid typing wise).
local_errors = self.msg.copy()
local_errors.disable_count = 0
sub_result, method_type = self.check_op_local('__contains__', right_type,
left, e, local_errors)
if isinstance(right_type, PartialType):
# We don't really know if this is an error or not, so just shut up.
pass
elif (local_errors.is_errors() and
# is_valid_var_arg is True for any Iterable
self.is_valid_var_arg(right_type)):
itertype = self.chk.analyze_iterable_item_type(right)
method_type = CallableType(
[left_type],
[nodes.ARG_POS],
[None],
self.chk.bool_type(),
self.named_type('builtins.function'))
sub_result = self.chk.bool_type()
if not is_subtype(left_type, itertype):
self.msg.unsupported_operand_types('in', left_type, right_type, e)
else:
self.msg.add_errors(local_errors)
if operator == 'not in':
sub_result = self.chk.bool_type()
elif operator in nodes.op_methods:
method = self.get_operator_method(operator)
sub_result, method_type = self.check_op(method, left_type, right, e,
allow_reverse=True)
elif operator == 'is' or operator == 'is not':
sub_result = self.chk.bool_type()
method_type = None
else:
raise RuntimeError('Unknown comparison operator {}'.format(operator))
e.method_types.append(method_type)
# Determine type of boolean-and of result and sub_result
if result is None:
result = sub_result
else:
# TODO: check on void needed?
self.check_usable_type(sub_result, e)
result = join.join_types(result, sub_result)
return result
def get_operator_method(self, op: str) -> str:
if op == '/' and self.chk.options.python_version[0] == 2:
# TODO also check for "from __future__ import division"
return '__div__'
else:
return nodes.op_methods[op]
def _check_op_for_errors(self, method: str, base_type: Type, arg: Expression,
context: Context
) -> Tuple[Tuple[Type, Type], MessageBuilder]:
"""Type check a binary operation which maps to a method call.
Return ((result type, inferred operator method type), error message).
"""
local_errors = self.msg.copy()
local_errors.disable_count = 0
result = self.check_op_local(method, base_type,
arg, context,
local_errors)
return result, local_errors
def check_op_local(self, method: str, base_type: Type, arg: Expression,
context: Context, local_errors: MessageBuilder) -> Tuple[Type, Type]:
"""Type check a binary operation which maps to a method call.
Return tuple (result type, inferred operator method type).
"""
method_type = analyze_member_access(method, base_type, context, False, False, True,
self.named_type, self.not_ready_callback, local_errors,
chk=self.chk)
return self.check_call(method_type, [arg], [nodes.ARG_POS],
context, arg_messages=local_errors)
def check_op(self, method: str, base_type: Type, arg: Expression,
context: Context,
allow_reverse: bool = False) -> Tuple[Type, Type]:
"""Type check a binary operation which maps to a method call.
Return tuple (result type, inferred operator method type).
"""
# Use a local error storage for errors related to invalid argument
# type (but NOT other errors). This error may need to be suppressed
# for operators which support __rX methods.
local_errors = self.msg.copy()
local_errors.disable_count = 0
if not allow_reverse or self.has_member(base_type, method):
result = self.check_op_local(method, base_type, arg, context,
local_errors)
if allow_reverse:
arg_type = self.chk.type_map[arg]
if isinstance(arg_type, AnyType):
# If the right operand has type Any, we can't make any
# conjectures about the type of the result, since the
# operand could have a __r method that returns anything.
result = AnyType(), result[1]
success = not local_errors.is_errors()
else:
result = AnyType(), AnyType()
success = False
if success or not allow_reverse or isinstance(base_type, AnyType):
# We were able to call the normal variant of the operator method,
# or there was some problem not related to argument type
# validity, or the operator has no __rX method. In any case, we
# don't need to consider the __rX method.
self.msg.add_errors(local_errors)
return result
else:
# Calling the operator method was unsuccessful. Try the __rX
# method of the other operand instead.
rmethod = self.get_reverse_op_method(method)
arg_type = self.accept(arg)
base_arg_node = TempNode(base_type)
# In order to be consistent with showing an error about the lhs not matching if neither
# the lhs nor the rhs have a compatible signature, we keep track of the first error
# message generated when considering __rX methods and __cmp__ methods for Python 2.
first_error = None # type: Optional[Tuple[Tuple[Type, Type], MessageBuilder]]
if self.has_member(arg_type, rmethod):
result, local_errors = self._check_op_for_errors(rmethod, arg_type,
base_arg_node, context)
if not local_errors.is_errors():
return result
first_error = first_error or (result, local_errors)
# If we've failed to find an __rX method and we're checking Python 2, check to see if
# there is a __cmp__ method on the lhs or on the rhs.
if (self.chk.options.python_version[0] == 2 and
method in nodes.ops_falling_back_to_cmp):
cmp_method = nodes.comparison_fallback_method
if self.has_member(base_type, cmp_method):
# First check the if the lhs has a __cmp__ method that works
result, local_errors = self._check_op_for_errors(cmp_method, base_type,
arg, context)
if not local_errors.is_errors():
return result
first_error = first_error or (result, local_errors)
if self.has_member(arg_type, cmp_method):
# Failed to find a __cmp__ method on the lhs, check if
# the rhs as a __cmp__ method that can operate on lhs
result, local_errors = self._check_op_for_errors(cmp_method, arg_type,
base_arg_node, context)
if not local_errors.is_errors():
return result
first_error = first_error or (result, local_errors)
if first_error:
# We found either a __rX method, a __cmp__ method on the base_type, or a __cmp__
# method on the rhs and failed match. Return the error for the first of these to
# fail.
self.msg.add_errors(first_error[1])
return first_error[0]
else:
# No __rX method or __cmp__. Do deferred type checking to
# produce error message that we may have missed previously.
# TODO Fix type checking an expression more than once.
return self.check_op_local(method, base_type, arg, context,
self.msg)
def get_reverse_op_method(self, method: str) -> str:
if method == '__div__' and self.chk.options.python_version[0] == 2:
return '__rdiv__'
else:
return nodes.reverse_op_methods[method]
def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
"""Type check a boolean operation ('and' or 'or')."""
# A boolean operation can evaluate to either of the operands.
# We use the current type context to guide the type inference of of
# the left operand. We also use the left operand type to guide the type
# inference of the right operand so that expressions such as
# '[1] or []' are inferred correctly.
ctx = self.chk.type_context[-1]
left_type = self.accept(e.left, ctx)
assert e.op in ('and', 'or') # Checked by visit_op_expr
if e.op == 'and':
right_map, left_map = \
mypy.checker.find_isinstance_check(e.left, self.chk.type_map)
restricted_left_type = false_only(left_type)
result_is_left = not left_type.can_be_true
elif e.op == 'or':
left_map, right_map = \
mypy.checker.find_isinstance_check(e.left, self.chk.type_map)
restricted_left_type = true_only(left_type)
result_is_left = not left_type.can_be_false
with self.chk.binder.frame_context():
if right_map:
for var, type in right_map.items():
self.chk.binder.push(var, type)
right_type = self.accept(e.right, left_type)
self.check_usable_type(left_type, context)
self.check_usable_type(right_type, context)
if right_map is None:
# The boolean expression is statically known to be the left value
assert left_map is not None # find_isinstance_check guarantees this
return left_type
if left_map is None:
# The boolean expression is statically known to be the right value
assert right_map is not None # find_isinstance_check guarantees this
return right_type
if isinstance(restricted_left_type, UninhabitedType):
# The left operand can never be the result
return right_type
elif result_is_left:
# The left operand is always the result
return left_type
else:
return UnionType.make_simplified_union([restricted_left_type, right_type])
def check_list_multiply(self, e: OpExpr) -> Type:
"""Type check an expression of form '[...] * e'.
Type inference is special-cased for this common construct.
"""
right_type = self.accept(e.right)
if is_subtype(right_type, self.named_type('builtins.int')):
# Special case: [...] * <int value>. Use the type context of the
# OpExpr, since the multiplication does not affect the type.
left_type = self.accept(e.left, context=self.chk.type_context[-1])
else:
left_type = self.accept(e.left)
result, method_type = self.check_op('__mul__', left_type, e.right, e)
e.method_type = method_type
return result
def visit_unary_expr(self, e: UnaryExpr) -> Type:
"""Type check an unary operation ('not', '-', '+' or '~')."""
operand_type = self.accept(e.expr)
op = e.op
if op == 'not':
self.check_usable_type(operand_type, e)
result = self.chk.bool_type() # type: Type
elif op == '-':
method_type = self.analyze_external_member_access('__neg__',
operand_type, e)
result, method_type = self.check_call(method_type, [], [], e)
e.method_type = method_type
elif op == '+':
method_type = self.analyze_external_member_access('__pos__',
operand_type, e)
result, method_type = self.check_call(method_type, [], [], e)
e.method_type = method_type
else:
assert op == '~', "unhandled unary operator"
method_type = self.analyze_external_member_access('__invert__',
operand_type, e)
result, method_type = self.check_call(method_type, [], [], e)
e.method_type = method_type
return result
def visit_index_expr(self, e: IndexExpr) -> Type:
"""Type check an index expression (base[index]).
It may also represent type application.
"""
result = self.visit_index_expr_helper(e)
return self.chk.narrow_type_from_binder(e, result)
def visit_index_expr_helper(self, e: IndexExpr) -> Type:
if e.analyzed:
# It's actually a type application.
return self.accept(e.analyzed)
left_type = self.accept(e.base)
if isinstance(left_type, TupleType) and self.chk.in_checked_function():
# Special case for tuples. They support indexing only by integer
# literals.
index = e.index
if isinstance(index, SliceExpr):
return self.visit_tuple_slice_helper(left_type, index)
ok = False
if isinstance(index, IntExpr):
n = index.value
ok = True
elif isinstance(index, UnaryExpr):
if index.op == '-':
operand = index.expr
if isinstance(operand, IntExpr):
n = len(left_type.items) - operand.value
ok = True
if ok:
if n >= 0 and n < len(left_type.items):
return left_type.items[n]
else:
self.chk.fail(messages.TUPLE_INDEX_OUT_OF_RANGE, e)
return AnyType()
else:
self.chk.fail(messages.TUPLE_INDEX_MUST_BE_AN_INT_LITERAL, e)
return AnyType()
else:
result, method_type = self.check_op('__getitem__', left_type, e.index, e)
e.method_type = method_type
return result
def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr) -> Type:
begin = None # type: int
end = None # type: int
stride = None # type:int
if slic.begin_index:
begin = self._get_value(slic.begin_index)
if begin is None:
self.chk.fail(
messages.TUPLE_SLICE_MUST_BE_AN_INT_LITERAL,
slic.begin_index)
return AnyType()
if slic.end_index:
end = self._get_value(slic.end_index)
if end is None:
self.chk.fail(
messages.TUPLE_SLICE_MUST_BE_AN_INT_LITERAL,
slic.end_index)
return AnyType()
if slic.stride:
stride = self._get_value(slic.stride)
if stride is None:
self.chk.fail(
messages.TUPLE_SLICE_MUST_BE_AN_INT_LITERAL,
slic.stride)
return AnyType()
return left_type.slice(begin, stride, end)
def _get_value(self, index: Expression) -> Optional[int]:
if isinstance(index, IntExpr):
return index.value
elif isinstance(index, UnaryExpr):
if index.op == '-':
operand = index.expr
if isinstance(operand, IntExpr):
return -1 * operand.value
return None
def visit_cast_expr(self, expr: CastExpr) -> Type:
"""Type check a cast expression."""
source_type = self.accept(expr.expr, context=AnyType())
target_type = expr.type
if self.chk.options.warn_redundant_casts and is_same_type(source_type, target_type):
self.msg.redundant_cast(target_type, expr)
if not self.is_valid_cast(source_type, target_type):
self.msg.invalid_cast(target_type, source_type, expr)
return target_type
def is_valid_cast(self, source_type: Type, target_type: Type) -> bool:
"""Is a cast from source_type to target_type meaningful?"""
return (isinstance(target_type, AnyType) or
(not isinstance(source_type, Void) and
not isinstance(target_type, Void)))
def visit_reveal_type_expr(self, expr: RevealTypeExpr) -> Type:
"""Type check a reveal_type expression."""
revealed_type = self.accept(expr.expr)
self.msg.reveal_type(revealed_type, expr)
return revealed_type
def visit_type_application(self, tapp: TypeApplication) -> Type:
"""Type check a type application (expr[type, ...])."""
self.chk.fail(messages.GENERIC_TYPE_NOT_VALID_AS_EXPRESSION, tapp)
return AnyType()
def visit_type_alias_expr(self, alias: TypeAliasExpr) -> Type:
return AnyType()
def visit_list_expr(self, e: ListExpr) -> Type:
"""Type check a list expression [...]."""
return self.check_lst_expr(e.items, 'builtins.list', '<list>', e)
def visit_set_expr(self, e: SetExpr) -> Type:
return self.check_lst_expr(e.items, 'builtins.set', '<set>', e)
def check_lst_expr(self, items: List[Expression], fullname: str,
tag: str, context: Context) -> Type:
# Translate into type checking a generic function call.
# Used for list and set expressions, as well as for tuples
# containing star expressions that don't refer to a
# Tuple. (Note: "lst" stands for list-set-tuple. :-)
tvdef = TypeVarDef('T', -1, [], self.chk.object_type())
tv = TypeVarType(tvdef)
constructor = CallableType(
[tv],
[nodes.ARG_STAR],
[None],
self.chk.named_generic_type(fullname, [tv]),
self.named_type('builtins.function'),
name=tag,
variables=[tvdef])
return self.check_call(constructor,
[(i.expr if isinstance(i, StarExpr) else i)
for i in items],
[(nodes.ARG_STAR if isinstance(i, StarExpr) else nodes.ARG_POS)
for i in items],
context)[0]
def visit_tuple_expr(self, e: TupleExpr) -> Type:
"""Type check a tuple expression."""
# Try to determine type context for type inference.
type_context = self.chk.type_context[-1]
type_context_items = None
if isinstance(type_context, UnionType):
tuples_in_context = [t for t in type_context.items
if (isinstance(t, TupleType) and len(t.items) == len(e.items)) or
is_named_instance(t, 'builtins.tuple')]
if len(tuples_in_context) == 1:
type_context = tuples_in_context[0]
else:
# There are either no relevant tuples in the Union, or there is
# more than one. Either way, we can't decide on a context.
pass
if isinstance(type_context, TupleType):
type_context_items = type_context.items
elif is_named_instance(type_context, 'builtins.tuple'):
assert isinstance(type_context, Instance)
if type_context.args:
type_context_items = [type_context.args[0]] * len(e.items)
# NOTE: it's possible for the context to have a different
# number of items than e. In that case we use those context
# items that match a position in e, and we'll worry about type
# mismatches later.
# Infer item types. Give up if there's a star expression
# that's not a Tuple.
items = [] # type: List[Type]
j = 0 # Index into type_context_items; irrelevant if type_context_items is none
for i in range(len(e.items)):
item = e.items[i]
tt = None # type: Type
if isinstance(item, StarExpr):
# Special handling for star expressions.
# TODO: If there's a context, and item.expr is a
# TupleExpr, flatten it, so we can benefit from the
# context? Counterargument: Why would anyone write
# (1, *(2, 3)) instead of (1, 2, 3) except in a test?
tt = self.accept(item.expr)
self.check_usable_type(tt, e)
if isinstance(tt, TupleType):
items.extend(tt.items)
j += len(tt.items)
else:
# A star expression that's not a Tuple.
# Treat the whole thing as a variable-length tuple.
return self.check_lst_expr(e.items, 'builtins.tuple', '<tuple>', e)
else:
if not type_context_items or j >= len(type_context_items):
tt = self.accept(item)
else:
tt = self.accept(item, type_context_items[j])
j += 1
self.check_usable_type(tt, e)
items.append(tt)
fallback_item = join.join_type_list(items)
return TupleType(items, self.chk.named_generic_type('builtins.tuple', [fallback_item]))
def visit_dict_expr(self, e: DictExpr) -> Type:
"""Type check a dict expression.
Translate it into a call to dict(), with provisions for **expr.
"""
# Collect function arguments, watching out for **expr.
args = [] # type: List[Expression] # Regular "key: value"
stargs = [] # type: List[Expression] # For "**expr"
for key, value in e.items:
if key is None:
stargs.append(value)
else:
args.append(TupleExpr([key, value]))
# Define type variables (used in constructors below).
ktdef = TypeVarDef('KT', -1, [], self.chk.object_type())
vtdef = TypeVarDef('VT', -2, [], self.chk.object_type())
kt = TypeVarType(ktdef)
vt = TypeVarType(vtdef)
# Call dict(*args), unless it's empty and stargs is not.
if args or not stargs:
# The callable type represents a function like this:
#
# def <unnamed>(*v: Tuple[kt, vt]) -> Dict[kt, vt]: ...
constructor = CallableType(
[TupleType([kt, vt], self.named_type('builtins.tuple'))],
[nodes.ARG_STAR],
[None],
self.chk.named_generic_type('builtins.dict', [kt, vt]),
self.named_type('builtins.function'),
name='<list>',
variables=[ktdef, vtdef])
rv = self.check_call(constructor, args, [nodes.ARG_POS] * len(args), e)[0]
else:
# dict(...) will be called below.
rv = None
# Call rv.update(arg) for each arg in **stargs,
# except if rv isn't set yet, then set rv = dict(arg).
if stargs:
for arg in stargs:
if rv is None:
constructor = CallableType(
[self.chk.named_generic_type('typing.Mapping', [kt, vt])],
[nodes.ARG_POS],
[None],
self.chk.named_generic_type('builtins.dict', [kt, vt]),
self.named_type('builtins.function'),
name='<list>',
variables=[ktdef, vtdef])
rv = self.check_call(constructor, [arg], [nodes.ARG_POS], arg)[0]
else:
method = self.analyze_external_member_access('update', rv, arg)
self.check_call(method, [arg], [nodes.ARG_POS], arg)
return rv
def visit_func_expr(self, e: FuncExpr) -> Type:
"""Type check lambda expression."""
inferred_type = self.infer_lambda_type_using_context(e)
if not inferred_type:
# No useful type context.
ret_type = e.expr().accept(self.chk)
if not e.arguments:
# Form 'lambda: e'; just use the inferred return type.
return CallableType([], [], [], ret_type, self.named_type('builtins.function'))
else:
# TODO: Consider reporting an error. However, this is fine if
# we are just doing the first pass in contextual type
# inference.
return AnyType()
else:
# Type context available.
self.chk.check_func_item(e, type_override=inferred_type)
if e.expr() not in self.chk.type_map:
self.accept(e.expr())
ret_type = self.chk.type_map[e.expr()]
if isinstance(ret_type, NoneTyp):
# For "lambda ...: None", just use type from the context.
# Important when the context is Callable[..., None] which
# really means Void. See #1425.
return inferred_type
return replace_callable_return_type(inferred_type, ret_type)
def infer_lambda_type_using_context(self, e: FuncExpr) -> CallableType:
"""Try to infer lambda expression type using context.
Return None if could not infer type.
"""
# TODO also accept 'Any' context
ctx = self.chk.type_context[-1]
if not ctx or not isinstance(ctx, CallableType):
return None
# The context may have function type variables in it. We replace them
# since these are the type variables we are ultimately trying to infer;
# they must be considered as indeterminate. We use ErasedType since it
# does not affect type inference results (it is for purposes like this
# only).
ctx = replace_meta_vars(ctx, ErasedType())
callable_ctx = cast(CallableType, ctx)
arg_kinds = [arg.kind for arg in e.arguments]
if callable_ctx.is_ellipsis_args:
# Fill in Any arguments to match the arguments of the lambda.
callable_ctx = callable_ctx.copy_modified(
is_ellipsis_args=False,
arg_types=[AnyType()] * len(arg_kinds),
arg_kinds=arg_kinds
)
if callable_ctx.arg_kinds != arg_kinds:
# Incompatible context; cannot use it to infer types.
self.chk.fail(messages.CANNOT_INFER_LAMBDA_TYPE, e)
return None
return callable_ctx
def visit_super_expr(self, e: SuperExpr) -> Type:
"""Type check a super expression (non-lvalue)."""
t = self.analyze_super(e, False)
return t
def analyze_super(self, e: SuperExpr, is_lvalue: bool) -> Type:
"""Type check a super expression."""
if e.info and e.info.bases:
# TODO fix multiple inheritance etc
if len(e.info.mro) < 2:
self.chk.fail('Internal error: unexpected mro for {}: {}'.format(
e.info.name(), e.info.mro), e)
return AnyType()
for base in e.info.mro[1:]:
if e.name in base.names or base == e.info.mro[-1]:
if e.info.fallback_to_any and base == e.info.mro[-1]:
# There's an undefined base class, and we're
# at the end of the chain. That's not an error.
return AnyType()
if not self.chk.in_checked_function():
return AnyType()
return analyze_member_access(e.name, self_type(e.info), e,
is_lvalue, True, False,
self.named_type, self.not_ready_callback,
self.msg, base, chk=self.chk)
else:
# Invalid super. This has been reported by the semantic analyzer.
return AnyType()
def visit_slice_expr(self, e: SliceExpr) -> Type:
for index in [e.begin_index, e.end_index, e.stride]:
if index:
t = self.accept(index)
self.chk.check_subtype(t, self.named_type('builtins.int'),
index, messages.INVALID_SLICE_INDEX)
return self.named_type('builtins.slice')
def visit_list_comprehension(self, e: ListComprehension) -> Type:
return self.check_generator_or_comprehension(
e.generator, 'builtins.list', '<list-comprehension>')
def visit_set_comprehension(self, e: SetComprehension) -> Type:
return self.check_generator_or_comprehension(
e.generator, 'builtins.set', '<set-comprehension>')
def visit_generator_expr(self, e: GeneratorExpr) -> Type:
return self.check_generator_or_comprehension(e, 'typing.Iterator',
'<generator>')
def check_generator_or_comprehension(self, gen: GeneratorExpr,
type_name: str,
id_for_messages: str) -> Type:
"""Type check a generator expression or a list comprehension."""
with self.chk.binder.frame_context():
self.check_for_comp(gen)
# Infer the type of the list comprehension by using a synthetic generic
# callable type.
tvdef = TypeVarDef('T', -1, [], self.chk.object_type())
tv = TypeVarType(tvdef)
constructor = CallableType(
[tv],
[nodes.ARG_POS],
[None],
self.chk.named_generic_type(type_name, [tv]),
self.chk.named_type('builtins.function'),
name=id_for_messages,
variables=[tvdef])
return self.check_call(constructor,
[gen.left_expr], [nodes.ARG_POS], gen)[0]
def visit_dictionary_comprehension(self, e: DictionaryComprehension) -> Type:
"""Type check a dictionary comprehension."""
with self.chk.binder.frame_context():
self.check_for_comp(e)
# Infer the type of the list comprehension by using a synthetic generic
# callable type.
ktdef = TypeVarDef('KT', -1, [], self.chk.object_type())
vtdef = TypeVarDef('VT', -2, [], self.chk.object_type())
kt = TypeVarType(ktdef)
vt = TypeVarType(vtdef)
constructor = CallableType(
[kt, vt],
[nodes.ARG_POS, nodes.ARG_POS],
[None, None],
self.chk.named_generic_type('builtins.dict', [kt, vt]),
self.chk.named_type('builtins.function'),
name='<dictionary-comprehension>',
variables=[ktdef, vtdef])
return self.check_call(constructor,
[e.key, e.value], [nodes.ARG_POS, nodes.ARG_POS], e)[0]
def check_for_comp(self, e: Union[GeneratorExpr, DictionaryComprehension]) -> None:
"""Check the for_comp part of comprehensions. That is the part from 'for':
... for x in y if z
Note: This adds the type information derived from the condlists to the current binder.
"""
for index, sequence, conditions in zip(e.indices, e.sequences,
e.condlists):
sequence_type = self.chk.analyze_iterable_item_type(sequence)
self.chk.analyze_index_variables(index, sequence_type, e)
for condition in conditions:
self.accept(condition)
# values are only part of the comprehension when all conditions are true
true_map, _ = mypy.checker.find_isinstance_check(condition, self.chk.type_map)
if true_map:
for var, type in true_map.items():
self.chk.binder.push(var, type)
def visit_conditional_expr(self, e: ConditionalExpr) -> Type:
cond_type = self.accept(e.cond)
self.check_usable_type(cond_type, e)
ctx = self.chk.type_context[-1]
# Gain type information from isinstance if it is there
# but only for the current expression
if_map, else_map = mypy.checker.find_isinstance_check(e.cond, self.chk.type_map)
if_type = self.analyze_cond_branch(if_map, e.if_expr, context=ctx)
if not mypy.checker.is_valid_inferred_type(if_type):
# Analyze the right branch disregarding the left branch.
else_type = self.analyze_cond_branch(else_map, e.else_expr, context=ctx)
# If it would make a difference, re-analyze the left
# branch using the right branch's type as context.
if ctx is None or not is_equivalent(else_type, ctx):
# TODO: If it's possible that the previous analysis of
# the left branch produced errors that are avoided
# using this context, suppress those errors.
if_type = self.analyze_cond_branch(if_map, e.if_expr, context=else_type)
else:
# Analyze the right branch in the context of the left
# branch's type.
else_type = self.analyze_cond_branch(else_map, e.else_expr, context=if_type)
res = join.join_types(if_type, else_type)
return res
def analyze_cond_branch(self, map: Optional[Dict[Node, Type]],
node: Node, context: Optional[Type]) -> Type:
with self.chk.binder.frame_context():
if map:
for var, type in map.items():
self.chk.binder.push(var, type)
return self.accept(node, context=context)
def visit_backquote_expr(self, e: BackquoteExpr) -> Type:
self.accept(e.expr)
return self.named_type('builtins.str')
#
# Helpers
#
def accept(self, node: Node, context: Type = None) -> Type:
"""Type check a node. Alias for TypeChecker.accept."""
return self.chk.accept(node, context)
def check_usable_type(self, typ: Type, context: Context) -> None:
"""Generate an error if type is Void."""
self.chk.check_usable_type(typ, context)
def is_boolean(self, typ: Type) -> bool:
"""Is type compatible with bool?"""
return is_subtype(typ, self.chk.bool_type())
def named_type(self, name: str) -> Instance:
"""Return an instance type with type given by the name and no type
arguments. Alias for TypeChecker.named_type.
"""
return self.chk.named_type(name)
def is_valid_var_arg(self, typ: Type) -> bool:
"""Is a type valid as a *args argument?"""
return (isinstance(typ, TupleType) or
is_subtype(typ, self.chk.named_generic_type('typing.Iterable',
[AnyType()])) or
isinstance(typ, AnyType))
def is_valid_keyword_var_arg(self, typ: Type) -> bool:
"""Is a type valid as a **kwargs argument?"""
if self.chk.options.python_version[0] >= 3:
return is_subtype(typ, self.chk.named_generic_type(
'builtins.dict', [self.named_type('builtins.str'),
AnyType()]))
else:
return (
is_subtype(typ, self.chk.named_generic_type(
'builtins.dict',
[self.named_type('builtins.str'),
AnyType()]))
or
is_subtype(typ, self.chk.named_generic_type(
'builtins.dict',
[self.named_type('builtins.unicode'),
AnyType()])))
def has_non_method(self, typ: Type, member: str) -> bool:
"""Does type have a member variable / property with the given name?"""
if isinstance(typ, Instance):
return (not typ.type.has_method(member) and
typ.type.has_readable_member(member))
else:
return False
def has_member(self, typ: Type, member: str) -> bool:
"""Does type have member with the given name?"""
# TODO TupleType => also consider tuple attributes
if isinstance(typ, Instance):
return typ.type.has_readable_member(member)
elif isinstance(typ, AnyType):
return True
elif isinstance(typ, UnionType):
result = all(self.has_member(x, member) for x in typ.items)
return result
elif isinstance(typ, TupleType):
return self.has_member(typ.fallback, member)
else:
return False
def not_ready_callback(self, name: str, context: Context) -> None:
"""Called when we can't infer the type of a variable because it's not ready yet.
Either defer type checking of the enclosing function to the next
pass or report an error.
"""
self.chk.handle_cannot_determine_type(name, context)
def map_actuals_to_formals(caller_kinds: List[int],
caller_names: List[str],
callee_kinds: List[int],
callee_names: List[str],
caller_arg_type: Callable[[int],
Type]) -> List[List[int]]:
"""Calculate mapping between actual (caller) args and formals.
The result contains a list of caller argument indexes mapping to each
callee argument index, indexed by callee index.
The caller_arg_type argument should evaluate to the type of the actual
argument type with the given index.
"""
ncallee = len(callee_kinds)
map = [[] for i in range(ncallee)] # type: List[List[int]]
j = 0
for i, kind in enumerate(caller_kinds):
if kind == nodes.ARG_POS:
if j < ncallee:
if callee_kinds[j] in [nodes.ARG_POS, nodes.ARG_OPT,
nodes.ARG_NAMED]:
map[j].append(i)
j += 1
elif callee_kinds[j] == nodes.ARG_STAR:
map[j].append(i)
elif kind == nodes.ARG_STAR:
# We need to know the actual type to map varargs.
argt = caller_arg_type(i)
if isinstance(argt, TupleType):
# A tuple actual maps to a fixed number of formals.
for _ in range(len(argt.items)):
if j < ncallee:
if callee_kinds[j] != nodes.ARG_STAR2:
map[j].append(i)
else:
break
if callee_kinds[j] != nodes.ARG_STAR:
j += 1
else:
# Assume that it is an iterable (if it isn't, there will be
# an error later).
while j < ncallee:
if callee_kinds[j] in (nodes.ARG_NAMED, nodes.ARG_STAR2):
break
else:
map[j].append(i)
if callee_kinds[j] == nodes.ARG_STAR:
break
j += 1
elif kind == nodes.ARG_NAMED:
name = caller_names[i]
if name in callee_names:
map[callee_names.index(name)].append(i)
elif nodes.ARG_STAR2 in callee_kinds:
map[callee_kinds.index(nodes.ARG_STAR2)].append(i)
else:
assert kind == nodes.ARG_STAR2
for j in range(ncallee):
# TODO tuple varargs complicate this
no_certain_match = (
not map[j] or caller_kinds[map[j][0]] == nodes.ARG_STAR)
if ((callee_names[j] and no_certain_match)
or callee_kinds[j] == nodes.ARG_STAR2):
map[j].append(i)
return map
def is_empty_tuple(t: Type) -> bool:
return isinstance(t, TupleType) and not t.items
def is_duplicate_mapping(mapping: List[int], actual_kinds: List[int]) -> bool:
# Multiple actuals can map to the same formal only if they both come from
# varargs (*args and **kwargs); in this case at runtime it is possible that
# there are no duplicates. We need to allow this, as the convention
# f(..., *args, **kwargs) is common enough.
return len(mapping) > 1 and not (
len(mapping) == 2 and
actual_kinds[mapping[0]] == nodes.ARG_STAR and
actual_kinds[mapping[1]] == nodes.ARG_STAR2)
def replace_callable_return_type(c: CallableType, new_ret_type: Type) -> CallableType:
"""Return a copy of a callable type with a different return type."""
return c.copy_modified(ret_type=new_ret_type)
class ArgInferSecondPassQuery(types.TypeQuery):
"""Query whether an argument type should be inferred in the second pass.
The result is True if the type has a type variable in a callable return
type anywhere. For example, the result for Callable[[], T] is True if t is
a type variable.
"""
def __init__(self) -> None:
super().__init__(False, types.ANY_TYPE_STRATEGY)
def visit_callable_type(self, t: CallableType) -> bool:
return self.query_types(t.arg_types) or t.accept(HasTypeVarQuery())
class HasTypeVarQuery(types.TypeQuery):
"""Visitor for querying whether a type has a type variable component."""
def __init__(self) -> None:
super().__init__(False, types.ANY_TYPE_STRATEGY)
def visit_type_var(self, t: TypeVarType) -> bool:
return True
def has_erased_component(t: Type) -> bool:
return t is not None and t.accept(HasErasedComponentsQuery())
class HasErasedComponentsQuery(types.TypeQuery):
"""Visitor for querying whether a type has an erased component."""
def __init__(self) -> None:
super().__init__(False, types.ANY_TYPE_STRATEGY)
def visit_erased_type(self, t: ErasedType) -> bool:
return True
def overload_arg_similarity(actual: Type, formal: Type) -> int:
"""Return if caller argument (actual) is compatible with overloaded signature arg (formal).
Return a similarity level:
0: no match
1: actual is compatible, but only using type promotions (e.g. int vs float)
2: actual is compatible without type promotions (e.g. int vs object)
The distinction is important in cases where multiple overload items match. We want
give priority to higher similarity matches.
"""
# Replace type variables with their upper bounds. Overloading
# resolution is based on runtime behavior which erases type
# parameters, so no need to handle type variables occurring within
# a type.
if isinstance(actual, TypeVarType):
actual = actual.erase_to_union_or_bound()
if isinstance(formal, TypeVarType):
formal = formal.erase_to_union_or_bound()
if (isinstance(actual, UninhabitedType) or isinstance(actual, AnyType) or
isinstance(formal, AnyType) or isinstance(formal, CallableType) or
(isinstance(actual, Instance) and actual.type.fallback_to_any)):
# These could match anything at runtime.
return 2
if isinstance(actual, NoneTyp):
if not experiments.STRICT_OPTIONAL:
# NoneTyp matches anything if we're not doing strict Optional checking
return 2
else:
# NoneType is a subtype of object
if isinstance(formal, Instance) and formal.type.fullname() == "builtins.object":
return 2
if isinstance(actual, UnionType):
return max(overload_arg_similarity(item, formal)
for item in actual.items)
if isinstance(formal, UnionType):
return max(overload_arg_similarity(actual, item)
for item in formal.items)
if isinstance(formal, TypeType):
if isinstance(actual, TypeType):
# Since Type[T] is covariant, check if actual = Type[A] is
# a subtype of formal = Type[F].
return overload_arg_similarity(actual.item, formal.item)
elif isinstance(actual, CallableType) and actual.is_type_obj():
# Check if the actual is a constructor of some sort.
# Note that this is this unsound, since we don't check the __init__ signature.
return overload_arg_similarity(actual.ret_type, formal.item)
else:
return 0
if isinstance(formal, Instance):
if isinstance(actual, CallableType):
actual = actual.fallback
if isinstance(actual, Overloaded):
actual = actual.items()[0].fallback
if isinstance(actual, TupleType):
actual = actual.fallback
if isinstance(actual, Instance):
# First perform a quick check (as an optimization) and fall back to generic
# subtyping algorithm if type promotions are possible (e.g., int vs. float).
if formal.type in actual.type.mro:
return 2
elif actual.type._promote and is_subtype(actual, formal):
return 1
else:
return 0
elif isinstance(actual, TypeType):
if formal.type.fullname() in {"builtins.object", "builtins.type"}:
return 2
else:
return 0
else:
return 0
if isinstance(actual, UnboundType) or isinstance(formal, UnboundType):
# Either actual or formal is the result of an error; shut up.
return 2
# Fall back to a conservative equality check for the remaining kinds of type.
return 2 if is_same_type(erasetype.erase_type(actual), erasetype.erase_type(formal)) else 0
def freshen_generic_callable(callee: CallableType) -> CallableType:
tvdefs = []
tvmap = {} # type: Dict[TypeVarId, Type]
for v in callee.variables:
tvdef = TypeVarDef.new_unification_variable(v)
tvdefs.append(tvdef)
tvmap[v.id] = TypeVarType(tvdef)
return cast(CallableType, expand_type(callee, tvmap)).copy_modified(variables=tvdefs)