Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tag Anys with their provenance #3786

Merged
merged 29 commits into from Aug 4, 2017
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a6633f5
Tag Anys with their provenance
ilinum Jul 31, 2017
694e30d
Fix broken test
ilinum Jul 31, 2017
1650bc2
Report most common sources of Any
ilinum Aug 1, 2017
4ddd163
Visit inner types in StatisticsVisitor
ilinum Aug 1, 2017
6d062ea
Mark unchecked functions as `special_form`
ilinum Aug 1, 2017
a95d7ea
Reword comment
ilinum Aug 1, 2017
988ba83
Do not use typing.Counter() because it doesn't exist in 3.5 and older
ilinum Aug 1, 2017
6477941
Use OrderedDict for column names in any-exprs-report
ilinum Aug 1, 2017
d05b7f0
Record type of Anys coming from other Anys as type of source Any.
ilinum Aug 2, 2017
87399d9
Use precise types for typing.Counter in type comments.
ilinum Aug 2, 2017
8645315
Refactor AnyExpressionsReport to avoid code duplication
ilinum Aug 3, 2017
a088b95
Remove resolved todos about type of Any -- all of them are good now
ilinum Aug 3, 2017
c01ede9
Merge branch 'master' into tag-anys
ilinum Aug 3, 2017
cd13986
Merge branch 'master' into tag-anys
ilinum Aug 3, 2017
e0d8411
[StatsVisitor] Do not visit lvalues twice
ilinum Aug 4, 2017
d30dec4
Report unqualified unresolved symbols as from_error
ilinum Aug 4, 2017
a923af1
Merge branch 'tag-anys'
ilinum Aug 4, 2017
2b3c1a1
Fix error after merge
ilinum Aug 4, 2017
498fa67
Convert several implicit Anys to special form (as per code review)
ilinum Aug 4, 2017
81cb76f
Partially revert previous commit that were breaking tests
ilinum Aug 4, 2017
be84bee
Do not run any-expressions-report in untyped defs
ilinum Aug 4, 2017
389d9eb
Revert accidental typeshed change
ilinum Aug 4, 2017
392b773
Remove redundant variable type annotation
ilinum Aug 4, 2017
f5ac528
Merge branch 'master' into tag-anys
ilinum Aug 4, 2017
78dcf05
fix typeshed commit
ilinum Aug 4, 2017
6ef0af4
Makes each TypeOfAny an instance of NewType
ilinum Aug 4, 2017
107053c
Use actual class instead of NewType
ilinum Aug 4, 2017
970613f
Use NewType for typechecking, alias for runtime
ilinum Aug 4, 2017
5260af8
Fix lint
ilinum Aug 4, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions mypy/binder.py
@@ -1,8 +1,8 @@
from typing import Dict, List, Set, Iterator, Union, Optional, cast
from contextlib import contextmanager

from mypy.types import Type, AnyType, PartialType, UnionType, NoneTyp
from mypy.nodes import (Key, Node, Expression, Var, RefExpr, SymbolTableNode)
from mypy.types import Type, AnyType, PartialType, UnionType, TypeOfAny
from mypy.nodes import (Key, Expression, Var, RefExpr)

from mypy.subtypes import is_subtype
from mypy.join import join_simple
Expand Down Expand Up @@ -175,10 +175,11 @@ def update_from_options(self, frames: List[Frame]) -> bool:

type = resulting_values[0]
assert type is not None
if isinstance(self.declarations.get(key), AnyType):
declaration_type = self.declarations.get(key)
if isinstance(declaration_type, AnyType):
# At this point resulting values can't contain None, see continue above
if not all(is_same_type(type, cast(Type, t)) for t in resulting_values[1:]):
type = AnyType()
type = AnyType(TypeOfAny.from_another_any, source_any=declaration_type)
else:
for other in resulting_values[1:]:
assert other is not None
Expand Down
97 changes: 53 additions & 44 deletions mypy/checker.py

Large diffs are not rendered by default.

127 changes: 68 additions & 59 deletions mypy/checkexpr.py

Large diffs are not rendered by default.

39 changes: 20 additions & 19 deletions mypy/checkmember.py
Expand Up @@ -4,7 +4,7 @@

from mypy.types import (
Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike, TypeVarDef,
Overloaded, TypeVarType, UnionType, PartialType, UninhabitedType,
Overloaded, TypeVarType, UnionType, PartialType, UninhabitedType, TypeOfAny,
DeletedType, NoneTyp, TypeType, function_type, get_type_vars,
)
from mypy.nodes import (
Expand Down Expand Up @@ -58,7 +58,7 @@ def analyze_member_access(name: str,
# Accessing __init__ in statically typed code would compromise
# type safety unless used via super().
msg.fail(messages.CANNOT_ACCESS_INIT, node)
return AnyType()
return AnyType(TypeOfAny.from_error)

# The base object has an instance type.

Expand Down Expand Up @@ -101,10 +101,10 @@ def analyze_member_access(name: str,
original_type=original_type, chk=chk)
elif isinstance(typ, AnyType):
# The base object has dynamic type.
return AnyType()
return AnyType(TypeOfAny.from_another_any, source_any=typ)
elif isinstance(typ, NoneTyp):
if chk.should_suppress_optional_error([typ]):
return AnyType()
return AnyType(TypeOfAny.from_error)
# The only attribute NoneType has are those it inherits from object
return analyze_member_access(name, builtin_type('builtins.object'), node, is_lvalue,
is_super, is_operator, builtin_type, not_ready_callback, msg,
Expand Down Expand Up @@ -171,7 +171,7 @@ def analyze_member_access(name: str,
original_type=original_type, chk=chk)
elif isinstance(typ, DeletedType):
msg.deleted_as_rvalue(typ, node)
return AnyType()
return AnyType(TypeOfAny.from_error)
elif isinstance(typ, TypeType):
# Similar to FunctionLike + is_type_obj() above.
item = None
Expand Down Expand Up @@ -202,7 +202,7 @@ def analyze_member_access(name: str,
original_type=original_type, chk=chk)

if chk.should_suppress_optional_error([typ]):
return AnyType()
return AnyType(TypeOfAny.from_error)
return msg.has_no_attr(original_type, typ, name, node)


Expand Down Expand Up @@ -264,15 +264,15 @@ def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo,
return setattr_type.arg_types[-1]

if itype.type.fallback_to_any:
return AnyType()
return AnyType(TypeOfAny.special_form)

# Could not find the member.
if is_super:
msg.undefined_in_superclass(name, node)
return AnyType()
return AnyType(TypeOfAny.from_error)
else:
if chk and chk.should_suppress_optional_error([itype]):
return AnyType()
return AnyType(TypeOfAny.from_error)
return msg.has_no_attr(original_type, itype, name, node)


Expand Down Expand Up @@ -325,7 +325,7 @@ def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, node: Cont
if not var.is_ready:
not_ready_callback(var.name(), node)
# Implicit 'Any' type.
result = AnyType()
result = AnyType(TypeOfAny.special_form)
fullname = '{}.{}'.format(var.info.fullname(), name)
hook = chk.plugin.get_attribute_hook(fullname)
if hook:
Expand Down Expand Up @@ -354,7 +354,7 @@ def handle_partial_attribute_type(typ: PartialType, is_lvalue: bool, msg: Messag
return typ
else:
msg.fail(messages.NEED_ANNOTATION_FOR_VAR, context)
return AnyType()
return AnyType(TypeOfAny.from_error)


def lookup_member_var_or_accessor(info: TypeInfo, name: str,
Expand Down Expand Up @@ -393,7 +393,7 @@ def check_method_type(functype: FunctionLike, itype: Instance, is_classmethod: b
if not subtypes.is_equivalent(clsarg.ret_type, itype):
msg.invalid_class_method_type(item, context)
else:
if not subtypes.is_equivalent(clsarg, AnyType()):
if not subtypes.is_equivalent(clsarg, AnyType(TypeOfAny.special_form)):
msg.invalid_class_method_type(item, context)


Expand All @@ -409,7 +409,7 @@ def analyze_class_attribute_access(itype: Instance,
node = itype.type.get(name)
if not node:
if itype.type.fallback_to_any:
return AnyType()
return AnyType(TypeOfAny.special_form)
return None

is_decorated = isinstance(node.node, Decorator)
Expand All @@ -435,12 +435,12 @@ def analyze_class_attribute_access(itype: Instance,
return add_class_tvars(t, itype, is_classmethod, builtin_type, original_type)
elif isinstance(node.node, Var):
not_ready_callback(name, context)
return AnyType()
return AnyType(TypeOfAny.special_form)

if isinstance(node.node, TypeVarExpr):
msg.fail('Type variable "{}.{}" cannot be used as an expression'.format(
itype.type.name(), name), context)
return AnyType()
return AnyType(TypeOfAny.from_error)

if isinstance(node.node, TypeInfo):
return type_object_type(node.node, builtin_type)
Expand All @@ -451,7 +451,7 @@ def analyze_class_attribute_access(itype: Instance,

if is_decorated:
# TODO: Return type of decorated function. This is quick hack to work around #998.
return AnyType()
return AnyType(TypeOfAny.special_form)
else:
return function_type(cast(FuncBase, node.node), builtin_type('builtins.function'))

Expand Down Expand Up @@ -501,7 +501,7 @@ def type_object_type(info: TypeInfo, builtin_type: Callable[[str], Instance]) ->
init_method = info.get_method('__init__')
if not init_method:
# Must be an invalid class definition.
return AnyType()
return AnyType(TypeOfAny.from_error)
else:
fallback = info.metaclass_type or builtin_type('builtins.type')
if init_method.info.fullname() == 'builtins.object':
Expand All @@ -514,10 +514,11 @@ def type_object_type(info: TypeInfo, builtin_type: Callable[[str], Instance]) ->
# base class, we can't know for sure, so check for that.
if info.fallback_to_any:
# Construct a universal callable as the prototype.
sig = CallableType(arg_types=[AnyType(), AnyType()],
any_type = AnyType(TypeOfAny.special_form)
sig = CallableType(arg_types=[any_type, any_type],
arg_kinds=[ARG_STAR, ARG_STAR2],
arg_names=["_args", "_kwds"],
ret_type=AnyType(),
ret_type=any_type,
fallback=builtin_type('builtins.function'))
return class_callable(sig, info, fallback, None)
# Construct callable type based on signature of __init__. Adjust
Expand Down
12 changes: 7 additions & 5 deletions mypy/checkstrformat.py
Expand Up @@ -5,7 +5,7 @@
from typing import cast, List, Tuple, Dict, Callable, Union, Optional

from mypy.types import (
Type, AnyType, TupleType, Instance, UnionType
Type, AnyType, TupleType, Instance, UnionType, TypeOfAny
)
from mypy.nodes import (
StrExpr, BytesExpr, UnicodeExpr, TupleExpr, DictExpr, Context, Expression, StarExpr
Expand Down Expand Up @@ -71,7 +71,7 @@ def check_str_interpolation(self,
if isinstance(expr, BytesExpr) and (3, 0) <= self.chk.options.python_version < (3, 5):
self.msg.fail('Bytes formatting is only supported in Python 3.5 and later',
replacements)
return AnyType()
return AnyType(TypeOfAny.from_error)

if has_mapping_keys is None:
pass # Error was reported
Expand Down Expand Up @@ -185,8 +185,9 @@ def check_mapping_str_interpolation(self, specifiers: List[ConversionSpecifier],
'placeholder with key \'%s\' has type' % specifier.key)
else:
rep_type = self.accept(replacements)
any_type = AnyType(TypeOfAny.special_form)
dict_type = self.chk.named_generic_type('builtins.dict',
[AnyType(), AnyType()])
[any_type, any_type])
self.chk.check_subtype(rep_type, dict_type, replacements,
messages.FORMAT_REQUIRES_MAPPING,
'expression has type', 'expected type for mapping is')
Expand Down Expand Up @@ -309,9 +310,10 @@ def conversion_type(self, p: str, context: Context, expr: FormatStringExpr) -> O
if self.chk.options.python_version < (3, 0):
self.msg.fail("Format character 'a' is only supported in Python 3", context)
return None
return AnyType()
# todo: return type object?
return AnyType(TypeOfAny.special_form)
elif p in ['s', 'r']:
return AnyType()
return AnyType(TypeOfAny.special_form)
elif p in ['d', 'i', 'o', 'u', 'x', 'X',
'e', 'E', 'f', 'F', 'g', 'G']:
return UnionType([self.named_type('builtins.int'),
Expand Down
28 changes: 14 additions & 14 deletions mypy/constraints.py
Expand Up @@ -4,9 +4,9 @@

from mypy import experiments
from mypy.types import (
CallableType, Type, TypeVisitor, UnboundType, AnyType, NoneTyp, TypeVarType,
Instance, TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType,
DeletedType, UninhabitedType, TypeType, TypeVarId, TypeQuery, is_named_instance
CallableType, Type, TypeVisitor, UnboundType, AnyType, NoneTyp, TypeVarType, Instance,
TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType, DeletedType,
UninhabitedType, TypeType, TypeVarId, TypeQuery, is_named_instance, TypeOfAny
)
from mypy.maptype import map_instance_to_supertype
from mypy import nodes
Expand Down Expand Up @@ -82,19 +82,19 @@ def get_actual_type(arg_type: Type, kind: int,
# TODO try to map type arguments to Iterable
return arg_type.args[0]
else:
return AnyType()
return AnyType(TypeOfAny.from_error)
elif isinstance(arg_type, TupleType):
# Get the next tuple item of a tuple *arg.
tuple_counter[0] += 1
return arg_type.items[tuple_counter[0] - 1]
else:
return AnyType()
return AnyType(TypeOfAny.from_error)
elif kind == nodes.ARG_STAR2:
if isinstance(arg_type, Instance) and (arg_type.type.fullname() == 'builtins.dict'):
# Dict **arg. TODO more general (Mapping)
return arg_type.args[1]
else:
return AnyType()
return AnyType(TypeOfAny.from_error)
else:
# No translation for other kinds.
return arg_type
Expand Down Expand Up @@ -338,7 +338,7 @@ def visit_instance(self, template: Instance) -> List[Constraint]:
return res
if isinstance(actual, AnyType):
# IDEA: Include both ways, i.e. add negation as well?
return self.infer_against_any(template.args)
return self.infer_against_any(template.args, actual)
if (isinstance(actual, TupleType) and
(is_named_instance(template, 'typing.Iterable') or
is_named_instance(template, 'typing.Container') or
Expand Down Expand Up @@ -371,9 +371,9 @@ def visit_callable_type(self, template: CallableType) -> List[Constraint]:
return res
elif isinstance(self.actual, AnyType):
# FIX what if generic
res = self.infer_against_any(template.arg_types)
res.extend(infer_constraints(template.ret_type, AnyType(),
self.direction))
res = self.infer_against_any(template.arg_types, self.actual)
any_type = AnyType(TypeOfAny.from_another_any, source_any=self.actual)
res.extend(infer_constraints(template.ret_type, any_type, self.direction))
return res
elif isinstance(self.actual, Overloaded):
return self.infer_against_overloaded(self.actual, template)
Expand Down Expand Up @@ -403,7 +403,7 @@ def visit_tuple_type(self, template: TupleType) -> List[Constraint]:
self.direction))
return res
elif isinstance(actual, AnyType):
return self.infer_against_any(template.items)
return self.infer_against_any(template.items, actual)
else:
return []

Expand All @@ -419,18 +419,18 @@ def visit_typeddict_type(self, template: TypedDictType) -> List[Constraint]:
self.direction))
return res
elif isinstance(actual, AnyType):
return self.infer_against_any(template.items.values())
return self.infer_against_any(template.items.values(), actual)
else:
return []

def visit_union_type(self, template: UnionType) -> List[Constraint]:
assert False, ("Unexpected UnionType in ConstraintBuilderVisitor"
" (should have been handled in infer_constraints)")

def infer_against_any(self, types: Iterable[Type]) -> List[Constraint]:
def infer_against_any(self, types: Iterable[Type], any_type: AnyType) -> List[Constraint]:
res = [] # type: List[Constraint]
for t in types:
res.extend(infer_constraints(t, AnyType(), self.direction))
res.extend(infer_constraints(t, any_type, self.direction))
return res

def visit_overloaded(self, template: Overloaded) -> List[Constraint]:
Expand Down
12 changes: 6 additions & 6 deletions mypy/erasetype.py
@@ -1,9 +1,9 @@
from typing import Optional, Container, Callable

from mypy.types import (
Type, TypeVisitor, UnboundType, AnyType, NoneTyp, TypeVarId,
Instance, TypeVarType, CallableType, TupleType, TypedDictType, UnionType, Overloaded,
ErasedType, PartialType, DeletedType, TypeTranslator, TypeList, UninhabitedType, TypeType
Type, TypeVisitor, UnboundType, AnyType, NoneTyp, TypeVarId, Instance, TypeVarType,
CallableType, TupleType, TypedDictType, UnionType, Overloaded, ErasedType, PartialType,
DeletedType, TypeTranslator, TypeList, UninhabitedType, TypeType, TypeOfAny
)
from mypy import experiments

Expand Down Expand Up @@ -51,10 +51,10 @@ def visit_deleted_type(self, t: DeletedType) -> Type:
return t

def visit_instance(self, t: Instance) -> Type:
return Instance(t.type, [AnyType()] * len(t.args), t.line)
return Instance(t.type, [AnyType(TypeOfAny.special_form)] * len(t.args), t.line)

def visit_type_var(self, t: TypeVarType) -> Type:
return AnyType()
return AnyType(TypeOfAny.special_form)

def visit_callable_type(self, t: CallableType) -> Type:
# We must preserve the fallback type for overload resolution to work.
Expand Down Expand Up @@ -86,7 +86,7 @@ def erase_id(id: TypeVarId) -> bool:
if ids_to_erase is None:
return True
return id in ids_to_erase
return t.accept(TypeVarEraser(erase_id, AnyType()))
return t.accept(TypeVarEraser(erase_id, AnyType(TypeOfAny.special_form)))


def replace_meta_vars(t: Type, target_type: Type) -> Type:
Expand Down
4 changes: 2 additions & 2 deletions mypy/exprtotype.py
Expand Up @@ -7,7 +7,7 @@
)
from mypy.fastparse import parse_type_comment
from mypy.types import (
Type, UnboundType, TypeList, EllipsisType, AnyType, Optional, CallableArgument,
Type, UnboundType, TypeList, EllipsisType, AnyType, Optional, CallableArgument, TypeOfAny
)


Expand Down Expand Up @@ -77,7 +77,7 @@ def expr_to_unanalyzed_type(expr: Expression, _parent: Optional[Expression] = No

# Go through the constructor args to get its name and type.
name = None
default_type = AnyType(implicit=True)
default_type = AnyType(TypeOfAny.implicit)
typ = default_type # type: Type
for i, arg in enumerate(expr.args):
if expr.arg_names[i] is not None:
Expand Down