-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Make type inference failures more consistent #7079
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,7 +34,7 @@ | |
Instance, NoneType, strip_type, TypeType, TypeOfAny, | ||
UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType, TypeVarDef, | ||
true_only, false_only, function_type, is_named_instance, union_items, TypeQuery, LiteralType, | ||
is_optional, remove_optional | ||
is_optional, remove_optional, TypeTranslator | ||
) | ||
from mypy.sametypes import is_same_type | ||
from mypy.messages import MessageBuilder, make_inferred_type_note, append_invariance_notes | ||
|
@@ -2520,7 +2520,7 @@ def infer_variable_type(self, name: Var, lvalue: Lvalue, | |
# gets generated in assignment like 'x = []' where item type is not known. | ||
if not self.infer_partial_type(name, lvalue, init_type): | ||
self.msg.need_annotation_for_var(name, context, self.options.python_version) | ||
self.set_inference_error_fallback_type(name, lvalue, init_type, context) | ||
self.set_inference_error_fallback_type(name, lvalue, init_type) | ||
elif (isinstance(lvalue, MemberExpr) and self.inferred_attribute_types is not None | ||
and lvalue.def_var and lvalue.def_var in self.inferred_attribute_types | ||
and not is_same_type(self.inferred_attribute_types[lvalue.def_var], init_type)): | ||
|
@@ -2569,20 +2569,18 @@ def set_inferred_type(self, var: Var, lvalue: Lvalue, type: Type) -> None: | |
self.inferred_attribute_types[lvalue.def_var] = type | ||
self.store_type(lvalue, type) | ||
|
||
def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type, | ||
context: Context) -> None: | ||
"""If errors on context line are ignored, store dummy type for variable. | ||
def set_inference_error_fallback_type(self, var: Var, lvalue: Lvalue, type: Type) -> None: | ||
"""Store best known type for variable if type inference failed. | ||
|
||
If a program ignores error on type inference error, the variable should get some | ||
inferred type so that if can used later on in the program. Example: | ||
|
||
x = [] # type: ignore | ||
x.append(1) # Should be ok! | ||
|
||
We implement this here by giving x a valid type (Any). | ||
We implement this here by giving x a valid type (replacing inferred <nothing> with Any). | ||
""" | ||
if context.get_line() in self.errors.ignored_lines[self.errors.file]: | ||
self.set_inferred_type(var, lvalue, AnyType(TypeOfAny.from_error)) | ||
self.set_inferred_type(var, lvalue, type.accept(SetNothingToAny())) | ||
|
||
def check_simple_assignment(self, lvalue_type: Optional[Type], rvalue: Expression, | ||
context: Context, | ||
|
@@ -4300,26 +4298,26 @@ def is_valid_inferred_type(typ: Type) -> bool: | |
# specific Optional type. This resolution happens in | ||
# leave_partial_types when we pop a partial types scope. | ||
return False | ||
return is_valid_inferred_type_component(typ) | ||
return not typ.accept(NothingSeeker()) | ||
|
||
|
||
def is_valid_inferred_type_component(typ: Type) -> bool: | ||
"""Is this part of a type a valid inferred type? | ||
class NothingSeeker(TypeQuery[bool]): | ||
"""Find any <nothing> types resulting from failed (ambiguous) type inference.""" | ||
|
||
In strict Optional mode this excludes bare None types, as otherwise every | ||
type containing None would be invalid. | ||
""" | ||
if is_same_type(typ, UninhabitedType()): | ||
return False | ||
elif isinstance(typ, Instance): | ||
for arg in typ.args: | ||
if not is_valid_inferred_type_component(arg): | ||
return False | ||
elif isinstance(typ, TupleType): | ||
for item in typ.items: | ||
if not is_valid_inferred_type_component(item): | ||
return False | ||
return True | ||
def __init__(self) -> None: | ||
super().__init__(any) | ||
|
||
def visit_uninhabited_type(self, t: UninhabitedType) -> bool: | ||
return t.ambiguous | ||
|
||
|
||
class SetNothingToAny(TypeTranslator): | ||
"""Replace all ambiguous <nothing> types with Any (to avoid spurious extra errors).""" | ||
|
||
def visit_uninhabited_type(self, t: UninhabitedType) -> Type: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style nit: add empty line after class docstring. |
||
if t.ambiguous: | ||
return AnyType(TypeOfAny.from_error) | ||
return t | ||
|
||
|
||
def is_node_static(node: Optional[Node]) -> Optional[bool]: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -996,7 +996,8 @@ IntNode[int](1, 1) | |
IntNode[int](1, 'a') # E: Argument 2 to "Node" has incompatible type "str"; expected "int" | ||
|
||
SameNode = Node[T, T] | ||
ff = SameNode[T](1, 1) # E: Need type annotation for 'ff' | ||
# TODO: fix https://github.com/python/mypy/issues/7084. | ||
ff = SameNode[T](1, 1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that this should generate an error, since |
||
a = SameNode(1, 'x') | ||
reveal_type(a) # N: Revealed type is '__main__.Node[Any, Any]' | ||
b = SameNode[int](1, 1) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2716,3 +2716,43 @@ class N(NamedTuple): | |
) | ||
b | ||
[builtins fixtures/tuple.pyi] | ||
|
||
[case testNewAnalyzerLessErrorsNeedAnnotation] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about adding a test case for |
||
from typing import TypeVar, Optional | ||
|
||
T = TypeVar('T') | ||
|
||
def f(x: Optional[T] = None) -> T: ... | ||
|
||
x = f() # E: Need type annotation for 'x' | ||
y = x | ||
|
||
def g() -> None: | ||
x = f() # E: Need type annotation for 'x' | ||
y = x | ||
|
||
[case testNewAnalyzerLessErrorsNeedAnnotationList] | ||
x = [] # type: ignore | ||
reveal_type(x) # N: Revealed type is 'builtins.list[Any]' | ||
|
||
def g() -> None: | ||
x = [] # type: ignore | ||
reveal_type(x) # N: Revealed type is 'builtins.list[Any]' | ||
[builtins fixtures/list.pyi] | ||
|
||
[case testNewAnalyzerLessErrorsNeedAnnotationNested] | ||
from typing import TypeVar, Optional, Generic | ||
|
||
T = TypeVar('T') | ||
class G(Generic[T]): ... | ||
|
||
def f(x: Optional[T] = None) -> G[T]: ... | ||
|
||
x = f() # E: Need type annotation for 'x' | ||
y = x | ||
reveal_type(y) # N: Revealed type is '__main__.G[Any]' | ||
|
||
def g() -> None: | ||
x = f() # E: Need type annotation for 'x' | ||
y = x | ||
reveal_type(y) # N: Revealed type is '__main__.G[Any]' |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -205,8 +205,7 @@ def h() -> None: | |
[out] | ||
== | ||
a.py:3: error: Invalid type "b.C" | ||
b.py:6: error: Need type annotation for 'c' | ||
b.py:7: error: Cannot determine type of 'c' | ||
b.py:7: error: C? has no attribute "g" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm do we now propagate unbound types in type inference? Not sure if that's a good or bad thing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is rather bad, but I believe the right solution is #4987. |
||
|
||
-- TODO: More import cycle: | ||
-- | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: add empty line after class docstring.